You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This lib allows to bundle/dereference `json` and `yaml` documents and extend a document with [JSON-Patch](http://jsonpatch.com/)
5
5
6
+
[bundle](#bundle)
7
+
[dereference](#dereference)
8
+
[selectors](#selectors)
9
+
[examples](#examples)
10
+
6
11
## Purpose
7
-
When I was writing some API docs in [OpenAPI](https://www.openapis.org/) format I found some troubles:
12
+
* Split big documents
13
+
* Not only reference but inherit one documents from another
14
+
* Have differences between documents in form of [JSON-Patch](http://jsonpatch.com/)
15
+
16
+
Why does somebody want that?
17
+
Well, when I was writing some API docs in [OpenAPI](https://www.openapis.org/) format I had troubles:
8
18
1. one doc for whole API is too big
9
-
2. docs sometimes logically extend one another.
10
-
3.when I have several API versions, how to get a list of changes between them? It would be great to have it in form of [JSON-Patch](http://jsonpatch.com/) and visualize [somehow](https://github.com/benjamine/jsondiffpatch)
19
+
2. docs sometimes logically extend one another
20
+
3. how to get diff between versions of API? It would be great to have [JSON-Patch](http://jsonpatch.com/) and visualize it[somehow](https://github.com/benjamine/jsondiffpatch)
11
21
12
22
So I wanted to:
13
23
1. Use `json` and `yaml` config files simultaneously, being able to split big files into several smaller
14
-
2. Have some inheritance technique
15
-
16
-
First part is fairly easy, it can be solved by [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)
17
-
Second part also has a solution like [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch)
18
-
But all together it doesn't work out of box, so I made a little patch to [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser) that enables inheritance based on JSON-Patch syntax.
19
-
Also I extended JSON-Pointer in case when we point to something in array and it is called [array selector](#selectors).
20
-
21
-
## Example of how can JYBID be used
22
-
Suppose we have a weather forecast database and want to build a readonly service for it. We have data only for Russia and Finland and we supposed that cities in these countries always have different names so we dont need to specify country in request to our service. Of course this is not the best idea, but just for example..
23
-
So, we have a sevice API
24
-
25
-
```
26
-
openapi: 3.0.1
27
-
info:
28
-
title: Readonly API for weather forecast service
29
-
description: >-
30
-
Multiline
31
-
description of service.
32
-
version: 1.0.0
33
-
servers:
34
-
- url: 'https://weather.forecast/v1'
35
-
paths:
36
-
/city:
37
-
get:
38
-
summary: Get forecast for city by name
39
-
operationId: getForecastInCity
40
-
parameters:
41
-
- name: names
42
-
in: query
43
-
description: Latin city names
44
-
required: true
45
-
schema:
46
-
type: array
47
-
items:
48
-
type: string
49
-
default: Moscow
50
-
responses:
51
-
'200':
52
-
description: successful operation
53
-
content:
54
-
application/json:
55
-
schema:
56
-
type: array
57
-
items:
58
-
$ref: '#/components/schemas/Forecast'
59
-
'400':
60
-
description: any error
61
-
content: {}
62
-
components:
63
-
schemas:
64
-
Forecast:
65
-
$ref: ./forecast.json
66
-
```
67
-
68
-
Next month we add forecasts for Belarus and we have troubles now: for example a town named 'Kamenka' exists in Russia and in Belarus.
69
-
Of course we need to add parameter `country` but we can't add it to v1 because some people already use our API in their app and it works ok in Russia and in Finland. If we add `country` with some default value many requests will fail. Well, we could make some workarounds based on city name being checked against list of all cities in our 3 countries but.. it is just better to make next version of API correct and ask our clients to use it instead of incorrect v1.
70
-
So we need to replace unclear parameter 'names' with 'cities' and add 'country', this is how we could do it with [jybid](#jybid)
71
-
72
-
```
73
-
$inherit:
74
-
source:
75
-
$ref: ./api.v1.yaml
76
-
with:
77
-
- op: 'replace'
78
-
path: '/info/version'
79
-
value: 2.0.0
80
-
- op: 'replace'
81
-
path: '/servers/0/url'
82
-
value: 'https://weather.forecast/v2'
83
-
- op: 'remove'
84
-
path: '/paths/~1city/get/parameters/[name=names]'
85
-
- op: 'add'
86
-
path: '/paths/~1city/get/parameters/-'
87
-
value:
88
-
name: cities
89
-
in: query
90
-
description: Latin city names
91
-
required: true
92
-
schema:
93
-
type: array
94
-
items:
95
-
type: string
96
-
default: Moscow
97
-
- op: 'add'
98
-
path: '/paths/~1city/get/parameters/-'
99
-
value:
100
-
name: country
101
-
in: query
102
-
description: Latin country name
103
-
required: true
104
-
schema:
105
-
type: string
106
-
default: Russia
107
-
```
108
-
109
-
Referencing, bundling and inheritance is in keywords
110
-
111
-
$inherit, source, with
112
-
113
-
Array selector is in line
24
+
2. Have some inheritance technique based on JSON-Patch
114
25
115
-
path: '/paths/~1city/get/parameters/[name=names]'
26
+
1 comes easy, it can be solved by [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)
27
+
2 also has a solution like [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch)
28
+
But all together it doesn't work out of box.
29
+
So I made a little patch to [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser) that enables inheritance based on JSON-Patch syntax.
30
+
Also I extended JSON-Pointer for array indexes [array selector](#selectors).
116
31
117
-
## Methods: Bundle & Dereference
118
-
To bundle a file
119
-
**bundle(filepath, options) returns Promise**
32
+
## Methods
33
+
###bundle
34
+
**bundle(filepath, options) returns Promise**- to bundle a file
120
35
121
-
To bundle a file and resolve even internal references
**dereference(filepath, options) returns Promise**- to bundle a file and resolve even internal references
123
38
124
39
For both
125
-
1. if `options.inherit==true` then json-patches will be compiled
126
-
2. if `options.inherit` is a string then it will set a keyword instead of `"$inherit"` and if it is `"$patch"` then document syntax complies to [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch)
40
+
* if `options.inherit==true` then json-patches will be compiled
41
+
* if `options.inherit` is a string then it will set a keyword instead of `"$inherit"` and if it is `"$patch"` then document syntax complies to [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch)
To compile [JSON-Patch](http://jsonpatch.com/) with [selectors](#selectors) in pathes to [JSON-Patch](http://jsonpatch.com/) with [JSON-Pointer](https://tools.ietf.org/html/rfc6901) pathes there is a method
74
+
75
+
When source document is bundled we check every patch operation:
76
+
1. if it contains selector in **path**
77
+
2. corresponding object in **source** is array
78
+
79
+
then we replace it with equal operation with [JSON-Pointer](https://tools.ietf.org/html/rfc6901) path, for example:
In [JSON-Patch](http://jsonpatch.com/)**path** must be a [JSON-Pointer](https://tools.ietf.org/html/rfc6901), but referencing array elements does not look good: `path: '/2'`. At what element do we point? You never know until you see **object** for which we apply the patch
94
+
[by property value](#by_property=value_pairs)
95
+
[with property](#with_property)
96
+
[with value](#with_value)
97
+
98
+
In [JSON-Patch](http://jsonpatch.com/)**path** must be a [JSON-Pointer](https://tools.ietf.org/html/rfc6901), but referencing array elements does not look good: what means `path: '/2'` ?. At what element do we point? You never know until you see **object** to which you apply the patch
153
99
```
154
100
object = [1,2,234]
155
101
patch = [{op: 'remove', path: '/2'}]
156
102
```
157
-
So, we could make **path** look better if we select element by its properties or value instead of index in array, so we'd like to replace numbers with **selectors**, like in jquery
103
+
Now we know, we wanted to remove `234`.
104
+
Lets select element by its properties or value like in jquery
To compile [JSON-Patch](http://jsonpatch.com/) with [selectors](#selectors) in pathes to [JSON-Patch](http://jsonpatch.com/) with [JSON-Pointer](https://tools.ietf.org/html/rfc6901) pathes there is a method
200
-
201
-
**compilePatchOps(source, patch) returns Array**
146
+
## Examples
147
+
### Long story short
148
+
Weather service API v1 file [api_v1](./examples/api_1.0.yaml)
149
+
```
150
+
node index.js bundle --file examples/api_v1.yaml
151
+
```
152
+
And you have [bundled](./examples/api_v1.bundled.json) API file for version 1.
202
153
203
-
When source document is bundled we check every patch operation:
204
-
1. if it contains selector in **path**
205
-
2. corresponding object in **source** is array
154
+
Next you have version 2 [api_v2](./examples/api_2.0.yaml), look how short it is. Compile it to have correct service API file for version 2
155
+
```
156
+
node index.js bundle --file examples/api_v2.yaml
157
+
```
158
+
Without JYBID you'd have to write it manually: [bundled](./examples/api_v2.bundled.json)
206
159
207
-
then we replace it with equal operation with [JSON-Pointer](https://tools.ietf.org/html/rfc6901) path, for example:
160
+
### Long story long
161
+
Suppose we have a weather forecast database and want to build a readonly service for it. We have data only for Russia and Finland and we supposed that cities in these countries always have different names so we dont need to specify country in request to our service. Of course this is not the best idea, but just for example..
162
+
So, we have a sevice API [api_v1](./examples/api_1.0.yaml).
163
+
Also there are [bundled](./examples/api_v1.bundled.json) and [dereferenced](./examples/api_v1.dereferenced.json) documents.
164
+
You can try bundling or dereferencing like this
165
+
```
166
+
node index.js bundle --file examples/api_v1.yaml
167
+
```
168
+
169
+
Ok, next month we add forecasts for Belarus and we have troubles now: for example a town named 'Kamenka' exists in Russia and in Belarus.
170
+
Of course we need to add parameter `country` but we can't add it to v1 because some people already use our API in their app and it works ok in Russia and in Finland. If we add `country` with some default value many requests will fail. Well, we could make some workarounds based on city name being checked against list of all cities in our 3 countries but.. it is just better to make next version of API correct and ask our clients to use it instead of incorrect v1.
171
+
So we need to replace unclear parameter 'names' with 'cities' and add 'country', this [api_v2](./examples/api_2.0.yaml) is how we could do it with [jybid](#jybid)
172
+
Without inheritance you would have to make one of these documents manually [bundled](./examples/api_v2.bundled.json)[dereferenced](./examples/api_v2.dereferenced.json)
208
173
174
+
In [api_v2](./examples/api_2.0.yaml) you can find examples of inheritance
0 commit comments