Skip to content

Fix: Support @constraint:Date annotation mapping in OpenAPI specification#1886

Closed
sathindudezoysa wants to merge 4 commits into
ballerina-platform:masterfrom
sathindudezoysa:issue-5049-date-constraint-openapi
Closed

Fix: Support @constraint:Date annotation mapping in OpenAPI specification#1886
sathindudezoysa wants to merge 4 commits into
ballerina-platform:masterfrom
sathindudezoysa:issue-5049-date-constraint-openapi

Conversation

@sathindudezoysa

@sathindudezoysa sathindudezoysa commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Purpose

Currently, Ballerina's @constraint:Date constraints are not accurately mapped into the OpenAPI specification. The constraint mapper was failing to detect the Date constraint due to brittle string-matching logic and prematurely aborted processing if it encountered unsupported nested fields (like option: constraint:PAST or message) or an empty mapping constructor.

Resolves #5049

Goals

This fix ensures that any Ballerina record field annotated with @constraint:Date is accurately reflected in the generated OpenAPI schema with type: string and format: date. It also ensures that the mapper does not fail or skip processing when Ballerina-specific constraint options (like PAST, FUTURE, or custom messages) are present in the annotation.

Approach

The solution was implemented by modifying ConstraintMapperImpl.java and ConstraintAnnotation.java:

  1. State Tracking for Date Constraints: Added a date property to the ConstraintAnnotation model and its corresponding builder. This allows the mapper to track the presence of a Date constraint in memory without requiring standard constraint boundaries like minValue or maxLength.
  2. Graceful Field Handling: Updated extractedConstraintAnnotation to set the date flag and immediately return when the @constraint:Date annotation is detected. This early exit safely ignores unsupported Ballerina-specific Date constraint fields (like option: constraint:PAST and custom messages) by preventing the AST parser from diving into them, completely avoiding the diagnostic conversion errors that previously crashed the generator.
  3. OpenAPI Format Mapping: Modified the setStringConstraintValuesToSchema method to check for the presence of the date constraint flag. If present, it explicitly applies properties.setFormat("date") to the resulting OpenAPI string schema.

User stories

As an API developer using Ballerina, I want my @constraint:Date annotations to automatically generate the format: date property in my OpenAPI specification, so that API consumers and generated client SDKs correctly interpret the field as a Date.

Release note

Fix: Support @constraint:Date annotation mapping to OpenAPI specification.

Automation tests

  • Unit tests: Added testDateConstraintMapping to verify that a Ballerina record with a @constraint:Date field correctly maps to an OpenAPI schema with type: string and format: date. Validated against date_constraint.bal and expectedDate.yaml.

Security checks

Test environment

  • JDK: Java 21
  • OS: Ubuntu 22.04

Learning

Identified that utilizing Ballerina's AST (QualifiedNameReferenceNode) for syntax tree traversal is far more resilient to code formatting variations than performing raw string comparisons on node text representations.

Summary

This pull request enhances the OpenAPI specification generation to properly support Ballerina @constraint:Date annotations. Previously, the constraint mapper would fail to process Date constraints due to issues handling Ballerina-specific nested constraint fields and custom message attributes.

Changes

Core Mapping Logic:

  • Extended the ConstraintAnnotation model to track Date constraints via a new date property, including builder support and accessor methods
  • Updated the constraint extraction logic to detect and flag @constraint:Date annotations while avoiding traversal of unsupported nested fields
  • Modified the schema mapping to apply OpenAPI date formatting when Date constraints are detected
  • Enhanced the field value parser to handle Ballerina literal and reference expressions

Type Handling:

  • Added detection for Ballerina's built-in time:Date reference types to automatically format them as OpenAPI date strings

Testing and Dependencies:

  • Added a comprehensive unit test (testDateConstraintMapping) validating the mapping of constrained date fields to OpenAPI schemas with correct type and format attributes
  • Included test resources demonstrating various date constraint scenarios (future-only, past-only with custom messages)
  • Updated package versions from 2.4.1 to 2.4.2-SNAPSHOT across dependency and configuration files

Outcome

These changes enable the OpenAPI tools to correctly generate API specifications for Ballerina services that use date constraints, improving compatibility and specification accuracy for time-based validation requirements.

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds support for the time:Date constraint type in the Ballerina-to-OpenAPI mapper. It extends the constraint annotation model to capture date constraints, updates the constraint mapper to extract and apply date constraints from Ballerina code, adds type checking for Ballerina's time:Date reference type, and includes test fixtures validating the mapping to OpenAPI StringSchema with format: "date". Version dependencies are bumped to 2.4.2.

