mokkabonna / json-schema-merge-allof

Simplify your schema by combining allOf
97 stars 23 forks source link

Process schemas nested inside another document (like in OpenAPI schema) #26

Closed Envek closed 3 years ago

Envek commented 3 years ago

First of all, thank you for this awesome library! It saved me a lot of effort for rewriting API specification due to allOf and additionalProperties: false (see http://json-schema.org/understanding-json-schema/reference/combining.html#id5 and https://stackoverflow.com/questions/22689900/json-schema-allof-with-additionalproperties for details) but still requires some additional work to use it with OpenAPI specs.

Problem In OpenAPI specs, JSON schemas for requests and responses are located deep inside the API spec:

Fragment of an example OpenAPI spec for reference ```yml openapi: 3.0.1 info: title: Example Application API version: v1 paths: /applications: get: summary: Gets a list of Applications. responses: '200': description: Success content: application/json: schema: type: array items: $ref: '#/components/schemas/ExtendedApplication' components: schemas: Application: required: - name - id type: object properties: id: type: string name: type: string additionalProperties: false ExtendedApplication: allOf: - $ref: '#/components/schemas/Application' - type: object properties: additionalInfo: type: string ```

And if I feed whole spec into mergeAllOf then all the subschemas won't get merged automatically. Is there any option for this?

Workaround

Write recursive function to find subschemas in a swagger spec.

async function preprocessOpenapiSpec(apiSpec) {
  const schema = await $RefParser.dereference(apiSpec)

  function mergeAllOfRecursively(candidate, parent, k) {
    if (typeof candidate !== "object") return

    if (candidate.allOf) {
      parent[k] = mergeAllOf(candidate, { ignoreAdditionalProperties: true, deep: true })
    } else {
      for (const [key, value] of Object.entries(candidate)) {
        mergeAllOfRecursively(value, candidate, key)
      }
    }
  }

  for (const [key, value] of Object.entries(schema)) {
    mergeAllOfRecursively(value, apiSpec, key)
  }

  return schema
}
mokkabonna commented 3 years ago

Most of what you posted there is not a json schema, only under component -> schemas are actually json schemas. The first part (paths etc) are not json schemas. Just JSON.

I think merging a openapi specification is not in the scope of this module.

Perhaps the best thing would be to create a wrapper library that deals with open api and then calls this library whenever it encounters an actual json schema.


Merging what is actually a schema (ExtendedApplication) works as expected: image

Envek commented 3 years ago

In OpenAPI specs, JSON schemas are also located/referred in paths.**.responses and paths.**.requestBody (and maybe somewhere else). And after dereferencing with json-schema-ref-parser anything inside components.schemas isn't used anymore (because this section is designed just to place schemas to $ref to).


Anyway, your library works as expected after adding some wrapping code, so this issue is meant to start discussion and provide solution for other folks with the same problem as mine.

mokkabonna commented 3 years ago

True, also schema under responses -> content -> application/json -> schema