Skip to content

Commit 278fe7d

Browse files
committed
Create an app that can convert json schema to openAPI json objects
1 parent 99480ed commit 278fe7d

19 files changed

+3393
-0
lines changed

index.js

Whitespace-only changes.

package-lock.json

Lines changed: 2321 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "json-schema-openapi-json-schema",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "mocha --config './test/.mocharc.js'"
8+
},
9+
"author": "Jared Evans",
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/JaredCE/json-schema-to-openAPI-schema-object.git"
13+
},
14+
"license": "ISC",
15+
"devDependencies": {
16+
"chai": "^4.3.6",
17+
"mocha": "^10.0.0",
18+
"oas-validator": "^5.0.8",
19+
"sinon": "^14.0.0"
20+
},
21+
"dependencies": {
22+
"json-schema-traverse": "^1.0.0"
23+
}
24+
}

src/Convertor.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict'
2+
3+
const traverse = require('json-schema-traverse');
4+
5+
class Convertor {
6+
constructor(schema) {
7+
this.schema = JSON.parse(JSON.stringify(schema))
8+
9+
// this.specialProperties = ['allOf', 'oneOf', 'anyOf', 'not', 'items', 'properties', 'additionalProperties']
10+
this.specialProperties = ['allOf', 'anyOf', 'items', 'oneOf', 'not', 'properties']
11+
this.ofProperties = ['allOf', 'anyOf', 'oneOf']
12+
this.referencedSchemas = {}
13+
this.bannedKeyWords = ['$schema']
14+
15+
this.components = {
16+
schemas: {}
17+
}
18+
}
19+
20+
convert() {
21+
const traversal = (
22+
schema,
23+
jsonPointer,
24+
rootSchema,
25+
parentJSONPointer,
26+
parentKeyword,
27+
parentSchema,
28+
property
29+
) => {
30+
const inJSONLink = new RegExp('#[\/a-zA-Z0-9]+', 'g')
31+
const refExtraction = (properties) => {
32+
const path = properties['$ref'].split('/')
33+
const pathEnd = path[path.length-1]
34+
const newPath = `#/components/schemas/${pathEnd}`
35+
if (Object.keys(this.referencedSchemas).includes(newPath) === false) {
36+
Object.assign(this.referencedSchemas, {[newPath]: properties['$ref']})
37+
path.shift()
38+
const objPath = path.join('.')
39+
const newField = objPath.split('.').reduce((p,c)=>p&&p[c], rootSchema)
40+
Object.assign(this.components.schemas, {[pathEnd]: newField})
41+
properties['$ref'] = newPath
42+
} else {
43+
properties['$ref'] = newPath
44+
}
45+
}
46+
47+
const bannedWordsRemoval = () => {
48+
if (Object.keys(schema).some(item => this.bannedKeyWords.includes(item))) {
49+
for (const bannedWord of this.bannedKeyWords) {
50+
delete schema[bannedWord]
51+
}
52+
}
53+
}
54+
55+
bannedWordsRemoval()
56+
57+
if (this.specialProperties.indexOf(parentKeyword) !== -1) {
58+
if (this.ofProperties.indexOf(parentKeyword) !== -1) {
59+
for (const properties of parentSchema[parentKeyword]) {
60+
if (properties['$ref'] && inJSONLink.test(properties['$ref'])) {
61+
refExtraction(properties)
62+
} else {
63+
bannedWordsRemoval()
64+
}
65+
}
66+
} else {
67+
if (
68+
schema['$ref'] &&
69+
inJSONLink.test(schema['$ref'])
70+
) {
71+
refExtraction(schema)
72+
} else {
73+
bannedWordsRemoval()
74+
}
75+
}
76+
}
77+
}
78+
79+
traverse(this.schema, traversal)
80+
81+
if (Object.keys(this.components).includes('main') === false) {
82+
// for (const [key, value] of Object.entries(this.referencedSchemas)) {
83+
// const path = value.split('/')
84+
// path.shift()
85+
// delete this.schema[path]
86+
// // const objPath = path.join('.')
87+
// // const newField = objPath.split('.').reduce((p,c)=>p&&p[c], this.schema)
88+
// // delete this.schema
89+
// }
90+
// console.log(this.schema)
91+
92+
delete this.schema.definitions
93+
// if (this.schema.$schema) {
94+
// delete this.schema.$schema;
95+
// }
96+
Object.assign(this.components.schemas, {'main': this.schema})
97+
}
98+
return this.components
99+
}
100+
}
101+
102+
module.exports = Convertor

test/.mocharc.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
3+
module.exports = {
4+
recursive: true,
5+
reporter: 'spec',
6+
spec: 'test/**/*.spec.js',
7+
watch: true,
8+
'watch-files': ['src/**/*.js', 'test/**/*.spec.js'],
9+
}

