OAI / sig-moonwalk

Version 4.x of the OpenAPI Specification is known as "Moonwalk," and has a goal to ship in 2024.
https://www.openapis.org/blog/2023/12/06/openapi-moonwalk-2024
Apache License 2.0
278 stars 13 forks source link

Support for parameter interdependencies #98

Closed mikekistler closed 5 months ago

mikekistler commented 5 months ago

Moonwalk should allow API designers to express interdependencies between the parameters of an operation.

This is part of the initial Moonwalk Proposal and is broken out here to refine the ideas before creating the ADR.

The Problem to be Solved

All prior versions of OpenAPI used an array to define parameters for an operation. This approach does not provide any formal mechanism for describing interdependencies between parameters. Research [1] has shown that interdependencies are not uncommon in real-world APIs and come in a variety of forms. Issue #256 (opened in 2015) requested this feature and has received over 600 up-votes.

1) "API Specs and Inter-Parameter Dependencies: The Elephant in the Room", Alberto Martin, API Specifications Conference 2022

Proposed solution

Rather than using an array to define the parameters for an operation, we should use a JSON Schema. The parameters schema must be an object schema and must not have additionalProperties. Each named property of the parameters schema defines an operation parameter. Property names of the parameter schema have the form [<in>.]name, where <in> may be "query", "header", "path", or "cookie". When not specified, <in> defaults to "query".

Note: Combining the <in> and name allows parameters with the same name but different <in>, as is permitted in prior versions of OpenAPI. It also avoids the need to augment JSON Schema for this purpose.

The parameterSchema field would be supported on Operations and on Path Items. If specified on a Path Item, the effective parameter schema for an operation would be the allOf of the path item schema and the operation schema.

The parameter schema may contain JSON Schema assertions to express interdependencies between parameters. Some common interdependencies are described below.

Requires

The presence of a parameter (or a specific parameter value) requires the presence of another parameter.

Example: answerCount must be specified if promote is specified.

    oneOf:
    - not:
      required: [promote]
    - required: [answerCount]

Or

Given a set of parameters, one or more of them must be included.

Example: either title or description must be specified.

    anyOf:
    - required: [title]
    - required: [description]

OnlyOne

Given a set of parameters, one and only one of them must be included.

Example: either from or messagingServiceId must be specified but not both.

    oneOf:
    - required: [from]
    - required: [messagingServiceId]

AllOrNone

Given a set of parameters, either all of them are provided or none of them is.

Example: subject_type and subject_id are both specified or neither is specified.

    oneOf:
    - required: [subject_type,subject_id]
    - not:
        anyOf:
          - required: [subject_type]
          - required: [subject_id]

Example: value is present if and only if type is "bucket"

    oneOf:
    - properties:
        type: 
          enum: ["bucket"]
      required: [type,value]
    - not:
        properties:
          type: 
            enum: ["bucket']      
        required: [type,value]

ZeroOrOne

Given a set of parameters, at most one of can be included.

Example: specify zero or one of forContentOwner, forDeveloper, forMine, or relatedToVideoId:

    oneOf:
    - required: [forContentOwner]
      not:  
        anyOf:
        - required: [forDeveloper]
        - required: [forMine]
        - required: [relatedToVideoId]
    - required: [forDeveloper]
      not:  
        anyOf:
        - required: [forContentOwner]
        - required: [forMine]
        - required: [relatedToVideoId]
    - required: [forMine]
      not:  
        anyOf:
        - required: [forContentOwner]
        - required: [forDeveloper]
        - required: [relatedToVideoId]
    - required: [relatedToVideoId]
      not:  
        anyOf:
        - required: [forContentOwner]
        - required: [forDeveloper]
        - required: [forMine]
    - not:
        anyOf:
        - required: [forContentOwner]
        - required: [forDeveloper]
        - required: [forMine]
        - required: [relatedToVideoId]

Note: There is probably an easier way to express this. Happy for feedback here.

Compatibility with OpenAPI 3.x

I believe that no functionality of OpenAPI 3.x is lost with this proposal. It should be straightforward to transform an existing parameters array to an equivalent parameterSchema.

mikekistler commented 5 months ago

Just realized that I have not accounted for the OpenAPI 3.x style attribute -- need to think about how to represent that in the parameter schema.