Changes

Cohort / File(s) Summary
Constraint Annotation Model
ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintAnnotation.java
Added date field to store date constraint values, implemented via private field, builder setter withDate(), and getter getDate() returning Optional<String>; included in hasConstraints() check.
Constraint Mapper Implementation
ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java
Extended extraction to handle @constraint:Date by calling withDate("date") instead of skipping; added logic in setStringConstraintValuesToSchema() to set OpenAPI schema format to "date" when date constraint present; enhanced extractFieldValue() to parse STRING_LITERAL, QUALIFIED_NAME_REFERENCE, and SIMPLE_NAME_REFERENCE expressions.
Reference Type Mapper
ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/ReferenceTypeMapper.java
Added isTimeDateType() helper method to detect Ballerina time:Date types; updated getSchema() to return StringSchema formatted as "date" for time:Date references instead of generating component $ref.
Test Infrastructure
openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ConstraintTests.java, openapi-cli/src/test/resources/ballerina-to-openapi/constraint/dateConstraint.bal, openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/expectedDate.yaml
Added testDateConstraintMapping() test method; created Ballerina fixture with constrained date types and HTTP service; added expected OpenAPI output YAML validating date constraint mapping and schema definitions.
Version Updates
openapi-tool/BalTool.toml, openapi-tool/Ballerina.toml, openapi-tool/Dependencies.toml
Bumped all artifact versions from 2.4.1 to 2.4.2 (SNAPSHOT for JAR dependencies, release for package version).

Sequence Diagram(s)

sequenceDiagram
    participant AST as Ballerina AST
    participant Mapper as ConstraintMapperImpl
    participant Annotation as ConstraintAnnotation
    participant Schema as Schema Generator
    
    AST->>Mapper: `@constraint`:Date found
    Mapper->>Mapper: extractFieldValue("date")
    Mapper->>Annotation: withDate("date")
    Annotation->>Annotation: store date field
    Mapper->>Schema: constraintAnnot.getDate()
    alt Date constraint present
        Schema->>Schema: set StringSchema format="date"
    end
    Schema->>Schema: return configured schema
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • daneshk
  • lnash94
  • shafreenAnfar
  • TharmiganK

Poem

🐰 A date constraint hops into our code,
Hopping through mappers, a logical road,
From Ballerina's time to OpenAPI's dress,
Format "date" makes the schemas impress! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for @constraint:Date annotation mapping in OpenAPI specifications, which is the primary objective of the PR.
Description check ✅ Passed The description comprehensively addresses the template requirements with detailed Purpose, Goals, Approach, User stories, Release notes, Automation tests, Security checks, and Test environment sections provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java (1)

379-384: Remove the orphaned diagnostic enum OAS_CONVERTOR_120.

The Date constraint is now properly handled and this diagnostic is no longer emitted. The enum value defined in DiagnosticMessages.java is unused and can be removed as cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java`
around lines 379 - 384, Remove the unused diagnostic enum OAS_CONVERTOR_120 from
DiagnosticMessages.java and any related references now that isDateConstraint in
ConstraintMapperImpl handles Date constraints; search for the symbol
OAS_CONVERTOR_120 and delete its enum entry from the DiagnosticMessages enum
(and remove any import/usages if found), ensuring no compilation errors remain
and tests still pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ConstraintTests.java`:
- Around line 124-128: The test testDateConstraintMapping references
"constraint/expectedDate.yaml" but the added file is named expectedDate.yml;
rename the expected file to use the .yaml extension (matching the other
constraint tests) so TestUtils.compareWithGeneratedFile(ballerinaFilePath,
"constraint/expectedDate.yaml") finds it; ensure the renamed file preserves
contents and casing exactly (expectedDate.yaml) and update any CI/test artifacts
if they reference the .yml name.

---

Nitpick comments:
In
`@ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java`:
- Around line 379-384: Remove the unused diagnostic enum OAS_CONVERTOR_120 from
DiagnosticMessages.java and any related references now that isDateConstraint in
ConstraintMapperImpl handles Date constraints; search for the symbol
OAS_CONVERTOR_120 and delete its enum entry from the DiagnosticMessages enum
(and remove any import/usages if found), ensuring no compilation errors remain
and tests still pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c85d90da-9cd8-4240-b93d-b8ed3f68d797

