From f84f0b6bf560b2fc042195003bc7b0e8180265f3 Mon Sep 17 00:00:00 2001 From: David Biesack Date: Sun, 21 Apr 2024 21:34:57 -0400 Subject: [PATCH 1/4] issue-26-JSON-Schema-Specification-version --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 00260b4..244d042 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,8 @@ Remove the `webhooks` object, if present. ### ⤓ JSON Schema related changes -OAS 3.0 uses an earlier JSON Schema version (Draft 7). The tool converts `examples` +OAS 3.0 uses an earlier JSON Schema version +([JSON Schema Specification Wright Draft 00](https://datatracker.ietf.org/doc/html/draft-wright-json-schema-00)). The tool converts `examples` in schemas to a single `example`. As a special case, if the resulting `example` includes an `id`, it is From 1a8a201cc3729f208c0e59e248b5efb43843398d Mon Sep 17 00:00:00 2001 From: David Biesack Date: Sun, 21 Apr 2024 21:49:05 -0400 Subject: [PATCH 2/4] Fix #24 - link to correct JSON Schema version for OAS 3.0.x Fixed #36 - Allow for no security requirements object on an operaetion --- src/converter.ts | 2 +- test/converter.spec.ts | 3 ++- test/data/openapi.yaml | 60 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/converter.ts b/src/converter.ts index 07c3358..3f4e92f 100644 --- a/src/converter.ts +++ b/src/converter.ts @@ -388,7 +388,7 @@ export class Converter { continue; } const operation = paths[path][op]; - const sec = operation?.security as object[]; + const sec = (operation?.security || []) as object[]; sec.forEach((s) => { const requirement = s?.[schemeName] as string[]; if (requirement) { diff --git a/test/converter.spec.ts b/test/converter.spec.ts index 0b2f582..85053c6 100644 --- a/test/converter.spec.ts +++ b/test/converter.spec.ts @@ -773,6 +773,8 @@ describe('resolver test suite', () => { const scopes = converted.components.securitySchemes.accessToken.flows.authorizationCode.scopes; expect(scopes['scope1']).toEqual('Allow the application to access your personal profile data.'); expect(scopes['scope3']).toEqual(`TODO: describe the 'scope3' scope`); + const unauthOp = (converted.paths['/users/{appId}/open-preferences'] as object)['get']; + expect(unauthOp['security']).toBeFalsy(); done(); }); }); @@ -950,4 +952,3 @@ test('contentMediaType with existing unexpected format', (done) => { // TODO how to check that Converter logged to console.warn ? done(); }); - diff --git a/test/data/openapi.yaml b/test/data/openapi.yaml index e8f77a2..0376f0a 100644 --- a/test/data/openapi.yaml +++ b/test/data/openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.1.0 info: - title: Transactions + title: Application Preferences (Example OpenAPI) description: ... version: 0.1.2 contact: @@ -21,7 +21,7 @@ paths: parameters: - $ref: '#/components/parameters/appIdPathParam' get: - summary: Return preferences for a application + summary: Return preferences for an application description: ... operationId: listPreferences tags: @@ -66,7 +66,7 @@ paths: application/pdf: schema: type: string - contentMediaType: application/json + contentMediaType: application/pdf contentEncoding: base64 maxLength: 5000000 '400': @@ -92,6 +92,58 @@ paths: - scope2 - scope3 - scope4 + /users/{appId}/open-preferences: + parameters: + - $ref: '#/components/parameters/appIdPathParam' + get: + summary: Return public preferences for an application, without auth + description: ... + operationId: listPublicPreferences + tags: + - Preferences + parameters: + - name: categories + description: >- + Filter preferences to only those whose `category` is in this + pipe-separated list. + in: query + style: pipeDelimited + schema: + type: array + minItems: 1 + maxItems: 16 + examples: + - - Presentation + - - Presentation + - Notifications + items: + type: string + - name: type + description: >- + Filter preferences only those whose `type` is in this pipe-separated + list. + in: query + style: pipeDelimited + schema: + type: array + minItems: 1 + maxItems: 4 + uniqueItems: true + items: + type: string + responses: + '200': + description: OK. + content: + application/json: + schema: + $ref: '#/components/schemas/preferences' + application/pdf: + schema: + type: string + contentMediaType: application/pdf + contentEncoding: base64 + maxLength: 5000000 components: securitySchemes: accessToken: @@ -437,4 +489,4 @@ components: minlength is for no milliseconds, such as '2021-10-30T19:06:00Z' maxLength is for '.' plus up to 9 digits for milliseconds, - such as '2021-10-30T19:06:04.999000999Z' \ No newline at end of file + such as '2021-10-30T19:06:04.999000999Z' From 7a0d3fa1deafd58a50b3a31e5f3558f6170ece84 Mon Sep 17 00:00:00 2001 From: David Biesack Date: Mon, 22 Apr 2024 16:21:26 -0400 Subject: [PATCH 3/4] filter out path.{$ref, summary, description, parameters, servers} and x-* specification extensions --- src/converter.ts | 11 +++++------ test/data/openapi.yaml | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/converter.ts b/src/converter.ts index 3f4e92f..d5ca8fb 100644 --- a/src/converter.ts +++ b/src/converter.ts @@ -370,7 +370,7 @@ export class Converter { private json(x) { return JSON.stringify(x, null, 2); } - + static readonly METHODS = ['delete', 'get', 'head', 'options', 'patch', 'post', 'put', 'trace' ]; /** * OpenAPI 3.1 defines a new `openIdConnect` security scheme. * Down-convert the scheme to `oauth2` / authorization code flow. @@ -383,11 +383,10 @@ export class Converter { const scopes = {}; const paths = this.openapi30?.paths; for (const path in paths) { - for (const op in paths[path]) { - if (op === 'parameters') { - continue; - } - const operation = paths[path][op]; + // filter out path.{$ref, summary, description, parameters, servers} and x-* specification extensions + const methods = Object.keys(paths[path]).filter((op) => Converter.METHODS.includes(op)); + for (const method in methods) { + const operation = paths[path][method]; const sec = (operation?.security || []) as object[]; sec.forEach((s) => { const requirement = s?.[schemeName] as string[]; diff --git a/test/data/openapi.yaml b/test/data/openapi.yaml index 0376f0a..0e9c36c 100644 --- a/test/data/openapi.yaml +++ b/test/data/openapi.yaml @@ -18,6 +18,8 @@ tags: description: Application Preferences paths: /users/{appId}/preferences: + summary: Application preferences + description: A user's preferences for an application parameters: - $ref: '#/components/parameters/appIdPathParam' get: From 58358a8438710ced3569bd1f4875d5d2f0d895fa Mon Sep 17 00:00:00 2001 From: David Biesack Date: Mon, 22 Apr 2024 16:47:29 -0400 Subject: [PATCH 4/4] only look for operation children (HTTP methods) of a path --- src/converter.ts | 4 ++-- test/converter.spec.ts | 4 ++-- test/data/openapi.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/converter.ts b/src/converter.ts index d5ca8fb..3868dc5 100644 --- a/src/converter.ts +++ b/src/converter.ts @@ -385,7 +385,7 @@ export class Converter { for (const path in paths) { // filter out path.{$ref, summary, description, parameters, servers} and x-* specification extensions const methods = Object.keys(paths[path]).filter((op) => Converter.METHODS.includes(op)); - for (const method in methods) { + methods.forEach(method => { const operation = paths[path][method]; const sec = (operation?.security || []) as object[]; sec.forEach((s) => { @@ -396,7 +396,7 @@ export class Converter { }); } }); - } + }); } return scopes; }; diff --git a/test/converter.spec.ts b/test/converter.spec.ts index 85053c6..915eb57 100644 --- a/test/converter.spec.ts +++ b/test/converter.spec.ts @@ -773,8 +773,8 @@ describe('resolver test suite', () => { const scopes = converted.components.securitySchemes.accessToken.flows.authorizationCode.scopes; expect(scopes['scope1']).toEqual('Allow the application to access your personal profile data.'); expect(scopes['scope3']).toEqual(`TODO: describe the 'scope3' scope`); - const unauthOp = (converted.paths['/users/{appId}/open-preferences'] as object)['get']; - expect(unauthOp['security']).toBeFalsy(); + const publicOp = (converted.paths['/users/{appId}/public-preferences'] as object)['get']; + expect(publicOp['security']).toBeFalsy(); done(); }); }); diff --git a/test/data/openapi.yaml b/test/data/openapi.yaml index 0e9c36c..c209bdb 100644 --- a/test/data/openapi.yaml +++ b/test/data/openapi.yaml @@ -94,7 +94,7 @@ paths: - scope2 - scope3 - scope4 - /users/{appId}/open-preferences: + /users/{appId}/public-preferences: parameters: - $ref: '#/components/parameters/appIdPathParam' get: