diff --git a/lib/rspec/openapi/result_recorder.rb b/lib/rspec/openapi/result_recorder.rb index d1c55c6..396f916 100644 --- a/lib/rspec/openapi/result_recorder.rb +++ b/lib/rspec/openapi/result_recorder.rb @@ -26,6 +26,7 @@ def record_results! RSpec::OpenAPI::SchemaCleaner.cleanup!(spec, new_from_zero) RSpec::OpenAPI::ComponentsUpdater.update!(spec, new_from_zero) RSpec::OpenAPI::SchemaCleaner.cleanup_empty_required_array!(spec) + RSpec::OpenAPI::SchemaCleaner.sort_paths!(spec) end end end diff --git a/lib/rspec/openapi/schema_cleaner.rb b/lib/rspec/openapi/schema_cleaner.rb index f6cf5b2..3c5efcc 100644 --- a/lib/rspec/openapi/schema_cleaner.rb +++ b/lib/rspec/openapi/schema_cleaner.rb @@ -51,6 +51,13 @@ def cleanup_empty_required_array!(base) end end + # Sort "paths" lexicographically to make the order more predictable + # + # @param [Hash] # + def sort_paths!(spec) + spec['paths'] = spec['paths']&.entries&.sort_by! { |path, _| path }.to_h + end + private def cleanup_array!(base, spec, selector, fields_for_identity = []) diff --git a/spec/rails/doc/openapi.json b/spec/rails/doc/openapi.json index 1fb3b44..20a4662 100644 --- a/spec/rails/doc/openapi.json +++ b/spec/rails/doc/openapi.json @@ -15,6 +15,332 @@ } ], "paths": { + "/images": { + "get": { + "summary": "index", + "tags": [ + "Image" + ], + "responses": { + "200": { + "description": "can return an object with an attribute of empty array", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + } + } + }, + "required": [ + "name", + "tags" + ] + } + }, + "example": [ + { + "name": "file.png", + "tags": [ + + ] + } + ] + } + } + } + } + } + }, + "/images/upload": { + "post": { + "summary": "upload", + "tags": [ + "Image" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "image" + ] + }, + "example": { + "image": "test.png" + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_multiple": { + "post": { + "summary": "upload_multiple", + "tags": [ + "Image" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + }, + "required": [ + "images" + ] + }, + "example": { + "images": [ + "test.png", + "test.png" + ] + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload multiple", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_multiple_nested": { + "post": { + "summary": "upload_multiple_nested", + "tags": [ + "Image" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "image" + ] + } + } + }, + "required": [ + "images" + ] + }, + "example": { + "images": [ + { + "image": "test.png" + }, + { + "image": "test.png" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload multiple nested", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_nested": { + "post": { + "summary": "upload_nested", + "tags": [ + "Image" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "nested_image": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + }, + "caption": { + "type": "string" + } + }, + "required": [ + "image", + "caption" + ] + } + }, + "required": [ + "nested_image" + ] + }, + "example": { + "nested_image": { + "image": "test.png", + "caption": "Some caption" + } + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload nested", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/{id}": { + "get": { + "summary": "show", + "tags": [ + "Image" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 1 + } + ], + "responses": { + "200": { + "description": "returns a image payload", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/my_engine/eng_route": { + "get": { + "summary": "GET /my_engine/eng_route", + "tags": [ + + ], + "responses": { + "200": { + "description": "returns some content from the engine", + "content": { + "text/plain": { + "schema": { + "type": "string" + }, + "example": "AN ENGINE TEST" + } + } + } + } + } + }, + "/my_engine/test": { + "get": { + "summary": "GET /my_engine/test", + "tags": [ + + ], + "responses": { + "200": { + "description": "returns the block content", + "content": { + "text/plain": { + "schema": { + "type": "string" + }, + "example": "ANOTHER TEST" + } + } + } + } + } + }, "/tables": { "get": { "summary": "index", @@ -307,254 +633,10 @@ } } } - }, - "/tables/{id}": { - "get": { - "summary": "show", - "tags": [ - "Table" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - }, - "example": 2 - } - ], - "responses": { - "200": { - "description": "returns a table", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "database": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ] - }, - "null_sample": { - "nullable": true - }, - "storage_size": { - "type": "number", - "format": "float" - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "description", - "database", - "null_sample", - "storage_size", - "created_at", - "updated_at" - ] - }, - "example": { - "id": 1, - "name": "access", - "description": "logs", - "database": { - "id": 2, - "name": "production" - }, - "null_sample": null, - "storage_size": 12.3, - "created_at": "2020-07-17T00:00:00+00:00", - "updated_at": "2020-07-17T00:00:00+00:00" - } - } - } - }, - "401": { - "description": "does not return a table if unauthorized", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ] - }, - "example": { - "message": "Unauthorized" - } - } - } - }, - "404": { - "description": "does not return a table if not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ] - }, - "example": { - "message": "not found" - } - } - } - } - } - }, - "patch": { - "summary": "update", - "tags": [ - "Table" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - }, - "example": 1 - } - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "required": [ - "name" - ] - }, - "example": { - "name": "test" - } - } - } - }, - "responses": { - "200": { - "description": "returns a table", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "database": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ] - }, - "null_sample": { - "nullable": true - }, - "storage_size": { - "type": "number", - "format": "float" - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "description", - "database", - "null_sample", - "storage_size", - "created_at", - "updated_at" - ] - }, - "example": { - "id": 1, - "name": "access", - "description": "logs", - "database": { - "id": 2, - "name": "production" - }, - "null_sample": null, - "storage_size": 12.3, - "created_at": "2020-07-17T00:00:00+00:00", - "updated_at": "2020-07-17T00:00:00+00:00" - } - } - } - } - } - }, - "delete": { - "summary": "destroy", + }, + "/tables/{id}": { + "get": { + "summary": "show", "tags": [ "Table" ], @@ -566,7 +648,7 @@ "schema": { "type": "integer" }, - "example": 1 + "example": 2 } ], "responses": { @@ -642,373 +724,291 @@ } } }, - "202": { - "description": "returns no content if specified" - } - }, - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "type": "object", - "properties": { - "no_content": { - "type": "string" - } - }, - "required": [ - "no_content" - ] - }, - "example": { - "no_content": "true" - } - } - } - } - } - }, - "/images/{id}": { - "get": { - "summary": "show", - "tags": [ - "Image" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer" - }, - "example": 1 - } - ], - "responses": { - "200": { - "description": "returns a image payload", - "content": { - "image/png": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/test_block": { - "get": { - "summary": "GET /test_block", - "tags": [ - - ], - "responses": { - "200": { - "description": "returns the block content", - "content": { - "text/plain": { - "schema": { - "type": "string" - }, - "example": "A TEST" - } - } - } - } - } - }, - "/my_engine/eng_route": { - "get": { - "summary": "GET /my_engine/eng_route", - "tags": [ - - ], - "responses": { - "200": { - "description": "returns some content from the engine", - "content": { - "text/plain": { - "schema": { - "type": "string" - }, - "example": "AN ENGINE TEST" - } - } - } - } - } - }, - "/my_engine/test": { - "get": { - "summary": "GET /my_engine/test", - "tags": [ - - ], - "responses": { - "200": { - "description": "returns the block content", - "content": { - "text/plain": { - "schema": { - "type": "string" - }, - "example": "ANOTHER TEST" - } - } - } - } - } - }, - "/images": { - "get": { - "summary": "index", - "tags": [ - "Image" - ], - "responses": { - "200": { - "description": "can return an object with an attribute of empty array", + "401": { + "description": "does not return a table if unauthorized", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - } - } - }, - "required": [ - "name", - "tags" - ] - } - }, - "example": [ - { - "name": "file.png", - "tags": [ - - ] - } - ] - } - } - } - } - } - }, - "/images/upload_nested": { - "post": { - "summary": "upload_nested", - "tags": [ - "Image" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "nested_image": { - "type": "object", - "properties": { - "image": { - "type": "string", - "format": "binary" - }, - "caption": { - "type": "string" - } - }, - "required": [ - "image", - "caption" - ] - } + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] }, - "required": [ - "nested_image" - ] - }, - "example": { - "nested_image": { - "image": "test.png", - "caption": "Some caption" + "example": { + "message": "Unauthorized" } } } - } - }, - "responses": { - "200": { - "description": "returns a image payload with upload nested", + }, + "404": { + "description": "does not return a table if not found", "content": { - "image/png": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "example": { + "message": "not found" } } } } } - } - }, - "/images/upload": { - "post": { - "summary": "upload", + }, + "patch": { + "summary": "update", "tags": [ - "Image" + "Table" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 1 + } ], "requestBody": { "content": { - "multipart/form-data": { + "application/x-www-form-urlencoded": { "schema": { "type": "object", "properties": { - "image": { - "type": "string", - "format": "binary" + "name": { + "type": "string" } }, "required": [ - "image" + "name" ] }, "example": { - "image": "test.png" + "name": "test" } } } }, "responses": { "200": { - "description": "returns a image payload with upload", + "description": "returns a table", "content": { - "image/png": { + "application/json": { "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/images/upload_multiple_nested": { - "post": { - "summary": "upload_multiple_nested", - "tags": [ - "Image" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "images": { - "type": "array", - "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { "type": "object", "properties": { - "image": { - "type": "string", - "format": "binary" + "id": { + "type": "integer" + }, + "name": { + "type": "string" } }, "required": [ - "image" + "id", + "name" ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" } - } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] }, - "required": [ - "images" - ] - }, - "example": { - "images": [ - { - "image": "test.png" + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" }, - { - "image": "test.png" - } - ] + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } } } } - }, + } + }, + "delete": { + "summary": "destroy", + "tags": [ + "Table" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "example": 1 + } + ], "responses": { "200": { - "description": "returns a image payload with upload multiple nested", + "description": "returns a table", "content": { - "image/png": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + }, + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" } } } + }, + "202": { + "description": "returns no content if specified" } - } - } - }, - "/images/upload_multiple": { - "post": { - "summary": "upload_multiple", - "tags": [ - "Image" - ], + }, "requestBody": { "content": { - "multipart/form-data": { + "application/x-www-form-urlencoded": { "schema": { "type": "object", "properties": { - "images": { - "type": "array", - "items": { - "type": "string", - "format": "binary" - } + "no_content": { + "type": "string" } }, "required": [ - "images" + "no_content" ] }, "example": { - "images": [ - "test.png", - "test.png" - ] + "no_content": "true" } } } - }, + } + } + }, + "/test_block": { + "get": { + "summary": "GET /test_block", + "tags": [ + + ], "responses": { "200": { - "description": "returns a image payload with upload multiple", + "description": "returns the block content", "content": { - "image/png": { + "text/plain": { "schema": { - "type": "string", - "format": "binary" - } + "type": "string" + }, + "example": "A TEST" } } } diff --git a/spec/rails/doc/openapi.yaml b/spec/rails/doc/openapi.yaml index fc4183e..0386816 100644 --- a/spec/rails/doc/openapi.yaml +++ b/spec/rails/doc/openapi.yaml @@ -14,6 +14,203 @@ info: servers: - url: http://localhost:3000 paths: + "/images": + get: + summary: index + tags: + - Image + responses: + '200': + description: can return an object with an attribute of empty array + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + tags: + type: array + items: {} + required: + - name + - tags + example: + - name: file.png + tags: [] + "/images/upload": + post: + summary: upload + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + image: + type: string + format: binary + required: + - image + example: + image: test.png + responses: + '200': + description: returns a image payload with upload + content: + image/png: + schema: + type: string + format: binary + "/images/upload_multiple": + post: + summary: upload_multiple + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: string + format: binary + required: + - images + example: + images: + - test.png + - test.png + responses: + '200': + description: returns a image payload with upload multiple + content: + image/png: + schema: + type: string + format: binary + "/images/upload_multiple_nested": + post: + summary: upload_multiple_nested + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: object + properties: + image: + type: string + format: binary + required: + - image + required: + - images + example: + images: + - image: test.png + - image: test.png + responses: + '200': + description: returns a image payload with upload multiple nested + content: + image/png: + schema: + type: string + format: binary + "/images/upload_nested": + post: + summary: upload_nested + tags: + - Image + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + nested_image: + type: object + properties: + image: + type: string + format: binary + caption: + type: string + required: + - image + - caption + required: + - nested_image + example: + nested_image: + image: test.png + caption: Some caption + responses: + '200': + description: returns a image payload with upload nested + content: + image/png: + schema: + type: string + format: binary + "/images/{id}": + get: + summary: show + tags: + - Image + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + responses: + '200': + description: returns a image payload + content: + image/png: + schema: + type: string + format: binary + "/my_engine/eng_route": + get: + summary: GET /my_engine/eng_route + tags: [] + responses: + '200': + description: returns some content from the engine + content: + text/plain: + schema: + type: string + example: AN ENGINE TEST + "/my_engine/test": + get: + summary: GET /my_engine/test + tags: [] + responses: + '200': + description: returns the block content + content: + text/plain: + schema: + type: string + example: ANOTHER TEST "/tables": get: summary: index @@ -457,26 +654,6 @@ paths: - no_content example: no_content: 'true' - "/images/{id}": - get: - summary: show - tags: - - Image - parameters: - - name: id - in: path - required: true - schema: - type: integer - example: 1 - responses: - '200': - description: returns a image payload - content: - image/png: - schema: - type: string - format: binary "/test_block": get: summary: GET /test_block @@ -489,180 +666,3 @@ paths: schema: type: string example: A TEST - "/my_engine/eng_route": - get: - summary: GET /my_engine/eng_route - tags: [] - responses: - '200': - description: returns some content from the engine - content: - text/plain: - schema: - type: string - example: AN ENGINE TEST - "/images": - get: - summary: index - tags: - - Image - responses: - '200': - description: can return an object with an attribute of empty array - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - tags: - type: array - items: {} - required: - - name - - tags - example: - - name: file.png - tags: [] - "/my_engine/test": - get: - summary: GET /my_engine/test - tags: [] - responses: - '200': - description: returns the block content - content: - text/plain: - schema: - type: string - example: ANOTHER TEST - "/images/upload": - post: - summary: upload - tags: - - Image - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - image: - type: string - format: binary - required: - - image - example: - image: test.png - responses: - '200': - description: returns a image payload with upload - content: - image/png: - schema: - type: string - format: binary - "/images/upload_nested": - post: - summary: upload_nested - tags: - - Image - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - nested_image: - type: object - properties: - image: - type: string - format: binary - caption: - type: string - required: - - image - - caption - required: - - nested_image - example: - nested_image: - image: test.png - caption: Some caption - responses: - '200': - description: returns a image payload with upload nested - content: - image/png: - schema: - type: string - format: binary - "/images/upload_multiple_nested": - post: - summary: upload_multiple_nested - tags: - - Image - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - images: - type: array - items: - type: object - properties: - image: - type: string - format: binary - required: - - image - required: - - images - example: - images: - - image: test.png - - image: test.png - responses: - '200': - description: returns a image payload with upload multiple nested - content: - image/png: - schema: - type: string - format: binary - "/images/upload_multiple": - post: - summary: upload_multiple - tags: - - Image - requestBody: - content: - multipart/form-data: - schema: - type: object - properties: - images: - type: array - items: - type: string - format: binary - required: - - images - example: - images: - - test.png - - test.png - responses: - '200': - description: returns a image payload with upload multiple - content: - image/png: - schema: - type: string - format: binary