Skip to content

COMMS-5397 OpenAPI-ready @Schema annotations for the v3.0 model - #87

Draft
cryptalith wants to merge 7 commits into
mainfrom
lmendoa/COMMS-5397-schema-annotations
Draft

cryptalith wants to merge 7 commits into
mainfrom
lmendoa/COMMS-5397-schema-annotations

Conversation

@cryptalith

@cryptalith cryptalith commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

What

Annotation-only changes, so the Registry can generate an OpenAPI 3 description of API v3.0 from this model (COMMS-5397). Nothing changes in the classes, fields, enum constants, JAXB/Jackson output or dependencies. The version is bumped to 4.0.3.

Why

swagger-core reads io.swagger.v3.oas.annotations.media.Schema. The 2023 conversion, and one later edit, left four problems behind:

  • Labels instead of names. Class labels went into description (for example "WorkV3_0") instead of name. Schemas were therefore named after XML elements, and record.Works and record.summary.Works overwrote each other, as did Educations, Employments and CreditName.
  • Comma-joined strings. Each allowableValues was one comma-joined string, so every enum came out as a single value. A missing comma in Work.type, from the commit that added new work types (ac6e8de, 2024), also produced clinical-studyconference-abstract.
  • Missing values. funded-by was missing from relationship, and private from visibility.
  • Unannotated enum fields. On these the generator falls back to Java constant names (JOURNAL_ARTICLE, SALARY_AWARD, AR, …) instead of the values that go over the wire. This affects WorkSummary.type, FundingSummary.type, PeerReviewSummary role and type, Preferences.locale, History.creationMethod, and nine visibility fields (Biography, City, Email, Name and the five activity summaries).

Changes

  • Schema names. Every JAXB-bound v3.0 class gets @Schema(name = …): the old label without V3_0, or the simple class name for the eight classes that had no label (ClientSummary, GroupIdRecord, GroupIdRecords, NotificationAmended, NotificationCustom, TransientError, TransientNonEmptyString, and record.CreditName).

    • Three names are neither: WorkSource (its label was just V3_0), ExpandedSearch (its label duplicated Search), and RecordCreditName (unlabelled, and its simple name clashes with common.CreditName).
    • Labels are removed from three v3 enums, and CreationMethodV2_0 from message.CreationMethod, which History.creationMethod uses.
  • Wire values. Every enum-valued field documents its JSON wire values as an array: what the field's XmlAdapter writes, narrowed by the XSD enumeration where the XSD lists a subset (visibility drops system). Where JSON and the XSD disagree, JSON wins: creation-method documents API, DIRECT, MEMBER_REFERRED, WEBSITE, INTEGRATION_TEST, not the XSD's Direct or website.

  • Contributor roles. Both contributor-role fields are Strings. They now list the values reads return and writes accept, read from the API's own 9058 error in a local test stack (writes also take a few aliases the API normalises, such as CONCEPTUALIZATION):

    • work: the 11 original roles plus the 14 CRediT role URIs;
    • funding: lead, co-lead, supported-by, other-contribution, plus the 14 CRediT role URIs.

    Before, both listed the 11 work roles, so funding documented values the API refuses and neither had the CRediT URIs the API accepts and returns.

  • Guard test. OpenApiSchemaAnnotationsTest enforces all of the above. It checks that names are unique; that no XxxV3_0 or XxxV2_0 label remains on a model class or on an enum a v3 field uses; that each enum field's values equal its JSON values (a field needs no annotation when its constants already match); and that the two contributor-role lists equal the role enums plus the CRediT URIs in common-3.0.xsd.

    • On main (9592e2d) the test fails with 146 + 143 + 58 + 2 problems (names, labels, enum fields, contributor roles); here it passes.

Required properties

Later commits make required match what the API does. The COMMS-5397 contract tests (a private ORCID repo)
remove each top-level property of the create body of each of the 16 record sections in turn; before these
commits, 15 of the 16 disagreed with the API. A second review checked the nested properties against the XSDs,
which the Registry validates every write against, and the API.

  • @Schema(required = true) where the API refuses a body without the property:

    • Work: title and type (the XSD validation refuses the body, 9047), external-ids, which needs at least one
      self identifier on create and update (9023);
    • Funding: external-ids;
    • PeerReview: reviewer-role, review-identifiers, review-type, review-completion-date,
      convening-organization;
    • ResearchResource: proposal, resource-item;
    • OtherName and Keyword: content;
    • ResearcherUrl: url;
    • PersonExternalIdentifier: url, relationship (9020);
    • NotificationPermission: notification-subject, notification-intro (9047).

    Funding and research-resource proposal start-date are required only with an end-date (9050), and an
    organization address's city and country only for education and employment (9060); these get descriptions
    instead.

  • Ten @XmlElement(required = true) flags removed, where this repo's own XSDs make the element optional and the
    API accepts a body without it (each checked against the running API):

    • Affiliation.startDate (common-3.0.xsd minOccurs="0");
    • AuthorizationUrl uri, path and host: notification-permission-3.0.xsd takes uri (with or without
      path) or path alone, with an optional host;
    • ContributorAttributes contributor-sequence and contributor-role, and FundingContributorAttributes
      contributor-role (work-3.0.xsd, funding-3.0.xsd);
    • a notification Item's external-id (notification-permission-3.0.xsd);
    • OrganizationAddress city and country (common-3.0.xsd).

    TransientError's two flags also disagree with the XSD; the type only appears in responses, where both
    fields are always written, so they are left alone.

    These are the only non-@Schema lines in the PR. @Schema cannot undo a JAXB required at the
    swagger-annotations version this repo compiles against (2.1.1 has no requiredMode); swagger-core and
    Jackson's JAXB introspector both read the flag. Marshalling and unmarshalling are unchanged:

    • JAXB ignores required, and Jackson enforces it only on creator properties, which these classes lack;
    • the round-trip tests over the samples still pass.

Verification

  • mvn test runs 192 tests with 0 failures across 18 classes, including the guard test.

  • Every changed line under src/main is a @Schema annotation or its import, apart from the ten removed required = true flags above.

  • Spike output from swagger-core-jakarta 2.2.55:

    • unique PascalCase schema names;
    • kebab-case properties;
    • enums with the real values.

    For nested references to use these names, the Registry's generator has to stop Jackson's JAXB introspector from supplying XML root names. That change lands in ORCID-Source, not here.

Notes for review

  • Values narrower on writes than on reads. A notification Item's item-type keeps all 13 types, because Items is shared with amended notifications, and its description lists the ten a permission notification accepts (9047). PersonExternalIdentifier.relationship keeps all four relationship values, because a response carries whatever is stored, and its description says only self is accepted on writes (9020). Notification.notificationType lists all eight types for every notification class: the field is shared, and narrowing it for NotificationPermission (whose XSD allows only permission) would need a field override, not an annotation.
  • service-anouncement. Notification.notificationType documents this spelling because that is the value on the wire.
  • Javadoc failure. mvn package fails in maven-javadoc-plugin on existing references in groupid_v2/GroupIdRecords.java and v3/release/groupid/GroupIdRecords.java. main fails the same way; this change doesn't touch it.
  • Release. Releasing 4.0.3 is what lets ORCID-Source pick these annotations up.

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.

1 participant