From a53d1d188d331f9d7cc37cba0df52667d4a99240 Mon Sep 17 00:00:00 2001 From: Vasily Kulikov Date: Thu, 6 Jan 2022 19:06:51 +0300 Subject: [PATCH 1/2] yaml: add several OpenAPI features --- .../parser-openapi.r/openapi.d/expected.tags | 23 ++ Units/parser-openapi.r/openapi.d/input.yaml | 66 ++++- .../parser-openapi.r/swagger.d/expected.tags | 3 + parsers/openapi.c | 240 ++++++++++++++++-- 4 files changed, 314 insertions(+), 18 deletions(-) diff --git a/Units/parser-openapi.r/openapi.d/expected.tags b/Units/parser-openapi.r/openapi.d/expected.tags index adc34cedd5..0a8fc5bfc7 100644 --- a/Units/parser-openapi.r/openapi.d/expected.tags +++ b/Units/parser-openapi.r/openapi.d/expected.tags @@ -1,3 +1,8 @@ +title input.yaml /^ title: test$/;" I +description input.yaml /^ description: test$/;" I +version input.yaml /^ version: '1.0'$/;" I +http://example.com input.yaml /^ - url: http:\/\/example.com$/;" S +http://example2.com input.yaml /^ - url: http:\/\/example2.com$/;" S /sample/path input.yaml /^ \/sample\/path:$/;" p /sample/other/path input.yaml /^ \/sample\/other\/path:$/;" p NullableField input.yaml /^ NullableField:$/;" d @@ -5,3 +10,21 @@ NullableFieldStringEnum input.yaml /^ NullableFieldStringEnum:$/;" d CustomHeader input.yaml /^ CustomHeader:$/;" P Response1 input.yaml /^ Response1:$/;" R Response2 input.yaml /^ Response2:$/;" R +Example1 input.yaml /^ Example1:$/;" e +Example2 input.yaml /^ Example2:$/;" e +Body1 input.yaml /^ Body1:$/;" B +Body2 input.yaml /^ Body2:$/;" B +Header1 input.yaml /^ Header1:$/;" h +Header2 input.yaml /^ Header2:$/;" h +SSchema1 input.yaml /^ SSchema1:$/;" C +SSchema2 input.yaml /^ SSchema2:$/;" C +Link1 input.yaml /^ Link1: {}$/;" l +Link2 input.yaml /^ Link2: {}$/;" l +Callback1 input.yaml /^ Callback1: {}$/;" c +Callback2 input.yaml /^ Callback2: {}$/;" c +PathItem1 input.yaml /^ PathItem1: {}$/;" A +PathItem2 input.yaml /^ PathItem2: {}$/;" A +SomeApiKey input.yaml /^ - SomeApiKey:$/;" s +android_handler input.yaml /^ - name: android_handler$/;" t +ios_handler input.yaml /^ - name: ios_handler$/;" t +http://example.com input.yaml /^ url: http:\/\/example.com$/;" D diff --git a/Units/parser-openapi.r/openapi.d/input.yaml b/Units/parser-openapi.r/openapi.d/input.yaml index 58b3624322..30b8f31761 100644 --- a/Units/parser-openapi.r/openapi.d/input.yaml +++ b/Units/parser-openapi.r/openapi.d/input.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.0 +openapi: 3.1.0 info: title: test description: test @@ -7,6 +7,8 @@ info: servers: - url: http://example.com description: production + - url: http://example2.com + description: testing paths: /sample/path: @@ -53,3 +55,65 @@ components: schema: type: object properties: {} + examples: + Example1: + summary: xxx + value: yyyyy + Example2: + summary: xxx + value: yyyyy + requestBodies: + Body1: + description: bla bla bla + content: + application/json: + schema: + type: string + Body2: + description: bla bla bla + content: + application/json: + schema: + type: integer + headers: + Header1: + schema: + type: string + Header2: + schema: + type: string + securitySchemes: + SSchema1: + type: http + scheme: basic + SSchema2: + type: http + scheme: basic + links: + Link1: {} + Link2: {} + callbacks: + Callback1: {} + Callback2: {} + # pathItems is allowed in 3.1.0 + pathItems: + PathItem1: {} + PathItem2: {} + + + +security: + - {} + - SomeApiKey: + - foo:read + - foo:write + +tags: + - name: android_handler + description: Handler for Android clients + - name: ios_handler + description: Handler for iOS clients + +externalDocs: + description: some docs + url: http://example.com diff --git a/Units/parser-openapi.r/swagger.d/expected.tags b/Units/parser-openapi.r/swagger.d/expected.tags index caa3932931..d6d28cdc39 100644 --- a/Units/parser-openapi.r/swagger.d/expected.tags +++ b/Units/parser-openapi.r/swagger.d/expected.tags @@ -1,3 +1,6 @@ +description input.yaml /^ description: test$/;" I +title input.yaml /^ title: test$/;" I +version input.yaml /^ version: '1.0'$/;" I /sample/path input.yaml /^ \/sample\/path:$/;" p /sample/other/path input.yaml /^ \/sample\/other\/path:$/;" p PolymorphicString input.yaml /^ PolymorphicString:$/;" d diff --git a/parsers/openapi.c b/parsers/openapi.c index e9d34e2152..3c46b84bb5 100644 --- a/parsers/openapi.c +++ b/parsers/openapi.c @@ -28,6 +28,18 @@ typedef enum { KIND_PATH, KIND_RESPONSE, KIND_PARAMETER, + KIND_INFO, + KIND_SERVER, + KIND_SECURITY, + KIND_TAG, + KIND_EXAMPLE, + KIND_EXTERNAL_DOCS, + KIND_REQUEST_BODY, + KIND_HEADER, + KIND_SECURITY_SCHEME, + KIND_LINK, + KIND_CALLBACK, + KIND_PATH_ITEM, } openapiKind; static kindDefinition OpenAPIKinds [] = { @@ -35,6 +47,18 @@ static kindDefinition OpenAPIKinds [] = { { true, 'p', "path", "paths" }, { true, 'R', "response", "responses" }, { true, 'P', "parameter", "parameters" }, + { true, 'I', "info", "info"}, + { true, 'S', "server", "servers"}, + { true, 's', "security", "security"}, + { true, 't', "tag", "tags"}, + { true, 'e', "example", "examples"}, + { true, 'D', "doc", "docs"}, + { true, 'B', "requestBody", "requestBodies"}, + { true, 'h', "header", "headers"}, + { true, 'C', "securityScheme", "securitySchemes"}, + { true, 'l', "link", "links"}, + { true, 'c', "callback", "callbacks"}, + { true, 'A', "pathItem", "pathItems"}, }; #define KEY_UNKNOWN KEYWORD_NONE @@ -45,15 +69,47 @@ enum openapiKeys { KEY_PARAMETERS, KEY_RESPONSES, KEY_DEFINITIONS, + KEY_INFO, + KEY_SERVERS, + KEY_SECURITY, + KEY_TAG, + KEY_EXTERNAL_DOCS, + KEY_NAME, + KEY_URL, + KEY_TYPE, + KEY_EXAMPLES, + KEY_REQUEST_BODIES, + KEY_HEADERS, + KEY_SECURITY_SCHEMES, + KEY_LINKS, + KEY_CALLBACKS, + KEY_PATH_ITEMS, + }; static const keywordTable OpenAPIKeywordTable[] = { - { "paths", KEY_PATHS }, - { "components", KEY_COMPONENTS }, - { "schemas", KEY_SCHEMAS }, - { "parameters", KEY_PARAMETERS }, - { "responses", KEY_RESPONSES }, - { "definitions", KEY_DEFINITIONS }, + { "paths", KEY_PATHS }, + { "components", KEY_COMPONENTS }, + { "schemas", KEY_SCHEMAS }, + { "parameters", KEY_PARAMETERS }, + { "responses", KEY_RESPONSES }, + { "definitions", KEY_DEFINITIONS }, + { "info", KEY_INFO }, + { "servers", KEY_SERVERS }, + { "security", KEY_SECURITY }, + { "tags", KEY_TAG }, + { "externalDocs", KEY_EXTERNAL_DOCS }, + { "url", KEY_URL }, + { "name", KEY_NAME }, + { "type", KEY_TYPE }, + { "examples", KEY_EXAMPLES }, + { "requestBodies", KEY_REQUEST_BODIES }, + { "links", KEY_LINKS }, + { "pathItems", KEY_PATH_ITEMS }, + { "callbacks", KEY_CALLBACKS }, + { "headers", KEY_HEADERS }, + { "securitySchemes", KEY_SECURITY_SCHEMES }, + }; struct yamlBlockTypeStack { @@ -89,8 +145,7 @@ static void pushBlockType (struct sOpenAPISubparser *openapi, yaml_token_type_t s->key = KEY_UNKNOWN; } -static void popBlockType (struct sOpenAPISubparser *openapi, - yaml_token_t *token) +static void popBlockType (struct sOpenAPISubparser *openapi) { struct yamlBlockTypeStack *s; @@ -102,11 +157,10 @@ static void popBlockType (struct sOpenAPISubparser *openapi, eFree (s); } -static void popAllBlockType (struct sOpenAPISubparser *openapi, - yaml_token_t *token) +static void popAllBlockType (struct sOpenAPISubparser *openapi) { while (openapi->type_stack) - popBlockType (openapi, token); + popBlockType (openapi); } static bool stateStackMatch (struct yamlBlockTypeStack *stack, @@ -197,7 +251,91 @@ static const enum openapiKeys definitions2Keys[] = { KEY_DEFINITIONS, }; +static const enum openapiKeys links3Keys[] = { + KEY_UNKNOWN, + KEY_LINKS, + KEY_COMPONENTS, +}; + +static const enum openapiKeys callbacks3Keys[] = { + KEY_UNKNOWN, + KEY_CALLBACKS, + KEY_COMPONENTS, +}; + +static const enum openapiKeys pathItems3Keys[] = { + KEY_UNKNOWN, + KEY_PATH_ITEMS, + KEY_COMPONENTS, +}; + +static const enum openapiKeys securitySchemes3Keys[] = { + KEY_UNKNOWN, + KEY_SECURITY_SCHEMES, + KEY_COMPONENTS, +}; + +static const enum openapiKeys headers3Keys[] = { + KEY_UNKNOWN, + KEY_HEADERS, + KEY_COMPONENTS, +}; + +static const enum openapiKeys requestBodies3Keys[] = { + KEY_UNKNOWN, + KEY_REQUEST_BODIES, + KEY_COMPONENTS, +}; + +static const enum openapiKeys examples3Keys[] = { + KEY_UNKNOWN, + KEY_EXAMPLES, + KEY_COMPONENTS, +}; + +static const enum openapiKeys info3Keys[] = { + KEY_UNKNOWN, + KEY_INFO, +}; + +static const enum openapiKeys server3Keys[] = { + KEY_URL, + KEY_UNKNOWN, + KEY_SERVERS, +}; + +// The tag "type" is used as a poor man's tag title. +// That's because "name" can be set with type=apiKey only :( +static const enum openapiKeys security3Keys[] = { + KEY_UNKNOWN, + KEY_UNKNOWN, + KEY_SECURITY, +}; + +static const enum openapiKeys tags3Keys[] = { + KEY_NAME, + KEY_UNKNOWN, + KEY_TAG, +}; + +static const enum openapiKeys externalDocs3Keys[] = { + KEY_URL, + KEY_EXTERNAL_DOCS, +}; + + + const struct tagSource tagSources[] = { + { + KIND_INFO, + info3Keys, + ARRAY_SIZE (info3Keys), + }, + { + KIND_EXAMPLE, + examples3Keys, + ARRAY_SIZE (examples3Keys), + }, { KIND_PATH, pathKeys, @@ -233,14 +371,68 @@ const struct tagSource tagSources[] = { definitions2Keys, ARRAY_SIZE (definitions2Keys), }, + { + KIND_REQUEST_BODY, + requestBodies3Keys, + ARRAY_SIZE (requestBodies3Keys), + }, + { + KIND_HEADER, + headers3Keys, + ARRAY_SIZE (headers3Keys), + }, + { + KIND_PATH_ITEM, + pathItems3Keys, + ARRAY_SIZE (pathItems3Keys), + }, + { + KIND_SECURITY_SCHEME, + securitySchemes3Keys, + ARRAY_SIZE (securitySchemes3Keys), + }, + { + KIND_LINK, + links3Keys, + ARRAY_SIZE (links3Keys), + }, + { + KIND_CALLBACK, + callbacks3Keys, + ARRAY_SIZE (callbacks3Keys), + }, + { + KIND_SECURITY, + security3Keys, + ARRAY_SIZE (security3Keys), + }, }; -static void handleKey(struct sOpenAPISubparser *openapi, - yaml_token_t *token) +const struct tagSource tagValueSources[] = { + { + KIND_SERVER, + server3Keys, + ARRAY_SIZE (server3Keys), + }, + { + KIND_TAG, + tags3Keys, + ARRAY_SIZE (tags3Keys), + }, + { + KIND_EXTERNAL_DOCS, + externalDocs3Keys, + ARRAY_SIZE (externalDocs3Keys), + }, +}; + + +static void handleToken(struct sOpenAPISubparser *openapi, yaml_token_t *token, + const struct tagSource *tss, size_t ts_count) { - for (int i = 0; i < ARRAY_SIZE(tagSources); i++) + for (int i = 0; i < ts_count; i++) { - const struct tagSource* ts = &tagSources[i]; + const struct tagSource* ts = &tss[i]; if (stateStackMatch(openapi->type_stack, ts->keys, ts->countKeys)) @@ -251,10 +443,23 @@ static void handleKey(struct sOpenAPISubparser *openapi, attachYamlPosition (&tag, token, false); makeTagEntry (&tag); + break; } } } +static void handleKey(struct sOpenAPISubparser *openapi, + yaml_token_t *token) +{ + handleToken(openapi, token, tagSources, ARRAY_SIZE(tagSources)); +} + +static void handleValue(struct sOpenAPISubparser *openapi, + yaml_token_t *token) +{ + handleToken(openapi, token, tagValueSources, ARRAY_SIZE(tagValueSources)); +} + static void openapiPlayStateMachine (struct sOpenAPISubparser *openapi, yaml_token_t *token) { @@ -277,6 +482,7 @@ static void openapiPlayStateMachine (struct sOpenAPISubparser *openapi, break; case DSTAT_LAST_VALUE: TRACE_PRINT(" value: %s\n", (char*)token->data.scalar.value); + handleValue (openapi, token); break; default: break; @@ -304,9 +510,9 @@ static void newTokenCallback (yamlSubparser *s, yaml_token_t *token) openapiPlayStateMachine ((struct sOpenAPISubparser *)s, token); if (token->type == YAML_BLOCK_END_TOKEN) - popBlockType ((struct sOpenAPISubparser *)s, token); + popBlockType ((struct sOpenAPISubparser *)s); else if (token->type == YAML_STREAM_END_TOKEN) - popAllBlockType ((struct sOpenAPISubparser *)s, token); + popAllBlockType ((struct sOpenAPISubparser *)s); } static void inputStart(subparser *s) From 9470b79ee3e8273fb5f2494995cc3d5e096689c8 Mon Sep 17 00:00:00 2001 From: Vasily Kulikov Date: Fri, 7 Jan 2022 20:02:26 +0300 Subject: [PATCH 2/2] OpenAPI: remove 'info' header parsing It is non-informative and rarely used. --- Units/parser-openapi.r/openapi.d/expected.tags | 3 --- Units/parser-openapi.r/swagger.d/expected.tags | 3 --- parsers/openapi.c | 15 --------------- 3 files changed, 21 deletions(-) diff --git a/Units/parser-openapi.r/openapi.d/expected.tags b/Units/parser-openapi.r/openapi.d/expected.tags index 0a8fc5bfc7..17a0497316 100644 --- a/Units/parser-openapi.r/openapi.d/expected.tags +++ b/Units/parser-openapi.r/openapi.d/expected.tags @@ -1,6 +1,3 @@ -title input.yaml /^ title: test$/;" I -description input.yaml /^ description: test$/;" I -version input.yaml /^ version: '1.0'$/;" I http://example.com input.yaml /^ - url: http:\/\/example.com$/;" S http://example2.com input.yaml /^ - url: http:\/\/example2.com$/;" S /sample/path input.yaml /^ \/sample\/path:$/;" p diff --git a/Units/parser-openapi.r/swagger.d/expected.tags b/Units/parser-openapi.r/swagger.d/expected.tags index d6d28cdc39..caa3932931 100644 --- a/Units/parser-openapi.r/swagger.d/expected.tags +++ b/Units/parser-openapi.r/swagger.d/expected.tags @@ -1,6 +1,3 @@ -description input.yaml /^ description: test$/;" I -title input.yaml /^ title: test$/;" I -version input.yaml /^ version: '1.0'$/;" I /sample/path input.yaml /^ \/sample\/path:$/;" p /sample/other/path input.yaml /^ \/sample\/other\/path:$/;" p PolymorphicString input.yaml /^ PolymorphicString:$/;" d diff --git a/parsers/openapi.c b/parsers/openapi.c index 3c46b84bb5..02f1d7dcc1 100644 --- a/parsers/openapi.c +++ b/parsers/openapi.c @@ -28,7 +28,6 @@ typedef enum { KIND_PATH, KIND_RESPONSE, KIND_PARAMETER, - KIND_INFO, KIND_SERVER, KIND_SECURITY, KIND_TAG, @@ -47,7 +46,6 @@ static kindDefinition OpenAPIKinds [] = { { true, 'p', "path", "paths" }, { true, 'R', "response", "responses" }, { true, 'P', "parameter", "parameters" }, - { true, 'I', "info", "info"}, { true, 'S', "server", "servers"}, { true, 's', "security", "security"}, { true, 't', "tag", "tags"}, @@ -69,7 +67,6 @@ enum openapiKeys { KEY_PARAMETERS, KEY_RESPONSES, KEY_DEFINITIONS, - KEY_INFO, KEY_SERVERS, KEY_SECURITY, KEY_TAG, @@ -94,7 +91,6 @@ static const keywordTable OpenAPIKeywordTable[] = { { "parameters", KEY_PARAMETERS }, { "responses", KEY_RESPONSES }, { "definitions", KEY_DEFINITIONS }, - { "info", KEY_INFO }, { "servers", KEY_SERVERS }, { "security", KEY_SECURITY }, { "tags", KEY_TAG }, @@ -293,11 +289,6 @@ static const enum openapiKeys examples3Keys[] = { KEY_COMPONENTS, }; -static const enum openapiKeys info3Keys[] = { - KEY_UNKNOWN, - KEY_INFO, -}; - static const enum openapiKeys server3Keys[] = { KEY_URL, KEY_UNKNOWN, @@ -324,13 +315,7 @@ static const enum openapiKeys externalDocs3Keys[] = { }; - const struct tagSource tagSources[] = { - { - KIND_INFO, - info3Keys, - ARRAY_SIZE (info3Keys), - }, { KIND_EXAMPLE, examples3Keys,