While I am well versed in the JSON Schema specification I am working to transition to the Open API 3.0.2 specification and I am running into an odd issue trying to DRY up and organize my definitions. I am trying to use $ref
to other JSON structures from in nested folders and running into an error. I am sure there is a rule or pathing character I am missing. I should add the $ref
under the info block appears to work correctly, it is only the model $ref
.
I have been following these guides:
I am using the committee gem to test these schemas and the error is:
Attempted "$ref": "models/user.json"
DRY Extraction to subfolder
{
"type": "object",
"description": "A user account.",
"required": [
"id",
"email"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"description": "Unique identifier for the given record."
},
"email": {
"type": "string",
"description": "The email associated to the given user."
},
"first_name": {
"type": "string",
"description": "User's first name."
},
"last_name": {
"type": "string",
"description": "User's last name."
},
"full_name": {
"type": "string",
"description": "Full user name"
}
}
}
How I have my folder structure setup:
common/
models/
requests/
api.json
The api.json
file
{
"openapi": "3.0.2",
"components": {
"schemas": {
"error": {
"properties": {
"code": {
"type": "string",
"description": "Machine readable code for this specific error message."
},
"message": {
"type": "string",
"description": "Description of the error."
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"user": {
"type": "object",
"description": "A user account.",
"required": [
"id",
"email"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"description": "Unique identifier for the given record."
},
"email": {
"type": "string",
"description": "The email associated to the given user."
},
"first_name": {
"type": "string",
"description": "User's first name."
},
"last_name": {
"type": "string",
"description": "User's last name."
},
"full_name": {
"type": "string",
"description": "Full user name"
}
}
},
"user_input": {
"properties": {
"email": {
"type": "string"
},
"first_name": {
"type": "string",
"description": "User's first name."
},
"last_name": {
"type": "string",
"description": "User's last name."
}
},
"required": [
"email"
],
"type": "object"
}
}
},
"info": { "$ref": "common/info.json" },
"paths": {
"/users": {
"get": {
"description": "Returns all the users in the system.",
"operationId": "get--users",
"parameters": [],
"responses": {
"200": {
"description": "[com.test.test]",
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "models/user.json"
},
"type": "array"
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
},
"post": {
"description": "Creates a new user.",
"operationId": "post--users",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/user_input"
}
}
}
},
"responses": {
"200": {
"description": "com.test.test",
"content": {
"application/json": {
"schema": {
"$ref": "models/user.json"
}
}
},
"headers": {}
},
"409": {
"description": "[com.test.test]",
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/error"
},
"type": "array"
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
}
},
"/users/{id}": {
"get": {
"description": "Returns the given user.",
"operationId": "get--users-id",
"parameters": [
{
"deprecated": false,
"in": "path",
"required": true,
"schema": {
"type": "integer"
},
"name": "id"
}
],
"responses": {
"200": {
"description": "com.test.test",
"content": {
"application/json": {
"schema": {
"$ref": "models/user.json"
}
}
},
"headers": {}
},
"404": {
"description": "unit",
"content": {
"application/json": {
"schema": {
"type": "integer",
"nullable": true
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
}
}
},
"servers": [
{
"url": "http://bobs.ngrok.io"
}
]
}
From https://swagger.io/docs/specification/using-ref/
A common misconception is that $ref is allowed anywhere in an OpenAPI specification file. Actually $ref is only allowed in places where the OpenAPI 3.0 Specification explicitly states that the value may be a reference. For example, $ref cannot be used in the info section and directly under paths:
I adjusted my json to..
{
"openapi": "3.0.2",
"components": {
"schemas": {
"error": {
"properties": {
"code": {
"type": "string",
"description": "Machine readable code for this specific error message."
},
"message": {
"type": "string",
"description": "Description of the error."
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"user": { "$ref": "models/user.json#/components/schemas/user" },
"user_input": {
"properties": {
"email": {
"type": "string"
},
"first_name": {
"type": "string",
"description": "User's first name."
},
"last_name": {
"type": "string",
"description": "User's last name."
}
},
"required": [
"email"
],
"type": "object"
}
}
},
"info": {
"contact": {
"name": "Bobs Burgers",
"email": "bob@bobsburgers.com"
},
"description": "Marketplace API.",
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"termsOfService": "",
"title": "explore",
"version": "0.0.2-dev"
},
"paths": {
"/users": {
"get": {
"description": "Returns all the users in the system.",
"operationId": "get--users",
"parameters": [],
"responses": {
"200": {
"description": "[com.test.test]",
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/user"
},
"type": "array"
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
},
"post": {
"description": "Creates a new user.",
"operationId": "post--users",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/user_input"
}
}
}
},
"responses": {
"200": {
"description": "com.test.test",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/user"
}
}
},
"headers": {}
},
"409": {
"description": "[com.test.test]",
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/error"
},
"type": "array"
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
}
},
"/users/{id}": {
"get": {
"description": "Returns the given user.",
"operationId": "get--users-id",
"parameters": [
{
"deprecated": false,
"in": "path",
"required": true,
"schema": {
"type": "integer"
},
"name": "id"
}
],
"responses": {
"200": {
"description": "com.test.test",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/user"
}
}
},
"headers": {}
},
"404": {
"description": "unit",
"content": {
"application/json": {
"schema": {
"type": "integer",
"nullable": true
}
}
},
"headers": {}
}
},
"tags": [
"user"
]
}
}
},
"servers": [
{
"url": "http://bobs.ngrok.io"
}
]
}
and in the models/user.json
file...
{
"components": {
"schemas": {
"user": {
"type": "object",
"description": "A user account.",
"required": [
"id",
"email"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"description": "Unique identifier for the given record."
},
"email": {
"type": "string",
"description": "The email associated to the given user."
},
"first_name": {
"type": "string",
"description": "User's first name."
},
"last_name": {
"type": "string",
"description": "User's last name."
},
"full_name": {
"type": "string",
"description": "Full user name"
}
}
}
}
}
}
The model $ref
is supported but apparently has to be nested to work...
"user": { "$ref": "models/user.json#/components/schemas/user" },