diff --git a/demo/apis.json b/demo/apis.json index 67fc983..1622413 100644 --- a/demo/apis.json +++ b/demo/apis.json @@ -20,5 +20,6 @@ "streetlights/streetlights.yaml": "ASYNC 2.0", "W-11383870/W-11383870.json": { "type": "OAS 3.0", "mime": "application/json" }, "agents-api/agents-api.yaml": { "type": "OAS 3.0", "mime": "application/yaml" }, - "oas31-webhooks/oas31-webhooks.yaml": { "type": "OAS 3.1", "mime": "application/yaml" } + "oas31-webhooks/oas31-webhooks.yaml": { "type": "OAS 3.1", "mime": "application/yaml" }, + "oas32-query/oas32-query.yaml": { "type": "OAS 3.2", "mime": "application/yaml" } } diff --git a/demo/index.js b/demo/index.js index be9fb10..3a3ca16 100644 --- a/demo/index.js +++ b/demo/index.js @@ -170,6 +170,7 @@ class ComponentDemo extends ApiDemoPage { ['multiple-messages', 'multiple-messages'], ['streetlights', 'streetlights'], ['oas31-webhooks', 'OAS 3.1 webhooks'], + ['oas32-query', 'OAS 3.2 (QUERY)'], ].map(([file, label]) => html` ${label} - compact model ${label} diff --git a/demo/oas32-query/oas32-query.yaml b/demo/oas32-query/oas32-query.yaml new file mode 100644 index 0000000..656e206 --- /dev/null +++ b/demo/oas32-query/oas32-query.yaml @@ -0,0 +1,55 @@ +openapi: 3.2.0 +info: + title: OAS 3.2 QUERY method + version: 1.0.0 +paths: + /pets: + # `query` is a Path Item Object fixed field (sibling to get/post/etc.), + # added in OAS 3.2.0 for the QUERY HTTP method (a safe/idempotent method + # that carries a request body). amf-client-js 5.11 parses it as a real + # operation, which is what the QUERY method-label test drives from. + query: + operationId: queryPets + summary: Search pets using a structured filter body + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + kind: + type: string + maxAge: + type: integer + responses: + "200": + description: Matching pets + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string + get: + operationId: listPets + summary: List pets (contrast operation for the QUERY label test) + responses: + "200": + description: All pets + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string diff --git a/package-lock.json b/package-lock.json index 0b8a23f..c786f34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@api-components/api-method-documentation", - "version": "5.2.31", + "version": "5.2.33", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@api-components/api-method-documentation", - "version": "5.2.31", + "version": "5.2.33", "license": "Apache-2.0", "dependencies": { "@advanced-rest-client/arc-icons": "^3.3.4", @@ -1181,9 +1181,9 @@ } }, "node_modules/@api-components/api-navigation": { - "version": "4.3.20", - "resolved": "https://registry.npmjs.org/@api-components/api-navigation/-/api-navigation-4.3.20.tgz", - "integrity": "sha512-ho9RaLhEK3WzHeS/QwwzVuK1WrOtZR8czxU0dWDI6YjMtBNz+pGy44sXA7rmj5F3iSPowKVu04neL0EW5JveiQ==", + "version": "4.3.24", + "resolved": "https://registry.npmjs.org/@api-components/api-navigation/-/api-navigation-4.3.24.tgz", + "integrity": "sha512-uhl5HG+DH0uWzIqupsO+X1bwRp5c9vQPNfT6pgBjRTmROrfK6RCrwr+SSOfCedbKJ+MsdAd4tgzlBi4vQ/KtrA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1191,7 +1191,7 @@ "@advanced-rest-client/icons": "^4.0.2", "@anypoint-web-components/anypoint-button": "^1.1.1", "@anypoint-web-components/anypoint-collapse": "^0.1.0", - "@api-components/amf-helper-mixin": "^4.5.34", + "@api-components/amf-helper-mixin": "^4.5.38", "@api-components/http-method-label": "^3.1.5", "@api-components/raml-aware": "^3.0.0", "lit-element": "^2.3.1", diff --git a/package.json b/package.json index 4fe6446..482e7af 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@api-components/api-method-documentation", "description": "A HTTP method documentation build from AMF model", - "version": "5.2.32", + "version": "5.2.33", "license": "Apache-2.0", "main": "index.js", "module": "index.js", diff --git a/src/Styles.js b/src/Styles.js index e92f6ad..0cbcc00 100644 --- a/src/Styles.js +++ b/src/Styles.js @@ -190,6 +190,33 @@ arc-marked { min-width: var(--api-method-documentation-http-method-label-min-width, inherit); } +.method-label[data-method='query'], +.method-label[data-method='QUERY'] { + background-color: var( + --http-method-label-query-background-color, + rgba(15, 157, 157, 0.12) + ); + color: var(--http-method-label-query-color, #0f9d9d); +} + +.method-label[data-method='copy'], +.method-label[data-method='COPY'] { + background-color: var( + --http-method-label-copy-background-color, + rgba(92, 107, 192, 0.12) + ); + color: var(--http-method-label-copy-color, #5c6bc0); +} + +.method-label[data-method='move'], +.method-label[data-method='MOVE'] { + background-color: var( + --http-method-label-move-background-color, + rgba(184, 134, 11, 0.12) + ); + color: var(--http-method-label-move-color, #b8860b); +} + .bottom-nav, .bottom-link { display: flex; diff --git a/test/api-url.test.js b/test/api-url.test.js index ccb9fa3..e8c208a 100644 --- a/test/api-url.test.js +++ b/test/api-url.test.js @@ -184,4 +184,82 @@ describe('', () => { }); }); }); + + describe('QUERY method (from a real generated OAS 3.2 model)', () => { + // OAS 3.2 adds the QUERY HTTP method. amf-client-js 5.11 PARSES `query:` from + // an OAS 3.2 pathItem and emits its method as "QUERY" (upper case), so the + // QUERY label is driven from a real generated model — + // demo/oas32-query/oas32-query.yaml — not hand-built AMF. `_computeMethod` + // keeps the raw upper-case value for display and the color hook is + // lower-cased to `data-method="query"`. + let amf; + let element; + + before(async () => { + amf = await AmfLoader.load('oas32-query', true); + }); + + beforeEach(async () => { + const [endpoint, operation] = AmfLoader.lookupEndpointOperation(amf, '/pets', 'QUERY'); + element = await operationFixture({ amf, endpoint, operation }); + await nextFrame(); + }); + + it('computes the method in upper case', () => { + assert.equal(element._method, 'QUERY'); + }); + + it('renders the method label with the lower-cased color hook', () => { + const label = element.shadowRoot.querySelector('.method-label'); + assert.exists(label, 'the method label is rendered'); + assert.equal(label.getAttribute('data-method'), 'query', 'color hook is lower-cased'); + }); + + it('displays the method name in upper case', () => { + const label = element.shadowRoot.querySelector('.method-label'); + assert.equal(label.textContent.trim(), 'QUERY'); + }); + }); + + ['COPY', 'MOVE'].forEach((verb) => { + describe(`${verb} method (OAS 3.2)`, () => { + // OAS 3.2 also adds the COPY and MOVE HTTP methods. Same casing contract + // as QUERY: displayed upper case, color hook lower-cased. Inline expanded + // operation keeps this independent of the model generator. + const METHOD = 'http://a.ml/vocabularies/apiContract#method'; + const OPERATION_T = 'http://a.ml/vocabularies/apiContract#Operation'; + + let element; + + beforeEach(async () => { + element = await operationFixture({ + operation: { + '@id': 'amf://id#12', + '@type': [OPERATION_T], + [METHOD]: [{ '@value': verb }], + }, + }); + await nextFrame(); + }); + + it('computes the method in upper case', () => { + assert.equal(element._method, verb); + }); + + it('renders the method label with the lower-cased color hook', () => { + const label = element.shadowRoot.querySelector('.method-label'); + assert.exists(label, 'the method label is rendered'); + assert.equal( + label.getAttribute('data-method'), + verb.toLowerCase(), + 'color hook is lower-cased' + ); + }); + + it('displays the method name in upper case', () => { + const label = element.shadowRoot.querySelector('.method-label'); + assert.equal(label.textContent.trim(), verb); + }); + }); + }); });