test/openAPI/simple.json

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"title": "Simple API overview",
5+
"version": "2.0.0"
6+
},
7+
"paths": {
8+
"/": {
9+
"get": {
10+
"operationId": "listVersionsv2",
11+
"summary": "List API versions",
12+
"responses": {
13+
"200": {
14+
"description": "200 response",
15+
"content": {
16+
"application/json": {
17+
"examples": {
18+
"foo": {
19+
"value": {
20+
"versions": [
21+
{
22+
"status": "CURRENT",
23+
"updated": "2011-01-21T11:33:21Z",
24+
"id": "v2.0",
25+
"links": [
26+
{
27+
"href": "http://127.0.0.1:8774/v2/",
28+
"rel": "self"
29+
}
30+
]
31+
},
32+
{
33+
"status": "EXPERIMENTAL",
34+
"updated": "2013-07-23T11:33:21Z",
35+
"id": "v3.0",
36+
"links": [
37+
{
38+
"href": "http://127.0.0.1:8774/v3/",
39+
"rel": "self"
40+
}
41+
]
42+
}
43+
]
44+
}
45+
}
46+
}
47+
}
48+
}
49+
},
50+
"300": {
51+
"description": "300 response",
52+
"content": {
53+
"application/json": {
54+
"examples": {
55+
"foo": {
56+
"value": "{\n \"versions\": [\n {\n \"status\": \"CURRENT\",\n \"updated\": \"2011-01-21T11:33:21Z\",\n \"id\": \"v2.0\",\n \"links\": [\n {\n \"href\": \"http://127.0.0.1:8774/v2/\",\n \"rel\": \"self\"\n }\n ]\n },\n {\n \"status\": \"EXPERIMENTAL\",\n \"updated\": \"2013-07-23T11:33:21Z\",\n \"id\": \"v3.0\",\n \"links\": [\n {\n \"href\": \"http://127.0.0.1:8774/v3/\",\n \"rel\": \"self\"\n }\n ]\n }\n ]\n}\n"
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}
64+
},
65+
"/v2": {
66+
"get": {
67+
"operationId": "getVersionDetailsv2",
68+
"summary": "Show API version details",
69+
"responses": {
70+
"200": {
71+
"description": "200 response",
72+
"content": {
73+
"application/json": {
74+
"examples": {
75+
"foo": {
76+
"value": {
77+
"version": {
78+
"status": "CURRENT",
79+
"updated": "2011-01-21T11:33:21Z",
80+
"media-types": [
81+
{
82+
"base": "application/xml",
83+
"type": "application/vnd.openstack.compute+xml;version=2"
84+
},
85+
{
86+
"base": "application/json",
87+
"type": "application/vnd.openstack.compute+json;version=2"
88+
}
89+
],
90+
"id": "v2.0",
91+
"links": [
92+
{
93+
"href": "http://127.0.0.1:8774/v2/",
94+
"rel": "self"
95+
},
96+
{
97+
"href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf",
98+
"type": "application/pdf",
99+
"rel": "describedby"
100+
},
101+
{
102+
"href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
103+
"type": "application/vnd.sun.wadl+xml",
104+
"rel": "describedby"
105+
},
106+
{
107+
"href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
108+
"type": "application/vnd.sun.wadl+xml",
109+
"rel": "describedby"
110+
}
111+
]
112+
}
113+
}
114+
}
115+
}
116+
}
117+
}
118+
},
119+
"203": {
120+
"description": "203 response",
121+
"content": {
122+
"application/json": {
123+
"examples": {
124+
"foo": {
125+
"value": {
126+
"version": {
127+
"status": "CURRENT",
128+
"updated": "2011-01-21T11:33:21Z",
129+
"media-types": [
130+
{
131+
"base": "application/xml",
132+
"type": "application/vnd.openstack.compute+xml;version=2"
133+
},
134+
{
135+
"base": "application/json",
136+
"type": "application/vnd.openstack.compute+json;version=2"
137+
}
138+
],
139+
"id": "v2.0",
140+
"links": [
141+
{
142+
"href": "http://23.253.228.211:8774/v2/",
143+
"rel": "self"
144+
},
145+
{
146+
"href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf",
147+
"type": "application/pdf",
148+
"rel": "describedby"
149+
},
150+
{
151+
"href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
152+
"type": "application/vnd.sun.wadl+xml",
153+
"rel": "describedby"
154+
}
155+
]
156+
}
157+
}
158+
}
159+
}
160+
}
161+
}
162+
}
163+
}
164+
}
165+
}
166+
}
167+
}

test/schemas/complex-allOf.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "JSON API Schema",
4+
"description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
5+
"type": "object",
6+
"required": [
7+
"errors"
8+
],
9+
"properties": {
10+
"errors": {
11+
"type": "object",
12+
"properties": {
13+
"message": {
14+
"allOf": [
15+
{
16+
"$ref": "#/definitions/message"
17+
}
18+
]
19+
}
20+
}
21+
}
22+
},
23+
"definitions": {
24+
"message": {
25+
"type": "string"
26+
}
27+
}
28+
}

test/schemas/complex-anyOf.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "JSON API Schema",
4+
"description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
5+
"type": "object",
6+
"required": [
7+
"errors"
8+
],
9+
"properties": {
10+
"errors": {
11+
"type": "object",
12+
"properties": {
13+
"message": {
14+
"anyOf": [
15+
{
16+
"$ref": "#/definitions/message"
17+
}
18+
]
19+
}
20+
}
21+
}
22+
},
23+
"definitions": {
24+
"message": {
25+
"type": "string"
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)