📥 Commits

Reviewing files that changed from the base of the PR and between dc1b20a and bcc77af.

📒 Files selected for processing (8)
  • ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintAnnotation.java
  • ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java
  • openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ConstraintTests.java
  • openapi-cli/src/test/resources/ballerina-to-openapi/constraint/date_constraint.bal
  • openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/expectedDate.yml
  • openapi-tool/BalTool.toml
  • openapi-tool/Ballerina.toml
  • openapi-tool/Dependencies.toml

@sathindudezoysa

Copy link
Copy Markdown
Contributor Author

Need to do some changes in the code

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java`:
- Around line 351-355: The code currently converts string/identifier annotation
values for every constraint field, causing non-date annotations (e.g.,
maxLength: MAX_LEN) to be coerced into strings and then fail numeric parsing; to
fix, after detecting a date constraint with isDateConstraint(annotation) and
calling constraintBuilder.withDate("date"), immediately short-circuit
(return/continue) to avoid passing this annotation to the generic extractor (the
MappingConstructorExpressionNode handling and extractFieldValue calls), leaving
extractFieldValue strict for numeric/other types; apply the same short-circuit
change to the other occurrence around lines 395-402 where date handling is
present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d4a27854-4178-4397-b389-3e6e66b0adf5

📥 Commits

Reviewing files that changed from the base of the PR and between bcc77af and 789a69e.

📒 Files selected for processing (5)
  • ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java
  • ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/ReferenceTypeMapper.java
  • openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ConstraintTests.java
  • openapi-cli/src/test/resources/ballerina-to-openapi/constraint/dateConstraint.bal
  • openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/expectedDate.yaml
✅ Files skipped from review due to trivial changes (1)
  • openapi-cli/src/test/resources/ballerina-to-openapi/constraint/dateConstraint.bal
🚧 Files skipped from review as they are similar to previous changes (1)
  • openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ConstraintTests.java

Comment on lines 351 to 355
if (isDateConstraint(annotation)) {
ExceptionDiagnostic error = new ExceptionDiagnostic(DiagnosticMessages.OAS_CONVERTOR_120,
annotation.location(), annotation.toString());
diagnostics.add(error);
return;
constraintBuilder.withDate("date");
}

MappingConstructorExpressionNode annotationValue = annotation.annotValue().orElse(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep the date workaround out of the generic value extractor.

STRING_LITERAL and identifier expressions are now accepted for every constraint field. That means a non-date annotation like maxLength: MAX_LEN can flow through as "MAX_LEN" and hit the later Integer.valueOf(...) / parseInt(...) calls, which aborts conversion with NumberFormatException instead of emitting the existing diagnostic. If the goal is just to tolerate option / message inside @constraint:Date, short-circuit that branch after withDate("date") and leave extractFieldValue() strict.

Proposed fix
                 .forEach(annotation -> {
                     if (isDateConstraint(annotation)) {
                         constraintBuilder.withDate("date");
+                        return;
                     }

                     MappingConstructorExpressionNode annotationValue = annotation.annotValue().orElse(null);
                     if (Objects.isNull(annotationValue)) {
                         return;
@@
         switch (syntaxKind) {
             case NUMERIC_LITERAL:
-            case STRING_LITERAL:
-            case QUALIFIED_NAME_REFERENCE:
-            case SIMPLE_NAME_REFERENCE:
                 return Optional.of(exprNode.toString().trim());

Also applies to: 395-402

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapperImpl.java`
around lines 351 - 355, The code currently converts string/identifier annotation
values for every constraint field, causing non-date annotations (e.g.,
maxLength: MAX_LEN) to be coerced into strings and then fail numeric parsing; to
fix, after detecting a date constraint with isDateConstraint(annotation) and
calling constraintBuilder.withDate("date"), immediately short-circuit
(return/continue) to avoid passing this annotation to the generic extractor (the
MappingConstructorExpressionNode handling and extractFieldValue calls), leaving
extractFieldValue strict for numeric/other types; apply the same short-circuit
change to the other occurrence around lines 395-402 where date handling is
present.

@sathindudezoysa

Copy link
Copy Markdown
Contributor Author

closed this PR with the aid of #1887

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ballerina to OpenAPI] Support @constraint:Date Annotation Mapping to OpenAPI Specification

1 participant