From 85d1bc6babab6925e029059417761adae8ade495 Mon Sep 17 00:00:00 2001 From: Paul Sturgess Date: Fri, 17 Nov 2023 11:22:35 +0000 Subject: [PATCH] Add descriptions for LookupArgumentSets (#21) OpenAPI 3.0 does not support parameter dependencies and mutually exclusive parameters. https://swagger.io/docs/specification/describing-parameters/#dependencies This is a complex topic with a lot of debate: https://github.com/OAI/OpenAPI-Specification/issues/256 We know that our LookupArgumentSets are mutually exclusive, so the best thing is to add a description explaining this, so that it will appear in documentation and tools like the swagger editor. Apia already returns a 400 with a friendly error message. closes: https://github.com/krystal/apia-openapi/issues/4 --- .../core_api/argument_sets/object_lookup.rb | 4 ++- lib/apia/open_api/helpers.rb | 6 ++++ lib/apia/open_api/objects/parameters.rb | 33 ++++++++++++------- lib/apia/open_api/objects/schema.rb | 1 + spec/support/fixtures/openapi.json | 20 +++++++---- 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/examples/core_api/argument_sets/object_lookup.rb b/examples/core_api/argument_sets/object_lookup.rb index 3e49c2b..da1af48 100644 --- a/examples/core_api/argument_sets/object_lookup.rb +++ b/examples/core_api/argument_sets/object_lookup.rb @@ -8,7 +8,9 @@ class ObjectLookup < Apia::LookupArgumentSet description "Provides for objects to be looked up" argument :id, type: :string - argument :permalink, type: :string + argument :permalink, type: :string do + description "The permalink of the object to look up" + end potential_error "ObjectNotFound" do code :object_not_found diff --git a/lib/apia/open_api/helpers.rb b/lib/apia/open_api/helpers.rb index 3cb8f06..c25e687 100644 --- a/lib/apia/open_api/helpers.rb +++ b/lib/apia/open_api/helpers.rb @@ -38,6 +38,12 @@ def generate_id_from_definition(definition) definition.id.gsub(/\//, "_") end + def formatted_description(description) + return description if description.end_with?(".") + + "#{description}." + end + end end end diff --git a/lib/apia/open_api/objects/parameters.rb b/lib/apia/open_api/objects/parameters.rb index 7f26097..d0d28a5 100644 --- a/lib/apia/open_api/objects/parameters.rb +++ b/lib/apia/open_api/objects/parameters.rb @@ -34,17 +34,7 @@ def initialize(spec:, argument:, route_spec:) def add_to_spec if @argument.type.argument_set? - # complex argument sets are not supported in query params (e.g. nested objects) - @argument.type.klass.definition.arguments.each_value do |child_arg| - param = { - name: "#{@argument.name}[#{child_arg.name}]", - in: "query", - schema: { - type: convert_type_to_open_api_data_type(child_arg.type) - } - } - add_to_parameters(param) - end + generate_argument_set_params elsif @argument.array? if @argument.type.enum? || @argument.type.object? items = generate_schema_ref(@argument.type.klass.definition) @@ -87,6 +77,27 @@ def add_to_spec private + # Complex argument sets are not supported in query params (e.g. nested objects) + # For any LookupArgumentSet only one argument is expected to be provided. + # However, OpenAPI does not currently support describing mutually exclusive query params. + # refer to: https://swagger.io/docs/specification/describing-parameters/#dependencies + def generate_argument_set_params + @argument.type.klass.definition.arguments.each_value do |child_arg| + param = { + name: "#{@argument.name}[#{child_arg.name}]", + in: "query", + schema: { + type: convert_type_to_open_api_data_type(child_arg.type) + } + } + description = [] + description << formatted_description(child_arg.description) if child_arg.description.present? + description << "All '#{@argument.name}[]' params are mutually exclusive, only one can be provided." + param[:description] = description.join(" ") + add_to_parameters(param) + end + end + def add_to_parameters(param) @route_spec[:parameters] << param end diff --git a/lib/apia/open_api/objects/schema.rb b/lib/apia/open_api/objects/schema.rb index f793ebd..a97b8a0 100644 --- a/lib/apia/open_api/objects/schema.rb +++ b/lib/apia/open_api/objects/schema.rb @@ -70,6 +70,7 @@ def build_schema_for_polymorph def generate_child_schemas if @definition.type.argument_set? @children = @definition.type.klass.definition.arguments.values + @schema[:description] = "All '#{@definition.name}[]' params are mutually exclusive, only one can be provided." elsif @definition.type.object? @children = @definition.type.klass.definition.fields.values elsif @definition.type.enum? diff --git a/spec/support/fixtures/openapi.json b/spec/support/fixtures/openapi.json index 23e305b..b521757 100644 --- a/spec/support/fixtures/openapi.json +++ b/spec/support/fixtures/openapi.json @@ -22,14 +22,16 @@ "in": "query", "schema": { "type": "string" - } + }, + "description": "All 'time[]' params are mutually exclusive, only one can be provided." }, { "name": "time[string]", "in": "query", "schema": { "type": "string" - } + }, + "description": "All 'time[]' params are mutually exclusive, only one can be provided." }, { "name": "timezone", @@ -384,14 +386,16 @@ "in": "query", "schema": { "type": "string" - } + }, + "description": "All 'object[]' params are mutually exclusive, only one can be provided." }, { "name": "object[permalink]", "in": "query", "schema": { "type": "string" - } + }, + "description": "The permalink of the object to look up. All 'object[]' params are mutually exclusive, only one can be provided." }, { "name": "scalar", @@ -520,14 +524,16 @@ "in": "query", "schema": { "type": "string" - } + }, + "description": "All 'time[]' params are mutually exclusive, only one can be provided." }, { "name": "time[string]", "in": "query", "schema": { "type": "string" - } + }, + "description": "All 'time[]' params are mutually exclusive, only one can be provided." }, { "name": "timezone", @@ -618,6 +624,7 @@ ] }, "CoreAPI_ArgumentSets_TimeLookupArgumentSet": { + "description": "All 'time[]' params are mutually exclusive, only one can be provided.", "type": "object", "properties": { "unix": { @@ -742,6 +749,7 @@ } }, "CoreAPI_ArgumentSets_ObjectLookup": { + "description": "All 'object[]' params are mutually exclusive, only one can be provided.", "type": "object", "properties": { "id": {