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
+
6
+
## Purpose
7
+
I was making some API docs in [OpenAPI](https://www.openapis.org/) format and met 2 troubles:
8
+
1. one doc for whole API is too big
9
+
2. docs sometimes extend one another: add some, remove some, change some. For example, when API version changed I added some URL request parameters, renamed some, and removed others
10
+
3. when I have versions of API, how to get a list of changes?
11
+
12
+
So I wanted to:
13
+
1. Use `json` and `yaml` config files simultaneously
14
+
2. Split big files into pieces and being able to glue them back on reading
15
+
3. Have some inheritance technique for files (single and splitted) that allows describe changes and get a resulting document
16
+
17
+
Needs 1 and 2 are solved by [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)
18
+
Need 3 could be solved by something like [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch)
19
+
But all together it didn't work out of box, so I made a little patch to [json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser) and added JSON-Patch compiler.
20
+
And extended JSON-Patch selector for array elements
21
+
22
+
## Example
23
+
We have weather forecast database 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. Of course it is wrong idea, but just for example..
24
+
```
25
+
openapi: 3.0.1
26
+
info:
27
+
title: Readonly API for weather forecast
28
+
description: >-
29
+
Multiline
30
+
description of service.
31
+
version: 1.0.0
32
+
servers:
33
+
- url: 'https://weather.forecast/v1'
34
+
paths:
35
+
/city:
36
+
get:
37
+
summary: Get forecast for city by it name
38
+
operationId: getForecastInCity
39
+
parameters:
40
+
- name: names
41
+
in: query
42
+
description: Latin city names
43
+
required: true
44
+
schema:
45
+
type: array
46
+
items:
47
+
type: string
48
+
default: Moscow
49
+
responses:
50
+
'200':
51
+
description: successful operation
52
+
content:
53
+
application/json:
54
+
schema:
55
+
type: array
56
+
items:
57
+
$ref: '#/components/schemas/Forecast'
58
+
'400':
59
+
description: any error
60
+
content: {}
61
+
components:
62
+
schemas:
63
+
Forecast:
64
+
$ref: ./forecast.json
65
+
```
66
+
Next month we add forecasts for Belarus and we have troubles now: for example a town named 'Kamenka' exists in Russia and in Belarus
67
+
But we cant add parameter to v1 because some good people use our API in android app and it works ok in Russia and in Finland, and if we set 'default' country many requests will fail. Well, we could make some workarounds but.. it is just better to make correct next version of API.
68
+
So we need to replace unclear parameter 'names' with 'cities' and add 'country', this is how we could do it with [jybid](#jybid)
69
+
```
70
+
$inherit:
71
+
source:
72
+
$ref: ./api.v1.yaml
73
+
with:
74
+
- op: 'replace'
75
+
path: '/info/version'
76
+
value: 2.0.0
77
+
- op: 'replace'
78
+
path: '/servers/0/url'
79
+
value: 'https://weather.forecast/v2'
80
+
- op: 'remove'
81
+
path: '/paths/~1city/get/parameters/[name=names]'
82
+
- op: 'add'
83
+
path: '/paths/~1city/get/parameters/-'
84
+
value:
85
+
name: cities
86
+
in: query
87
+
description: Latin city names
88
+
required: true
89
+
schema:
90
+
type: array
91
+
items:
92
+
type: string
93
+
default: Moscow
94
+
- op: 'add'
95
+
path: '/paths/~1city/get/parameters/-'
96
+
value:
97
+
name: country
98
+
in: query
99
+
description: Latin country name
100
+
required: true
101
+
schema:
102
+
type: string
103
+
default: Russia
104
+
105
+
```
106
+
107
+
Referencing, bundling and inheritance is in keywords
Both if `options.inherit` is passed compile json-patches, for example:
121
+
122
+
For both
123
+
1. if `options.inherit==true` then json-patches will be compiled
124
+
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)
## Better JSON-Patch - [*] path parts for array index
31
-
In JSON-Patch **path** referencing array elements does not look good, which element do we remove here, you never know until you see **object**?
149
+
## Selectors
150
+
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'`. Which element do we remove here? You never know until you see **object**
32
151
```
33
152
object = [1,2,234]
34
153
patch = [{op: 'remove', path: '/2'}]
35
154
```
36
-
We can make **path** better if we select element by its properties, not index, so we'd like to replace numbers with **selectors**, like in jquery
155
+
We can 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
37
156
```
157
+
// patch with "selector" in path
38
158
patch = [{op: 'remove', path: '/[=234]'}]
39
-
/* we compile patch to receive standart */
159
+
160
+
// compiling patch
40
161
compilePatchOps(object, patch)
162
+
163
+
// patch with JSON-Pointer path
41
164
[{op: 'remove', path: '/2'}]
42
165
```
43
-
Inside `[*]` quotation is used:
44
-
*`\"` to get `"`
45
-
*`\\` to get `\`
46
-
`/` is `/`, `~` is `~`, not `~1` and `~0` like in JSON-Pointer
47
166
48
-
### Selectors
49
-
#### by property value pairs
167
+
Note that inside selector **[\*]** quotation is used:
168
+
*`\"` is `"`
169
+
*`\\` is `\`
170
+
*`/` is `/`
171
+
*`~` is `~`, not `~1` and `~0` like in [JSON-Pointer](https://tools.ietf.org/html/rfc6901)
172
+
173
+
so, selectors are:
174
+
175
+
### by property=value pairs
50
176
Value is treated as string if possible and as number if it can't be string
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
70
198
**compilePatchOps(source, patch) returns Array**
199
+
71
200
When source document is bundled we check every patch operation:
72
-
1. if it contains our `[*]` in **path**
73
-
2. corresponding object in source is array
74
-
then we replace it with equal operations with JSON-Pointer pathes, for example:
201
+
1. if it contains selector in **path**
202
+
2. corresponding object in **source** is array
203
+
204
+
then we replace it with equal operation with [JSON-Pointer](https://tools.ietf.org/html/rfc6901) path, for example:
0 commit comments