diff --git a/fixtures/bugs/145/Program Files (x86)/AppName/ref.json b/fixtures/bugs/145/Program Files (x86)/AppName/ref.json new file mode 100644 index 0000000..10a8cb7 --- /dev/null +++ b/fixtures/bugs/145/Program Files (x86)/AppName/ref.json @@ -0,0 +1,17 @@ +{ +"definitions": { + "todo-partial": { + "title": "Todo Partial", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "completed": { + "type": ["boolean", "null"] + } + }, + "required": ["name", "completed"] + } + } +} diff --git a/fixtures/bugs/145/Program Files (x86)/AppName/todos-expanded.json b/fixtures/bugs/145/Program Files (x86)/AppName/todos-expanded.json new file mode 100644 index 0000000..ba22fdf --- /dev/null +++ b/fixtures/bugs/145/Program Files (x86)/AppName/todos-expanded.json @@ -0,0 +1,1049 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "This OAS2 (Swagger 2) file represents a real API that lives at http://todos.stoplight.io.\n\nFor authentication information, click the apikey security scheme in the editor sidebar.", + "title": "To-do Demo", + "contact": { + "name": "Stoplight", + "url": "https://stoplight.io" + }, + "license": { + "name": "MIT" + }, + "version": "1.0" + }, + "host": "todos.stoplight.io", + "paths": { + "/todos": { + "get": { + "description": "​", + "tags": [ + "Todos" + ], + "summary": "List Todos", + "operationId": "GET_todos", + "parameters": [ + { + "maximum": 100, + "type": "integer", + "description": "This is how it works.", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "name": "skip", + "in": "query" + } + ], + "responses": { + "200": { + "description": "", + "schema": { + "type": "array", + "items": { + "title": "Todo Full", + "allOf": [ + { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "id", + "user" + ], + "properties": { + "completed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer", + "maximum": 1000000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "object", + "title": "User", + "required": [ + "name", + "age" + ], + "properties": { + "age": { + "type": "number", + "maximum": 150, + "minimum": 0 + }, + "error": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "name": { + "description": "The user's full name.", + "type": "string" + } + } + } + } + } + ] + } + }, + "headers": { + "foo": { + "type": "string", + "default": "bar" + } + }, + "examples": { + "application/json": [ + { + "completed": true, + "id": 1, + "name": "design the thingz" + }, + { + "completed": true, + "id": 2, + "name": "mock the thingz" + }, + { + "completed": false, + "id": 3, + "name": "code the thingz" + } + ], + "empty": [] + } + }, + "500": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Server Error", + "status": "500" + } + } + } + } + }, + "post": { + "security": [ + { + "API Key": [] + }, + { + "Basic": [] + } + ], + "description": "This creates a Todo object.\n\nTesting `inline code`.", + "tags": [ + "Todos" + ], + "summary": "Create Todo", + "operationId": "POST_todos", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + } + } + ], + "responses": { + "201": { + "description": "", + "schema": { + "title": "Todo Full", + "allOf": [ + { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "id", + "user" + ], + "properties": { + "completed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer", + "maximum": 1000000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "object", + "title": "User", + "required": [ + "name", + "age" + ], + "properties": { + "age": { + "type": "number", + "maximum": 150, + "minimum": 0 + }, + "error": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "name": { + "description": "The user's full name.", + "type": "string" + } + } + } + } + } + ] + }, + "examples": { + "/todos/chores": { + "completed": false, + "created_at": "2014-08-28T14:14:28.494Z", + "id": 9000, + "name": "Do Chores", + "updated_at": "2014-08-28T14:14:28.494Z" + }, + "application/json": { + "completed": null, + "completed_at": null, + "created_at": "2014-08-28T14:14:28.494Z", + "id": 9000, + "name": "It's Over 9000!!!", + "updated_at": "2014-08-28T14:14:28.494Z" + } + } + }, + "401": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Authorized", + "status": "401" + } + } + }, + "500": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Server Error", + "status": "500" + } + } + } + } + } + }, + "/todos/{todoId}": { + "get": { + "tags": [ + "Todos" + ], + "summary": "Get Todo", + "operationId": "GET_todo", + "parameters": [ + { + "name": "todoId", + "type": "string", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "", + "schema": { + "title": "Todo Full", + "allOf": [ + { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "id", + "user" + ], + "properties": { + "completed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer", + "maximum": 1000000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "object", + "title": "User", + "required": [ + "name", + "age" + ], + "properties": { + "age": { + "type": "number", + "maximum": 150, + "minimum": 0 + }, + "error": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "name": { + "description": "The user's full name.", + "type": "string" + } + } + } + } + } + ] + }, + "examples": { + "/todos/chores": { + "completed": false, + "created_at": "2014-08-28T14:14:28.494Z", + "id": 9000, + "name": "Do Chores", + "updated_at": "2014-08-28T14:14:28.494Z" + }, + "/todos/foobar": "{\n\t\"foo\": \"bar\"\n}\n", + "application/json": { + "completed": false, + "completed_at": "1955-04-23T13:22:52.685Z", + "created_at": "1994-11-05T03:26:51.471Z", + "id": 1, + "name": "get food", + "updated_at": "1989-07-29T11:30:06.701Z" + }, + "new": { + "completed": null, + "completed_at": "2014-01-07T07:49:55.123Z", + "created_at": "1948-04-21T12:04:21.282Z", + "float": -47990796.228164576, + "id": 920778, + "name": "esse qui proident labore", + "updated_at": "1951-12-19T11:10:34.039Z", + "user": { + "age": 121.45395681110494, + "name": "irure deserunt fugiat" + } + } + } + }, + "404": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Found", + "status": "404" + } + } + }, + "500": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Server Error", + "status": "500" + } + } + } + } + }, + "put": { + "security": [ + { + "Basic": [] + }, + { + "API Key": [] + } + ], + "tags": [ + "Todos" + ], + "summary": "Update Todo", + "operationId": "PUT_todos", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + } + } + ], + "responses": { + "200": { + "description": "", + "schema": { + "title": "Todo Full", + "allOf": [ + { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "id", + "user" + ], + "properties": { + "completed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer", + "maximum": 1000000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "object", + "title": "User", + "required": [ + "name", + "age" + ], + "properties": { + "age": { + "type": "number", + "maximum": 150, + "minimum": 0 + }, + "error": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "name": { + "description": "The user's full name.", + "type": "string" + } + } + } + } + } + ] + }, + "examples": { + "application/json": { + "completed": true, + "completed_at": null, + "created_at": "2014-08-28T14:14:28.494Z", + "id": 9000, + "name": "It's Over 9000!!!", + "updated_at": "2015-08-28T14:14:28.494Z" + } + } + }, + "401": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Authorized", + "status": "401" + } + } + }, + "404": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Found", + "status": "404" + } + } + }, + "500": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Server Error", + "status": "500" + } + } + } + } + }, + "delete": { + "security": [ + { + "Basic": [] + }, + { + "API Key": [] + } + ], + "tags": [ + "Todos" + ], + "summary": "Delete Todo", + "operationId": "DELETE_todo", + "responses": { + "204": { + "description": "" + }, + "401": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Authorized", + "status": "401" + } + } + }, + "404": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Not Found", + "status": "404" + } + } + }, + "500": { + "description": "", + "schema": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "examples": { + "application/json": { + "error": "Server Error", + "status": "500" + } + } + } + } + }, + "parameters": [ + { + "type": "string", + "name": "todoId", + "in": "path", + "required": true + } + ] + } + }, + "definitions": { + "todo-full": { + "title": "Todo Full", + "allOf": [ + { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "id", + "user" + ], + "properties": { + "completed_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer", + "maximum": 1000000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "object", + "title": "User", + "required": [ + "name", + "age" + ], + "properties": { + "age": { + "type": "number", + "maximum": 150, + "minimum": 0 + }, + "error": { + "type": "object", + "title": "Error Response", + "required": [ + "status", + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "name": { + "description": "The user's full name.", + "type": "string" + } + } + } + } + } + ] + }, + "todo-partial": { + "type": "object", + "title": "Todo Partial", + "required": [ + "name", + "completed" + ], + "properties": { + "completed": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": "string" + } + } + } + }, + "parameters": { + "limit": { + "maximum": 100, + "type": "integer", + "description": "This is how it works.", + "name": "limit", + "in": "query" + }, + "skip": { + "type": "string", + "name": "skip", + "in": "query" + } + }, + "securityDefinitions": { + "API Key": { + "type": "apiKey", + "name": "apikey", + "in": "query" + }, + "Basic": { + "type": "basic" + } + }, + "tags": [ + { + "name": "Todos" + } + ] +} diff --git a/fixtures/bugs/145/Program Files (x86)/AppName/todos.common.json b/fixtures/bugs/145/Program Files (x86)/AppName/todos.common.json new file mode 100644 index 0000000..1c43908 --- /dev/null +++ b/fixtures/bugs/145/Program Files (x86)/AppName/todos.common.json @@ -0,0 +1,103 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0", + "title": "To-do Demo", + "description": + "### Notes:\n\nThis OAS2 (Swagger 2) specification defines common models and responses, that other specifications may reference.\n\nFor example, check out the user poperty in the main.oas2 todo-partial model - it references the user model in this specification!\n\nLikewise, the main.oas2 operations reference the shared error responses in this common specification.", + "contact": { + "name": "Stoplight", + "url": "https://stoplight.io" + }, + "license": { + "name": "MIT" + } + }, + "host": "example.com", + "securityDefinitions": {}, + "paths": {}, + "responses": { + "401": { + "description": "", + "schema": { + "$ref": "#/definitions/error-response" + }, + "examples": { + "application/json": { + "status": "401", + "error": "Not Authorized" + } + } + }, + "403": { + "description": "", + "schema": { + "$ref": "#/definitions/error-response" + }, + "examples": { + "application/json": { + "status": "403", + "error": "Forbbiden" + } + } + }, + "404": { + "description": "", + "schema": { + "$ref": "#/definitions/error-response" + }, + "examples": { + "application/json": { + "status": "404", + "error": "Not Found" + } + } + }, + "500": { + "description": "", + "schema": { + "$ref": "#/definitions/error-response" + }, + "examples": { + "application/json": { + "status": "500", + "error": "Server Error" + } + } + } + }, + "definitions": { + "user": { + "title": "User", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The user's full name." + }, + "age": { + "type": "number", + "minimum": 0, + "maximum": 150 + }, + "error": { + "$ref": "#/definitions/error-response" + } + }, + "required": ["name", "age"] + }, + "error-response": { + "type": "object", + "title": "Error Response", + "properties": { + "status": { + "type": "string" + }, + "error": { + "type": "string" + } + }, + "required": ["status", "error"] + } + } +} diff --git a/fixtures/bugs/145/Program Files (x86)/AppName/todos.json b/fixtures/bugs/145/Program Files (x86)/AppName/todos.json new file mode 100644 index 0000000..9c5072e --- /dev/null +++ b/fixtures/bugs/145/Program Files (x86)/AppName/todos.json @@ -0,0 +1,336 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0", + "title": "To-do Demo", + "description": "This OAS2 (Swagger 2) file represents a real API that lives at http://todos.stoplight.io.\n\nFor authentication information, click the apikey security scheme in the editor sidebar.", + "contact": { + "name": "Stoplight", + "url": "https://stoplight.io" + }, + "license": { + "name": "MIT" + } + }, + "host": "todos.stoplight.io", + "schemes": ["http"], + "consumes": ["application/json"], + "produces": ["application/json"], + "securityDefinitions": { + "Basic": { + "type": "basic" + }, + "API Key": { + "type": "apiKey", + "name": "apikey", + "in": "query" + } + }, + "paths": { + "/todos/{todoId}": { + "parameters": [{ + "name": "todoId", + "in": "path", + "required": true, + "type": "string" + }], + "get": { + "operationId": "GET_todo", + "summary": "Get Todo", + "tags": ["Todos"], + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/todo-full" + }, + "examples": { + "application/json": { + "id": 1, + "name": "get food", + "completed": false, + "completed_at": "1955-04-23T13:22:52.685Z", + "created_at": "1994-11-05T03:26:51.471Z", + "updated_at": "1989-07-29T11:30:06.701Z" + }, + "/todos/foobar": "{\n\t\"foo\": \"bar\"\n}\n", + "/todos/chores": { + "id": 9000, + "name": "Do Chores", + "completed": false, + "created_at": "2014-08-28T14:14:28.494Z", + "updated_at": "2014-08-28T14:14:28.494Z" + }, + "new": { + "name": "esse qui proident labore", + "completed": null, + "id": 920778, + "completed_at": "2014-01-07T07:49:55.123Z", + "created_at": "1948-04-21T12:04:21.282Z", + "updated_at": "1951-12-19T11:10:34.039Z", + "user": { + "name": "irure deserunt fugiat", + "age": 121.45395681110494 + }, + "float": -47990796.228164576 + } + } + }, + "404": { + "$ref": "./todos.common.json#/responses/404" + }, + "500": { + "$ref": "./todos.common.json#/responses/500" + } + }, + "parameters": [{ + "in": "query", + "name": "", + "type": "string" + }] + }, + "put": { + "operationId": "PUT_todos", + "summary": "Update Todo", + "tags": ["Todos"], + "parameters": [{ + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/todo-partial", + "example": { + "name": "my todo's new name", + "completed": false + } + } + }], + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/todo-full" + }, + "examples": { + "application/json": { + "id": 9000, + "name": "It's Over 9000!!!", + "completed": true, + "completed_at": null, + "created_at": "2014-08-28T14:14:28.494Z", + "updated_at": "2015-08-28T14:14:28.494Z" + } + } + }, + "401": { + "$ref": "./todos.common.json#/responses/401" + }, + "404": { + "$ref": "./todos.common.json#/responses/404" + }, + "500": { + "$ref": "./todos.common.json#/responses/500" + } + }, + "security": [{ + "Basic": [] + }, + { + "API Key": [] + } + ] + }, + "delete": { + "operationId": "DELETE_todo", + "summary": "Delete Todo", + "tags": ["Todos"], + "responses": { + "204": { + "description": "" + }, + "401": { + "$ref": "./todos.common.json#/responses/401" + }, + "404": { + "$ref": "./todos.common.json#/responses/404" + }, + "500": { + "$ref": "./todos.common.json#/responses/500" + } + }, + "security": [{ + "Basic": [] + }, + { + "API Key": [] + } + ] + } + }, + "/todos": { + "post": { + "operationId": "POST_todos", + "summary": "Create Todo", + "tags": ["Todos"], + "parameters": [{ + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/todo-partial", + "example": { + "name": "my todo's name", + "completed": false + } + } + }], + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/todo-full" + }, + "examples": { + "application/json": { + "id": 9000, + "name": "It's Over 9000!!!", + "completed": null, + "completed_at": null, + "created_at": "2014-08-28T14:14:28.494Z", + "updated_at": "2014-08-28T14:14:28.494Z" + }, + "/todos/chores": { + "id": 9000, + "name": "Do Chores", + "completed": false, + "created_at": "2014-08-28T14:14:28.494Z", + "updated_at": "2014-08-28T14:14:28.494Z" + } + } + }, + "401": { + "$ref": "./todos.common.json#/responses/401" + }, + "500": { + "$ref": "./todos.common.json#/responses/500" + } + }, + "security": [{ + "API Key": [] + }, + { + "Basic": [] + } + ], + "description": "This creates a Todo object.\n\nTesting `inline code`." + }, + "get": { + "operationId": "GET_todos", + "summary": "List Todos", + "tags": ["Todos"], + "parameters": [{ + "$ref": "#/parameters/limit" + }, + { + "$ref": "#/parameters/skip" + } + ], + "responses": { + "200": { + "description": "", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/todo-full" + } + }, + "examples": { + "application/json": [{ + "id": 1, + "name": "design the thingz", + "completed": true + }, + { + "id": 2, + "name": "mock the thingz", + "completed": true + }, + { + "id": 3, + "name": "code the thingz", + "completed": false + } + ], + "empty": [] + }, + "headers": { + "foo": { + "type": "string", + "default": "bar" + } + } + }, + "500": { + "$ref": "./todos.common.json#/responses/500" + } + }, + "description": "​" + } + } + }, + "parameters": { + "limit": { + "name": "limit", + "in": "query", + "description": "This is how it works.", + "required": false, + "type": "integer", + "maximum": 100 + }, + "skip": { + "name": "skip", + "in": "query", + "required": false, + "type": "string" + } + }, + "definitions": { + "todo-partial": { + "$ref": "ref.json#/definitions/todo-partial" + }, + "todo-full": { + "title": "Todo Full", + "allOf": [{ + "$ref": "#/definitions/todo-partial" + }, + { + "type": "object", + "properties": { + "id": { + "type": "integer", + "minimum": 0, + "maximum": 1000000 + }, + "completed_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "./todos.common.json#/definitions/user" + } + }, + "required": ["id", "user"] + } + ] + } + }, + "tags": [{ + "name": "Todos" + }] +} diff --git a/spec.go b/spec.go index 93c8d4b..30ce449 100644 --- a/spec.go +++ b/spec.go @@ -38,8 +38,8 @@ type Document struct { specFilePath string origSpec *spec.Swagger schema *spec.Schema - raw json.RawMessage pathLoader *loader + raw json.RawMessage } // JSONSpec loads a spec from a json document @@ -49,7 +49,14 @@ func JSONSpec(path string, options ...LoaderOption) (*Document, error) { return nil, err } // convert to json - return Analyzed(data, "", options...) + doc, err := Analyzed(data, "", options...) + if err != nil { + return nil, err + } + + doc.specFilePath = path + + return doc, nil } // Embedded returns a Document based on embedded specs. No analysis is required @@ -71,7 +78,6 @@ func Embedded(orig, flat json.RawMessage, options ...LoaderOption) (*Document, e // Spec loads a new spec document from a local or remote path func Spec(path string, options ...LoaderOption) (*Document, error) { - ldr := loaderFromOptions(options) b, err := ldr.Load(path) @@ -84,12 +90,10 @@ func Spec(path string, options ...LoaderOption) (*Document, error) { return nil, err } - if document != nil { - document.specFilePath = path - document.pathLoader = ldr - } + document.specFilePath = path + document.pathLoader = ldr - return document, err + return document, nil } // Analyzed creates a new analyzed spec document for a root json.RawMessage. @@ -117,7 +121,7 @@ func Analyzed(data json.RawMessage, version string, options ...LoaderOption) (*D } d := &Document{ - Analyzer: analysis.New(swspec), + Analyzer: analysis.New(swspec), // NOTE: at this moment, analysis does not follow $refs to documents outside the root doc schema: spec.MustLoadSwagger20Schema(), spec: swspec, raw: raw, @@ -152,9 +156,8 @@ func trimData(in json.RawMessage) (json.RawMessage, error) { return d, nil } -// Expanded expands the ref fields in the spec document and returns a new spec document +// Expanded expands the $ref fields in the spec document and returns a new spec document func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { - swspec := new(spec.Swagger) if err := json.Unmarshal(d.raw, swspec); err != nil { return nil, err @@ -163,6 +166,9 @@ func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { var expandOptions *spec.ExpandOptions if len(options) > 0 { expandOptions = options[0] + if expandOptions.RelativeBase == "" { + expandOptions.RelativeBase = d.specFilePath + } } else { expandOptions = &spec.ExpandOptions{ RelativeBase: d.specFilePath, @@ -194,7 +200,7 @@ func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { return dd, nil } -// BasePath the base path for this spec +// BasePath the base path for the API specified by this spec func (d *Document) BasePath() string { return d.spec.BasePath } @@ -244,6 +250,8 @@ func (d *Document) ResetDefinitions() *Document { func (d *Document) Pristine() *Document { dd, _ := Analyzed(d.Raw(), d.Version()) dd.pathLoader = d.pathLoader + dd.specFilePath = d.specFilePath + return dd } diff --git a/spec_test.go b/spec_test.go index aae4394..ea04557 100644 --- a/spec_test.go +++ b/spec_test.go @@ -16,6 +16,7 @@ package loads import ( "encoding/json" + "path/filepath" "regexp" "strconv" "strings" @@ -1123,3 +1124,59 @@ func TestSpecCircular(t *testing.T) { require.NoError(t, err) require.NotNil(t, document) } + +func TestIssueSpec145(t *testing.T) { + t.Run("with remote $ref", func(t *testing.T) { + docPath := filepath.Join("fixtures", "bugs", "145", "Program Files (x86)", "AppName", "todos.json") + + t.Run("with Spec loader", func(t *testing.T) { + document, err := Spec(docPath) + require.NoError(t, err) + require.NotNil(t, document) + + _, err = document.Expanded() + require.NoError(t, err) + }) + + t.Run("with JSONSpec loader", func(t *testing.T) { + document, err := JSONSpec(docPath) + require.NoError(t, err) + require.NotNil(t, document) + + _, err = document.Expanded() + require.NoError(t, err) + }) + }) + + t.Run("with self-contained root", func(t *testing.T) { + docPath := filepath.Join("fixtures", "bugs", "145", "Program Files (x86)", "AppName", "todos-expanded.json") + + t.Run("with Spec loader", func(t *testing.T) { + document, err := Spec(docPath) + require.NoError(t, err) + require.NotNil(t, document) + + require.Equal(t, docPath, document.SpecFilePath()) + + expanded, err := document.Expanded() + require.NoError(t, err) + + require.Equal(t, docPath, expanded.SpecFilePath()) + }) + + t.Run("with JSONSpec loader", func(t *testing.T) { + document, err := JSONSpec(docPath) + require.NoError(t, err) + require.NotNil(t, document) + + _, err = document.Expanded() + require.NoError(t, err) + + t.Run("with Pristine", func(t *testing.T) { + pristine := document.Pristine() + + require.Equal(t, document.SpecFilePath(), pristine.SpecFilePath()) + }) + }) + }) +}