diff --git a/CHANGELOG.md b/CHANGELOG.md index 603cd6056..e179be986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added SPECIFICATION_TESTING.md [#359](https://github.com/opensearch-project/opensearch-api-specification/pull/359) - Added StoryValidator to validate stories before running them ([#354](https://github.com/opensearch-project/opensearch-api-specification/issues/354)) - Added support for `text/plain` responses in `_cat` APIs ([#360](https://github.com/opensearch-project/opensearch-api-specification/pull/360)) +- Added support for `application/yaml` responses ([#363](https://github.com/opensearch-project/opensearch-api-specification/pull/363)) - Added test for search with seq_no_primary_term ([#367](https://github.com/opensearch-project/opensearch-api-specification/pull/367)) ### Changed diff --git a/spec/namespaces/cat.yaml b/spec/namespaces/cat.yaml index 316fd086c..cf87eb996 100644 --- a/spec/namespaces/cat.yaml +++ b/spec/namespaces/cat.yaml @@ -848,6 +848,11 @@ components: type: array items: $ref: '../schemas/cat.health.yaml#/components/schemas/HealthRecord' + application/yaml: + schema: + type: array + items: + $ref: '../schemas/cat.health.yaml#/components/schemas/HealthRecord' cat.help@200: description: '' content: @@ -864,6 +869,11 @@ components: type: array items: $ref: '../schemas/cat.indices.yaml#/components/schemas/IndicesRecord' + application/yaml: + schema: + type: array + items: + $ref: '../schemas/cat.indices.yaml#/components/schemas/IndicesRecord' cat.master@200: description: '' content: diff --git a/spec/schemas/cat.indices.yaml b/spec/schemas/cat.indices.yaml index c03496f7b..974f4cbfc 100644 --- a/spec/schemas/cat.indices.yaml +++ b/spec/schemas/cat.indices.yaml @@ -29,13 +29,13 @@ components: type: string docs.count: description: available docs - oneOf: + anyOf: - type: string - nullable: true type: string docs.deleted: description: deleted docs - oneOf: + anyOf: - type: string - nullable: true type: string @@ -47,13 +47,13 @@ components: type: string store.size: description: store size of primaries & replicas - oneOf: + anyOf: - type: string - nullable: true type: string pri.store.size: description: store size of primaries - oneOf: + anyOf: - type: string - nullable: true type: string diff --git a/tests/cat/health.yaml b/tests/cat/health.yaml index 0a462fe9a..26184eb82 100644 --- a/tests/cat/health.yaml +++ b/tests/cat/health.yaml @@ -43,7 +43,7 @@ chapters: response: status: 200 content_type: text/plain - - synopsis: Cat with a json response. + - synopsis: Cat in different formats (format=json). method: GET path: /_cat/health parameters: @@ -56,3 +56,16 @@ chapters: status: yellow node.data: '1' discovered_cluster_manager: 'true' + - synopsis: Cat in different formats (format=yaml). + method: GET + path: /_cat/health + parameters: + format: yaml + response: + status: 200 + content_type: application/yaml + payload: + - node.total: '1' + status: yellow + node.data: '1' + discovered_cluster_manager: 'true' diff --git a/tests/cat/indices.yaml b/tests/cat/indices.yaml new file mode 100644 index 000000000..ad5072424 --- /dev/null +++ b/tests/cat/indices.yaml @@ -0,0 +1,72 @@ +$schema: ../../json_schemas/test_story.schema.yaml + +description: Test cat/indices endpoints. +prologues: + - path: /{index} + method: PUT + parameters: + index: books + request_body: + payload: {} +epilogues: + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Cat with a default text response. + method: GET + path: /_cat/indices + response: + status: 200 + content_type: text/plain + - synopsis: Cat with verbose output (v=true). + method: GET + path: /_cat/indices + parameters: + v: true + response: + status: 200 + content_type: text/plain + - synopsis: Cat with headers (h=header1,header2). + method: GET + path: /_cat/indices + parameters: + h: + - health + - status + response: + status: 200 + content_type: text/plain + - synopsis: Cat displaying all available headers (help=true). + method: GET + path: /_cat/indices + parameters: + help: true + response: + status: 200 + content_type: text/plain + - synopsis: Cat with sorted results. + method: GET + path: /_cat/indices + parameters: + s: + - status + response: + status: 200 + content_type: text/plain + - synopsis: Cat in different formats (format=json). + method: GET + path: /_cat/indices + parameters: + format: json + response: + status: 200 + content_type: application/json + - synopsis: Cat in different formats (format=yaml). + method: GET + path: /_cat/indices + parameters: + format: yaml + response: + status: 200 + content_type: application/yaml diff --git a/tools/src/tester/ChapterEvaluator.ts b/tools/src/tester/ChapterEvaluator.ts index c9f60f50a..28ba11917 100644 --- a/tools/src/tester/ChapterEvaluator.ts +++ b/tools/src/tester/ChapterEvaluator.ts @@ -17,6 +17,7 @@ import type SchemaValidator from './SchemaValidator' import { type StoryOutputs } from './StoryOutputs' import { ChapterOutput } from './ChapterOutput' import { Operation, atomizeChangeset, diff } from 'json-diff-ts' +import YAML from 'yaml' import _ from 'lodash' export default class ChapterEvaluator { @@ -85,7 +86,9 @@ export default class ChapterEvaluator { #evaluate_payload_body(response: ActualResponse, expected_payload?: Payload): Evaluation { if (expected_payload == null) return { result: Result.PASSED } - const delta = atomizeChangeset(diff(expected_payload, response.payload)) + const content_type = response.content_type ?? 'application/json' + const payload = this.#deserialize_payload(response.payload, content_type) + const delta = atomizeChangeset(diff(expected_payload, payload)) const messages: string[] = _.compact(delta.map((value, _index, _array) => { switch (value.type) { case Operation.UPDATE: @@ -103,6 +106,14 @@ export default class ChapterEvaluator { const schema = content?.schema if (schema == null && content != null) return { result: Result.PASSED } if (schema == null) return { result: Result.FAILED, message: `Schema for "${response.status}: ${response.content_type}" response not found in the spec.` } - return this._schema_validator.validate(schema, response.payload) + return this._schema_validator.validate(schema, this.#deserialize_payload(response.payload, content_type)) + } + + #deserialize_payload(payload: any, content_type: string): any { + if (payload === undefined) return undefined + switch (content_type) { + case 'application/yaml': return YAML.parse(payload as string) + default: return payload + } } } diff --git a/tools/tests/tester/fixtures/evals/failed/not_found.yaml b/tools/tests/tester/fixtures/evals/failed/not_found.yaml index 92ab01556..766fb3b99 100644 --- a/tools/tests/tester/fixtures/evals/failed/not_found.yaml +++ b/tools/tests/tester/fixtures/evals/failed/not_found.yaml @@ -10,7 +10,7 @@ chapters: - title: This chapter should fail because the operation is not defined in the spec. overall: result: FAILED - message: Operation "GET /_cat/health" not found in the spec. + message: Operation "GET /_cat/does_not_exist" not found in the spec. - title: This chapter should fail because the parameter is not defined in the spec. overall: result: FAILED diff --git a/tools/tests/tester/fixtures/evals/passed.yaml b/tools/tests/tester/fixtures/evals/passed.yaml index 501041690..a1e264847 100644 --- a/tools/tests/tester/fixtures/evals/passed.yaml +++ b/tools/tests/tester/fixtures/evals/passed.yaml @@ -23,7 +23,7 @@ chapters: result: PASSED payload_schema: result: PASSED - - title: This GET /_cat chapter should pass. + - title: This GET /_cat chapter returns text/plain and should pass. overall: result: PASSED request: @@ -37,7 +37,38 @@ chapters: result: PASSED payload_schema: result: PASSED - + - title: This GET /_cat/health chapter returns application/json and should pass. + overall: + result: PASSED + request: + parameters: + format: + result: PASSED + request_body: + result: PASSED + response: + status: + result: PASSED + payload_body: + result: PASSED + payload_schema: + result: PASSED + - title: This GET /_cat/health chapter returns application/yaml and should pass. + overall: + result: PASSED + request: + parameters: + format: + result: PASSED + request_body: + result: PASSED + response: + status: + result: PASSED + payload_body: + result: PASSED + payload_schema: + result: PASSED epilogues: - title: DELETE /books overall: diff --git a/tools/tests/tester/fixtures/specs/excerpt.yaml b/tools/tests/tester/fixtures/specs/excerpt.yaml index 4914a30b5..136f08511 100644 --- a/tools/tests/tester/fixtures/specs/excerpt.yaml +++ b/tools/tests/tester/fixtures/specs/excerpt.yaml @@ -9,9 +9,22 @@ paths: x-operation-group: cat.help x-version-added: '1.0' description: Returns help for the Cat APIs. + parameters: + - $ref: '#/components/parameters/cat.help::query.format' responses: '200': $ref: '#/components/responses/cat.help@200' + /_cat/health: + get: + operationId: cat.health.0 + x-operation-group: cat.health + x-version-added: '1.0' + description: Returns health for the Cat APIs. + parameters: + - $ref: '#/components/parameters/cat.health::query.format' + responses: + '200': + $ref: '#/components/responses/cat.health@200' /{index}: delete: operationId: indices.delete.0 @@ -73,6 +86,21 @@ components: text/plain: schema: type: string + cat.health@200: + content: + text/plain: + schema: + type: string + application/json: + schema: + type: array + items: + type: object + application/yaml: + schema: + type: array + items: + type: object indices.delete@200: description: '' content: @@ -101,6 +129,11 @@ components: - shards_acknowledged - acknowledged parameters: + cat.health::query.format: + in: query + name: format + schema: + type: string indices.delete::path.index: in: path name: index diff --git a/tools/tests/tester/fixtures/stories/failed/not_found.yaml b/tools/tests/tester/fixtures/stories/failed/not_found.yaml index b210f74e9..6f1b75739 100644 --- a/tools/tests/tester/fixtures/stories/failed/not_found.yaml +++ b/tools/tests/tester/fixtures/stories/failed/not_found.yaml @@ -7,7 +7,7 @@ epilogues: status: [200, 404] chapters: - synopsis: This chapter should fail because the operation is not defined in the spec. - path: /_cat/health + path: /_cat/does_not_exist method: GET - synopsis: This chapter should fail because the parameter is not defined in the spec. path: /{index} diff --git a/tools/tests/tester/fixtures/stories/passed.yaml b/tools/tests/tester/fixtures/stories/passed.yaml index 17f1e57d1..c4a3c5c2b 100644 --- a/tools/tests/tester/fixtures/stories/passed.yaml +++ b/tools/tests/tester/fixtures/stories/passed.yaml @@ -11,6 +11,26 @@ chapters: method: PUT parameters: index: books - - synopsis: This GET /_cat chapter should pass. + - synopsis: This GET /_cat chapter returns text/plain and should pass. path: /_cat method: GET + - synopsis: This GET /_cat/health chapter returns application/json and should pass. + path: /_cat/health + parameters: + format: json + method: GET + response: + status: 200 + content_type: application/json + payload: + - node.total: '1' + - synopsis: This GET /_cat/health chapter returns application/yaml and should pass. + path: /_cat/health + parameters: + format: yaml + method: GET + response: + status: 200 + content_type: application/yaml + payload: + - node.total: '1'