@empathyco/x-adapter 8.0.0-alpha.14 → 8.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +454 -5
- package/dist/cjs/mappers/schema-mapper.factory.js.map +1 -1
- package/dist/cjs/schemas/types.js.map +1 -1
- package/dist/cjs/schemas/utils.js +7 -5
- package/dist/cjs/schemas/utils.js.map +1 -1
- package/dist/esm/mappers/schema-mapper.factory.js.map +1 -1
- package/dist/esm/schemas/types.js.map +1 -1
- package/dist/esm/schemas/utils.js +7 -5
- package/dist/esm/schemas/utils.js.map +1 -1
- package/dist/types/mappers/schema-mapper.factory.d.ts +2 -2
- package/dist/types/schemas/types.d.ts +6 -6
- package/dist/types/schemas/utils.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,462 @@
|
|
|
1
1
|
# x-adapter
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
**Empathy Adapter** is a library of utils to ease the communication with any API.
|
|
4
|
+
|
|
5
|
+
Some features that it provides:
|
|
5
6
|
|
|
6
7
|
- Create an API request function based on a simple configuration.
|
|
8
|
+
- Allow to configure several endpoints by extending the initial configuration.
|
|
7
9
|
- Allow to configure the response/request mapping.
|
|
8
10
|
- Create mapping functions based on Schemas.
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
<br>
|
|
13
|
+
|
|
14
|
+
## Tech Stack
|
|
15
|
+
|
|
16
|
+
[](https://www.typescriptlang.org/)
|
|
17
|
+
[](https://jestjs.io/)
|
|
18
|
+
|
|
19
|
+
<br>
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npm i @empathyco/x-adapter
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If you use this package together with
|
|
28
|
+
[x-components](https://github.com/empathyco/x/tree/main/packages/x-components), you should
|
|
29
|
+
additionally install the
|
|
30
|
+
[@empathyco/x-types](https://github.com/empathyco/x/tree/main/packages/search-types) package and
|
|
31
|
+
take the advantage of it in your project development.
|
|
32
|
+
|
|
33
|
+
<br>
|
|
34
|
+
|
|
35
|
+
## Configuration & Usage
|
|
36
|
+
|
|
37
|
+
An `API Adapter` is a collection of `EndpointAdapters`, one for each endpoint of the API you want to
|
|
38
|
+
consume. Each `EndpointAdapter` is an asynchronous function that performs a request with the given
|
|
39
|
+
data, and returns a response promise with the requested data. Internally, it usually has to
|
|
40
|
+
transform the request data so the API can understand it, and the response data so your app
|
|
41
|
+
understands it as well.
|
|
42
|
+
|
|
43
|
+
<br>
|
|
44
|
+
|
|
45
|
+
### Implement your own adapter
|
|
46
|
+
|
|
47
|
+
To create an `EndpointAdapter` you can use the `endpointAdapterFactory` function. This function will
|
|
48
|
+
receive an `EndpointAdapterOptions` object containing all the needed data to perform and map a
|
|
49
|
+
request, and return a function that when invoked will trigger the request. The options that can be
|
|
50
|
+
configured are:
|
|
51
|
+
|
|
52
|
+
- `endpoint`: The URL that the `httpClient` uses. It can be either a string or a mapper function
|
|
53
|
+
that dynamically generates the URL string using the request data.
|
|
54
|
+
- `httpClient`: A function that will receive the endpoint and request options such as the parameters
|
|
55
|
+
and will perform the request, returning a promise with the unprocessed response data.
|
|
56
|
+
- `defaultRequestOptions`: Default values for the endpoint configuration. You can use it to define
|
|
57
|
+
if a request is cancelable, a unique id to identify it, anything but the `endpoint` can be set.
|
|
58
|
+
Check
|
|
59
|
+
[EndpointAdapterOptions](https://github.com/empathyco/x/blob/main/packages/x-adapter/src/endpoint-adapter/types.ts)
|
|
60
|
+
to see the available options.
|
|
61
|
+
- `requestMapper`: A function to transform the unprocessed request into parameters the API can
|
|
62
|
+
understand.
|
|
63
|
+
- `responseMapper`: A function to transform the API response into data that your project can
|
|
64
|
+
understand.
|
|
65
|
+
|
|
66
|
+
<br>
|
|
67
|
+
|
|
68
|
+
#### Basic adapter implementation
|
|
69
|
+
|
|
70
|
+
In this example we have a simple request mapper that will add a `q` parameter to the endpoint's url
|
|
71
|
+
to perform the request. If you check the function call below, you will see the query parameter
|
|
72
|
+
passed.
|
|
73
|
+
|
|
74
|
+
###### Types definition
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// API data models
|
|
78
|
+
interface ApiRequest {
|
|
79
|
+
q?: string;
|
|
80
|
+
id?: number;
|
|
81
|
+
}
|
|
82
|
+
interface ApiSearchResponse {
|
|
83
|
+
products: ApiProduct[];
|
|
84
|
+
total: number;
|
|
85
|
+
}
|
|
86
|
+
interface ApiProduct {
|
|
87
|
+
id: number;
|
|
88
|
+
title: string;
|
|
89
|
+
price: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// App's data models
|
|
93
|
+
interface AppSearchRequest {
|
|
94
|
+
query: string;
|
|
95
|
+
}
|
|
96
|
+
interface AppSearchResponse {
|
|
97
|
+
products: AppProduct[];
|
|
98
|
+
total: number;
|
|
99
|
+
}
|
|
100
|
+
interface AppProduct {
|
|
101
|
+
id: string;
|
|
102
|
+
name: string;
|
|
103
|
+
price: number;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
###### Adapter's factory function implementation
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { endpointAdapterFactory } from '@empathyco/x-adapter';
|
|
111
|
+
|
|
112
|
+
export const searchProducts = endpointAdapterFactory({
|
|
113
|
+
endpoint: 'https://dummyjson.com/products/search',
|
|
114
|
+
requestMapper({ query }: Readonly<AppSearchRequest>): ApiRequest {
|
|
115
|
+
return {
|
|
116
|
+
q: query // the request will be triggered as https://dummyjson.com/products/search?q=phone
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
responseMapper({ products, total }: Readonly<ApiSearchResponse>): AppSearchResponse {
|
|
120
|
+
return {
|
|
121
|
+
products: products.map(product => {
|
|
122
|
+
return {
|
|
123
|
+
id: product.id.toString(),
|
|
124
|
+
name: product.title,
|
|
125
|
+
price: product.price
|
|
126
|
+
};
|
|
127
|
+
}),
|
|
128
|
+
total: total
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Function call
|
|
134
|
+
async function searchOnClick() {
|
|
135
|
+
const response = await searchProducts({ query: 'phone' });
|
|
136
|
+
console.log('products', response.products);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
<br>
|
|
141
|
+
|
|
142
|
+
#### Using a dynamic endpoint
|
|
143
|
+
|
|
144
|
+
If you need to generate an endpoint url dynamically, you can add parameters inside curly brackets to
|
|
145
|
+
the endpoint string. By default, these parameters will be replaced using the request data. If a
|
|
146
|
+
parameter is not found inside the request, an empty string will be used.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
export const getItemById = endpointAdapterFactory({
|
|
150
|
+
endpoint: 'https://dummyjson.com/{section}/{id}'
|
|
151
|
+
// ... rest of options to configure
|
|
152
|
+
});
|
|
153
|
+
getItemById({ section: 'products', id: 1 }); // 'https://dummyjson.com/products/1'
|
|
154
|
+
getItemById({ section: 'quotes', id: 3 }); // 'https://dummyjson.com/quotes/3'
|
|
155
|
+
getItemById({ section: 'quotes' }); // 'https://dummyjson.com/quotes/'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
For more complex use cases, you can use a mapper function. This function receives the request, and
|
|
159
|
+
must return the URL string.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
export const getProductById = endpointAdapterFactory({
|
|
163
|
+
endpoint: ({ id }: GetProductByIdRequest) => 'https://dummyjson.com/products/' + id
|
|
164
|
+
// ... rest of options to configure
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Additionally, you can also overwrite your adapter's endpoint definition using the
|
|
169
|
+
`RequestOptions.endpoint` parameter in the function call. Take into account that your
|
|
170
|
+
`responseMapper` definition should be agnostic enough to support the change:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
export const getItemById = endpointAdapterFactory({
|
|
174
|
+
endpoint: 'https://dummyjson.com/quotes/{id}',
|
|
175
|
+
// ... rest of options to configure
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// You would pass the new endpoint in the function call
|
|
179
|
+
getItemById({ id: 1 }, { endpoint: 'https://dummyjson.com/products/{id}');
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
<br>
|
|
183
|
+
|
|
184
|
+
#### Using the httpClient function
|
|
185
|
+
|
|
186
|
+
Every adapter created using `endpointAdapterFactory` uses the `Fetch API` by default to perform the
|
|
187
|
+
requests. But you can use your own `HttpClient` as part of the configurable
|
|
188
|
+
`EndpointAdapterOptions`. A `HttpClient` is a function that accepts two parameters: the `endpoint`
|
|
189
|
+
string, and an additional
|
|
190
|
+
[`options`](https://github.com/empathyco/x/blob/main/packages/x-adapter/src/http-clients/types.ts)
|
|
191
|
+
object to make the request with.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
// HTTP Client
|
|
195
|
+
const axiosHttpClient: HttpClient = (endpoint, options) =>
|
|
196
|
+
axios.get(endpoint, { params: options?.parameters }).then(response => response.data);
|
|
197
|
+
|
|
198
|
+
// Request Mapper
|
|
199
|
+
const customRequestMapper: Mapper<AppSearchRequest, ApiRequest> = ({ query }) => {
|
|
200
|
+
return {
|
|
201
|
+
q: query
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Response Mapper
|
|
206
|
+
const customResponseMapper: Mapper<ApiSearchResponse, AppSearchResponse> = ({
|
|
207
|
+
products,
|
|
208
|
+
total
|
|
209
|
+
}) => {
|
|
210
|
+
return {
|
|
211
|
+
products: products.map(product => {
|
|
212
|
+
return {
|
|
213
|
+
id: product.id.toString(),
|
|
214
|
+
name: product.title,
|
|
215
|
+
price: product.price
|
|
216
|
+
};
|
|
217
|
+
}),
|
|
218
|
+
total: total
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Adapter factory function implementation
|
|
223
|
+
export const searchProducts = endpointAdapterFactory({
|
|
224
|
+
endpoint: 'https://dummyjson.com/products/search',
|
|
225
|
+
httpClient: axiosHttpClient,
|
|
226
|
+
requestMapper: customRequestMapper,
|
|
227
|
+
responseMapper: customResponseMapper
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
<br>
|
|
232
|
+
|
|
233
|
+
### Implement your own adapter using schemas
|
|
234
|
+
|
|
235
|
+
Sometimes the transformations you will need to do in the mappers are just renaming parameters. What
|
|
236
|
+
the API calls `q` might be called `query` in your request. To ease this transformations,
|
|
237
|
+
`@empathyco/x-adapter` allows to create mappers using schemas.
|
|
238
|
+
|
|
239
|
+
A schema is just a dictionary where the key is the desired parameter name, and the value is the path
|
|
240
|
+
of the source object that has the desired value or a simple mapper function if you need to transform
|
|
241
|
+
the value somehow.
|
|
242
|
+
|
|
243
|
+
###### Types definition
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
// API data models
|
|
247
|
+
interface ApiUserRequest {
|
|
248
|
+
q: string;
|
|
249
|
+
}
|
|
250
|
+
interface ApiUserResponse {
|
|
251
|
+
users: ApiUser[];
|
|
252
|
+
total: number;
|
|
253
|
+
}
|
|
254
|
+
interface ApiUser {
|
|
255
|
+
id: number;
|
|
256
|
+
firstName: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// App's data models
|
|
260
|
+
interface AppUserRequest {
|
|
261
|
+
query: string;
|
|
262
|
+
}
|
|
263
|
+
interface AppUserResponse {
|
|
264
|
+
people: AppUser[];
|
|
265
|
+
total: number;
|
|
266
|
+
}
|
|
267
|
+
interface AppUser {
|
|
268
|
+
id: string;
|
|
269
|
+
name: string;
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
###### Schema's mapper factory function implementation
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
// Map both the request and the response to connect your model with the API you are working with.
|
|
277
|
+
const userSearchRequestMapper = schemaMapperFactory<AppUserRequest, ApiUserRequest>({
|
|
278
|
+
q: 'query'
|
|
279
|
+
});
|
|
280
|
+
const userSearchResponseMapper = schemaMapperFactory<ApiUserResponse, AppUserResponse>({
|
|
281
|
+
people: ({ users }) =>
|
|
282
|
+
users.map(user => {
|
|
283
|
+
return {
|
|
284
|
+
id: user.id.toString(),
|
|
285
|
+
name: user.firstName
|
|
286
|
+
};
|
|
287
|
+
}),
|
|
288
|
+
total: 'total'
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Use the mappers in the Endpoint's adapter factory function
|
|
292
|
+
export const searchUsers = endpointAdapterFactory({
|
|
293
|
+
endpoint: 'https://dummyjson.com/users/search',
|
|
294
|
+
requestMapper: userSearchRequestMapper,
|
|
295
|
+
responseMapper: userSearchResponseMapper
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
<br>
|
|
300
|
+
|
|
301
|
+
#### Create more complex models with SubSchemas
|
|
302
|
+
|
|
303
|
+
When you are creating adapters for different APIs you might find the case that you have to map the
|
|
304
|
+
same model in different places. To help you with that, schemas allows to use `SubSchemas`. To use
|
|
305
|
+
them you just have to provide with the `Path` of the data to map, and the `Schema` to apply to it.
|
|
306
|
+
|
|
307
|
+
###### Types definition
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
// API data models
|
|
311
|
+
interface ApiRequest {
|
|
312
|
+
q: string;
|
|
313
|
+
}
|
|
314
|
+
interface ApiResponse {
|
|
315
|
+
users: ApiUser[];
|
|
316
|
+
total: number;
|
|
317
|
+
}
|
|
318
|
+
interface ApiUser {
|
|
319
|
+
id: number;
|
|
320
|
+
email: string;
|
|
321
|
+
phone: string;
|
|
322
|
+
address: ApiAddress;
|
|
323
|
+
company: ApiAddress;
|
|
324
|
+
}
|
|
325
|
+
interface ApiAddress {
|
|
326
|
+
address: string;
|
|
327
|
+
city: string;
|
|
328
|
+
postalCode: string;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// APP data models
|
|
332
|
+
interface AppRequest {
|
|
333
|
+
query: string;
|
|
334
|
+
}
|
|
335
|
+
interface AppResponse {
|
|
336
|
+
people: AppUser[];
|
|
337
|
+
count: number;
|
|
338
|
+
}
|
|
339
|
+
interface AppUser {
|
|
340
|
+
id: number;
|
|
341
|
+
contact: {
|
|
342
|
+
email: string;
|
|
343
|
+
phone: string;
|
|
344
|
+
homeAddress: AppAddress;
|
|
345
|
+
companyAddress: AppAddress;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
interface AppAddress {
|
|
349
|
+
displayName: string;
|
|
350
|
+
postalCode: number;
|
|
351
|
+
city: string;
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
###### Schemas and SubSchemas implementation
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
// Address Schema definition
|
|
359
|
+
const addressSchema: Schema<ApiAddress, AppUserAddress> = {
|
|
360
|
+
displayName: source => `${source.address}, ${source.postalCode} - ${source.city}`,
|
|
361
|
+
city: 'city',
|
|
362
|
+
postalCode: source => parseInt(source.postalCode)
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// User Schema definition with a subSchema
|
|
366
|
+
const userSchema: Schema<ApiUser, AppUser> = {
|
|
367
|
+
id: 'id',
|
|
368
|
+
contact: {
|
|
369
|
+
email: source => source.email.toLowerCase(),
|
|
370
|
+
phone: 'phone',
|
|
371
|
+
homeAddress: {
|
|
372
|
+
$subSchema: addressSchema,
|
|
373
|
+
$path: 'address'
|
|
374
|
+
},
|
|
375
|
+
companyAddress: {
|
|
376
|
+
$subSchema: addressSchema,
|
|
377
|
+
$path: 'address'
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
###### Schema's mapper factory function implementation with subSchemas
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
// Response mapper with user's subSchema implemented
|
|
387
|
+
const responseMapper = schemaMapperFactory<ApiSearchUsersResponse, AppSearchUsersResponse>({
|
|
388
|
+
people: {
|
|
389
|
+
$subSchema: userSchema,
|
|
390
|
+
$path: 'users'
|
|
391
|
+
},
|
|
392
|
+
count: 'total'
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const requestMapper = schemaMapperFactory<SearchUsersRequest, ApiSearchUsersRequest>({
|
|
396
|
+
q: 'query'
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Endpoint Adapter Factory function implementation
|
|
400
|
+
export const searchUsersWithContactInfo = endpointAdapterFactory({
|
|
401
|
+
endpoint: 'https://dummyjson.com/users/search',
|
|
402
|
+
requestMapper,
|
|
403
|
+
responseMapper
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
<br>
|
|
408
|
+
|
|
409
|
+
#### Using a mutable schema
|
|
410
|
+
|
|
411
|
+
This feature lets you have some default mappers, and modify or extend them for some concrete
|
|
412
|
+
implementations. To do so, you should use the `createMutableSchema` helper function, and pass as a
|
|
413
|
+
parameter the schema you want to make mutable.
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
// TODO: Creating a mutable schema
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Once you have your mutable schema, you can use its available methods to obtain a new schema based on
|
|
420
|
+
it:
|
|
421
|
+
|
|
422
|
+
- `replace`: Replaces completely the original Schema.
|
|
423
|
+
- `override`: Merges the original schema with the new one.
|
|
424
|
+
- `extends`: Creates a new Schema based on the original one. The original remains unchanged.
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
// TODO: Mutable schema's methods: '$replace', '$override', '$extends'
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
<br>
|
|
431
|
+
|
|
432
|
+
### Extend an adapter that uses schemas
|
|
433
|
+
|
|
434
|
+
You can check the
|
|
435
|
+
[x-platform-adapter](https://github.com/empathyco/x/tree/main/packages/x-adapter-platform) library.
|
|
436
|
+
You will find a sample implementation of the `x-adapter` library based on the
|
|
437
|
+
[Search Platform API](https://docs.empathy.co/develop-empathy-platform/api-reference/search-api.html),
|
|
438
|
+
and also some guidance on how to extend it for your needs.
|
|
439
|
+
|
|
440
|
+
## Test
|
|
441
|
+
|
|
442
|
+
**Empathy Adapter** features are tested using [Jest](https://jestjs.io/). You will find a
|
|
443
|
+
`__tests__` folder inside each of the project's sections.
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
npm test
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Changelog
|
|
450
|
+
|
|
451
|
+
[Changelog summary](https://github.com/empathyco/x/blob/main/packages/x-adapter/CHANGELOG.md)
|
|
452
|
+
|
|
453
|
+
## Contributing
|
|
454
|
+
|
|
455
|
+
To start contributing to the project, please take a look to
|
|
456
|
+
our **[Contributing Guide](https://github.com/empathyco/x/blob/main/.github/CONTRIBUTING.md).** Take
|
|
457
|
+
in account that `x-adapter` is developed using [Typescript](https://www.typescriptlang.org/), so we
|
|
458
|
+
recommend you check it out.
|
|
459
|
+
|
|
460
|
+
## License
|
|
11
461
|
|
|
12
|
-
|
|
13
|
-
[Contributing Guide](../../.github/CONTRIBUTING.md).
|
|
462
|
+
[empathyco/x License](https://github.com/empathyco/x/blob/main/packages/x-adapter/LICENSE)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-mapper.factory.js","sourceRoot":"","sources":["../../../src/mappers/schema-mapper.factory.ts"],"names":[],"mappings":";;;AAAA,0DAAoD;AACpD,gDAS4B;AAE5B,4CAAyE;AAGzE;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CACjC,
|
|
1
|
+
{"version":3,"file":"schema-mapper.factory.js","sourceRoot":"","sources":["../../../src/mappers/schema-mapper.factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { deepMerge } from '@empathyco/x-deep-merge';\nimport {\n Dictionary,\n ExtractPath,\n getSafePropertyChain,\n isArray,\n isFunction,\n isObject,\n isPath,\n reduce\n} from '@empathyco/x-utils';\nimport { MutableSchema, Schema, SubSchemaTransformer } from '../schemas/types';\nimport { createMutableSchema, isInternalMethod } from '../schemas/utils';\nimport { Mapper, MapperContext } from './types';\n\n/**\n * The 'schemaMapperFactory' function creates a {@link Mapper | mapper function} for a given\n * {@link Schema | schema}.\n *\n * @param schema - The {@link Schema | schema} to apply in the {@link Mapper | mapper function}.\n * @returns A {@link Mapper | mapper function} that applies the given {@link Schema | schema}.\n * @public\n */\nexport function schemaMapperFactory<Source, Target>(\n schema: Schema<Source, Target> | MutableSchema<Source, Target>\n): Mapper<Source, Target> {\n return function mapper(source: Source, context: MapperContext): Target {\n return mapSchema(source, schema, context);\n };\n}\n\n/**\n * The `mapSchema()` function creates a new object populated with the transformations defined by a\n * {@link Schema} applied to a source object.\n *\n * @param source - The object to apply the transformations to.\n * @param schema - The object that defines the transformations to apply.\n * @param context - The {@link MapperContext | mapper context} to feed the transformations with.\n * @returns A new object with each element being the result of the applied transformation.\n * @internal\n */\nfunction mapSchema<Source, Target>(\n source: Source,\n schema: Schema<Source, Target>,\n context: MapperContext\n): Target {\n if (!source) {\n //eslint-disable-next-line no-console\n console.warn('This schema cannot be applied', createMutableSchema(schema));\n return undefined as any;\n }\n return reduce(\n schema,\n (target, key, transformer) => {\n type TargetKey = Target[keyof Target];\n if (typeof transformer === 'string' && isPath(source, transformer)) {\n target[key] = getSafePropertyChain(source, transformer) as TargetKey;\n } else if (isFunction(transformer) && !isInternalMethod(transformer.name)) {\n target[key] = transformer(source, context);\n } else if (isObject(transformer)) {\n const value =\n '$subSchema' in transformer\n ? (applySubSchemaTransformer<Source, TargetKey>(\n source,\n transformer as SubSchemaTransformer<Source, TargetKey>,\n context,\n schema as unknown as Schema<Source, TargetKey>\n ) as TargetKey)\n : mapSchema<Source, TargetKey>(source, transformer, context);\n\n if (value) {\n target[key] = value;\n }\n }\n return target;\n },\n {} as Target\n );\n}\n\n/**\n * The `applySubSchemaTransformer()` function executes a `mapSchema()` function applying the defined\n * {@link SubSchemaTransformer.$subSchema}.\n *\n * @param source - The object to feed the schema.\n * @param subSchemaTransformer - The {@link SubSchemaTransformer} object with a $path, $subSchema\n * and $context options.\n * @param subSchemaTransformer.$path\n * @param subSchemaTransformer.$subSchema\n * @param rawContext - The {@link MapperContext | mapper context} to feed the mapSchema function.\n * @param subSchemaTransformer.$context\n * @param schema - The {@link Schema} to apply.\n * @returns The result of calling `mapSchema()` with the source, schema and context arguments.\n * @internal\n */\nfunction applySubSchemaTransformer<Source, Target>(\n source: Source,\n { $subSchema, $path, $context }: SubSchemaTransformer<Source, Target>,\n rawContext: MapperContext,\n schema: Schema<Source, Target>\n): Target | Target[] | undefined {\n const subSource = getSafePropertyChain(source, $path);\n\n if (!subSource) {\n return;\n }\n\n const extendedContext: Dictionary = {};\n if ($context) {\n Object.entries($context).forEach(([key, value]) => {\n if (['requestParameters', 'endpoint', 'mappedValue'].includes(key)) {\n return;\n }\n extendedContext[key] = isFunction(value)\n ? value(source)\n : getSafePropertyChain(source, value as ExtractPath<typeof source>);\n });\n }\n\n const context = deepMerge({}, rawContext, $context, extendedContext);\n let subSchema: typeof $subSchema | typeof schema;\n if ($subSchema === '$self') {\n subSchema = schema;\n } else if (isFunction($subSchema)) {\n subSchema = $subSchema(source);\n } else {\n subSchema = $subSchema;\n }\n return isArray(subSource)\n ? subSource.map(item => mapSchema(item, subSchema, context) as Target)\n : mapSchema<typeof subSource, Target>(\n subSource,\n subSchema as Schema<typeof subSource, Target>,\n context\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/schemas/types.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AnyFunction,\n ExtractPath,\n ExtractPathByType,\n ExtractType,\n Primitive\n} from '@empathyco/x-utils';\nimport { MapperContext } from '../mappers/types';\n\n// TODO: EX-5830 - Enhance Schema type to support optional properties in the Source object\n/**\n * Template object to transform a source object to a target object.\n *\n * @remarks The source object must not have optional properties, as it could cause infinite\n * type instantiations.\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * price: {\n * max: number;\n * min: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * maxPrice: number;\n * minPrice: number;\n * img: string[]\n * }\n *\n * const schema: Schema<Source, Target> = {\n * identifier: 'id',\n * maxPrice: 'price.max',\n * minPrice: ({ price }) => Math.min(0, price.min),\n * img: 'images'\n * };\n * ```\n * @public\n */\nexport type Schema<Source = any, Target = any> = {\n [TargetKey in keyof Target]: SchemaTransformer<Source, Target, TargetKey>;\n};\n\n/**\n * A {@link Schema | schema} with extended functionality to: completely replace\n * the original schema, partially override it or create a new one.\n *\n * @param OriginalSchema - The {@link Schema | schema} that will be mutable.\n * @public\n */\nexport type MutableSchema<
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/schemas/types.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AnyFunction,\n DeepPartial,\n ExtractPath,\n ExtractPathByType,\n ExtractType,\n Primitive\n} from '@empathyco/x-utils';\nimport { MapperContext } from '../mappers/types';\n\n// TODO: EX-5830 - Enhance Schema type to support optional properties in the Source object\n/**\n * Template object to transform a source object to a target object.\n *\n * @remarks The source object must not have optional properties, as it could cause infinite\n * type instantiations.\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * price: {\n * max: number;\n * min: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * maxPrice: number;\n * minPrice: number;\n * img: string[]\n * }\n *\n * const schema: Schema<Source, Target> = {\n * identifier: 'id',\n * maxPrice: 'price.max',\n * minPrice: ({ price }) => Math.min(0, price.min),\n * img: 'images'\n * };\n * ```\n * @public\n */\nexport type Schema<Source = any, Target = any> = {\n [TargetKey in keyof Target]: SchemaTransformer<Source, Target, TargetKey>;\n};\n\n/**\n * A {@link Schema | schema} with extended functionality to: completely replace\n * the original schema, partially override it or create a new one.\n *\n * @param OriginalSchema - The {@link Schema | schema} that will be mutable.\n * @public\n */\nexport type MutableSchema<Source, Target> = Schema<Source, Target> & {\n /**\n * Replaces all usages of the original {@link Schema | schema} with the given one.\n *\n * @param newSchema - The {@link Schema | schema} to use instead of the original one.\n * @returns The new {@link Schema | schema} that will be used.\n */\n $replace<NewSource, NewTarget>(\n newSchema: Schema<NewSource, NewTarget>\n ): MutableSchema<NewSource, NewTarget>;\n /**\n * Merges the original {@link Schema | schema} with the given one.\n *\n * @param newSchema - The {@link Schema | schema} to use to merge with the original one.\n * @returns The {@link Schema | schema} returned by the merge.\n */\n // eslint-disable-next-line @typescript-eslint/ban-types\n $override<NewSource, NewTarget = {}>(\n newSchema: DeepPartial<Schema<Source & NewSource, Target>> &\n Schema<Source & NewSource, NewTarget>\n ): MutableSchema<Source & NewSource, Target & NewTarget>;\n /**\n * Creates a new {@link Schema | schema} using the original one as starting point.\n * The original {@link Schema | schema} will remain unchanged.\n *\n * @param newSchema - The {@link Schema | schema} to be used to extend the original one.\n * @returns The {@link Schema | schema} created.\n */\n // eslint-disable-next-line @typescript-eslint/ban-types\n $extends<NewSource, NewTarget = {}>(\n newSchema: DeepPartial<Schema<Source & NewSource, Target>> &\n Schema<Source & NewSource, NewTarget>\n ): MutableSchema<Source & NewSource, Target & NewTarget>;\n /**\n * Returns a string representing of the {@link Schema | schema}.\n *\n * @param includeInternalMethods - Flag to include in the string representation\n * the internal methods. Disabled by default.\n * @returns The string representation.\n */\n toString(includeInternalMethods?: boolean): string;\n};\n/**\n * The possible transformers to apply to the target key.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @param TargetKey - The target key to apply the transformation.\n * @public\n */\nexport type SchemaTransformer<Source, Target, TargetKey extends keyof Target> =\n | PathTransformer<Source, Target[TargetKey]>\n | FunctionTransformer<Source, Target[TargetKey]>\n | SubSchemaTransformer<Source, Target[TargetKey]>\n | Schema<Source, Exclude<Target[TargetKey], AnyFunction | Primitive>>;\n\n/**\n * A function with the source object and mapper context as parameters that returns the value of a\n * target's property.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * count: number;\n * }\n *\n * interface Target {\n * title: string;\n * hits: number;\n * }\n *\n * const subSchema: Schema<Source, Target> = {\n * title: 'id',\n * hits: 'count'\n * };\n *\n * const wrongSubSchema: Schema<Source, Target> = {\n * // @ts-expect-error\n * title: 'count', // This raises a TS error\n * hits: 'count'\n * };\n * ```\n * @public\n */\nexport type PathTransformer<Source, Target> = ExtractPathByType<Source, Target>;\n\n/**\n * A function with the source object and mapper context as parameters that returns the value of a\n * target's property.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * count: number;\n * }\n *\n * interface Target {\n * title: string;\n * hits: number;\n * }\n *\n * const subSchema: Schema<Source, Target> = {\n * title: source => source.id,\n * hits: (source, context) => context.requestParameters.query === 'example'\n * ? source.count\n * : 0\n * };\n * ```\n * @public\n */\nexport type FunctionTransformer<Source, Target> = (\n source: Source,\n context?: MapperContext\n) => Target;\n\n/**\n * An object containing a schema narrowing its source object based on the given path.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * facets: {\n * name: string;\n * count: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * filters: {\n * id: string;\n * numFound: number;\n * };\n * img: string[]\n * }\n *\n * const subSchema: SubSchemaTransformer<Source, Target['filters']> = {\n * $path: 'facets',\n * $subSchema: {\n * id: 'name',\n * numFound: 'count'\n * }\n * };\n * ```\n * @public\n */\nexport type SubSchemaTransformer<Source, Target> = {\n [Path in ExtractPath<Source>]: {\n $context?: MapperContext;\n $path: Path;\n $subSchema:\n | SubSchema<Source, Target, Path>\n | '$self'\n | ((source: Source) => SubSchema<Source, Target, Path>);\n };\n}[ExtractPath<Source>];\n\n/**\n * A {@link Schema | schema} that will be applied to an inner path of an object.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @param Path - The path where the schema will be applied.\n * @public\n */\nexport type SubSchema<Source, Target, Path extends ExtractPath<Source>> = ExtractType<\n Source,\n Path\n> extends (infer SourceArrayType)[]\n ? Target extends (infer TargetArrayType)[]\n ? Schema<SourceArrayType, TargetArrayType>\n : never\n : Target extends []\n ? never\n : Schema<ExtractType<Source, Path>, Target>;\n"]}
|
|
@@ -19,23 +19,25 @@ const mutableSchemasInternalMethods = ['$replace', '$override', '$extends', 'toS
|
|
|
19
19
|
function createMutableSchema(schema) {
|
|
20
20
|
return {
|
|
21
21
|
...schema,
|
|
22
|
-
$replace
|
|
23
|
-
|
|
22
|
+
$replace(newSchema) {
|
|
23
|
+
(0, x_utils_1.forEach)(this, key => {
|
|
24
24
|
if (isInternalMethod(key)) {
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
delete this[key];
|
|
28
28
|
});
|
|
29
29
|
Object.assign(this, newSchema);
|
|
30
|
+
/* We are replacing the schema with a completely new schema , so it makes sense that TS
|
|
31
|
+
complains that the old schema and the new one are not the same. */
|
|
30
32
|
return this;
|
|
31
33
|
},
|
|
32
|
-
$override
|
|
34
|
+
$override(newSchema) {
|
|
33
35
|
return (0, x_deep_merge_1.deepMerge)(this, newSchema);
|
|
34
36
|
},
|
|
35
|
-
$extends
|
|
37
|
+
$extends(newSchema) {
|
|
36
38
|
return (0, x_deep_merge_1.deepMerge)({}, this, newSchema);
|
|
37
39
|
},
|
|
38
|
-
toString
|
|
40
|
+
toString(includeInternalMethods = false) {
|
|
39
41
|
return serialize(this, !!includeInternalMethods);
|
|
40
42
|
}
|
|
41
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/schemas/utils.ts"],"names":[],"mappings":";;;AAAA,0DAAoD;AACpD,gDAAmE;AAGnE;;GAEG;AACH,MAAM,6BAA6B,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/schemas/utils.ts"],"names":[],"mappings":";;;AAAA,0DAAoD;AACpD,gDAAmE;AAGnE;;GAEG;AACH,MAAM,6BAA6B,GAAa,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAElG;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CACjC,MAA8B;IAE9B,OAAO;QACL,GAAG,MAAM;QACT,QAAQ,CAAC,SAAS;YAChB,IAAA,iBAAO,EAAC,IAAI,EAAE,GAAG,CAAC,EAAE;gBAClB,IAAI,gBAAgB,CAAC,GAAa,CAAC,EAAE;oBACnC,OAAO;iBACR;gBACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC/B;+EACmE;YACnE,OAAO,IAAW,CAAC;QACrB,CAAC;QACD,SAAS,CAAC,SAAS;YACjB,OAAO,IAAA,wBAAS,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,QAAQ,CAAC,SAAS;YAChB,OAAO,IAAA,wBAAS,EAAC,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QACD,QAAQ,CAAC,sBAAsB,GAAG,KAAK;YACrC,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,sBAAsB,CAAC,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC;AA3BD,kDA2BC;AAED;;;;;;;;;GASG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AAFD,4CAEC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAChB,IAA6B,EAC7B,sBAA+B,EAC/B,IAAI,GAAG,CAAC;IAER,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAA,iBAAO,EAAC,IAAI,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QAC3B,IAAI,IAAA,kBAAQ,EAAC,KAAK,CAAC,EAAE;YACnB,MAAM,IAAI,GAAG,WAAW,GAAG,GAAG,QAAQ,SAAS,CAC7C,KAAK,EACL,sBAAsB,EACtB,EAAE,IAAI,CACP,GAAG,WAAW,MAAM,CAAC;SACvB;aAAM,IAAI,CAAC,IAAA,oBAAU,EAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,sBAAsB,EAAE;YACjF,4EAA4E;YAC5E,MAAM,IAAI,GAAG,WAAW,GAAG,GAAG,KAAK,KAAK,KAAK,CAAC;SAC/C;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { deepMerge } from '@empathyco/x-deep-merge';\nimport { forEach, isFunction, isObject } from '@empathyco/x-utils';\nimport { MutableSchema, Schema } from './types';\n\n/**\n * Collection of internal method names for {@link MutableSchema | mutable schemas}.\n */\nconst mutableSchemasInternalMethods: string[] = ['$replace', '$override', '$extends', 'toString'];\n\n/**\n * Creates a {@link MutableSchema | mutable schema } version of a given {@link Schema | schema}.\n *\n * @param schema - The {@link Schema | schema} to make mutable.\n *\n * @returns A {@link MutableSchema | mutable schema} version of the given {@link Schema | schema}.\n *\n * @public\n */\nexport function createMutableSchema<Source, Target>(\n schema: Schema<Source, Target>\n): MutableSchema<Source, Target> {\n return {\n ...schema,\n $replace(newSchema) {\n forEach(this, key => {\n if (isInternalMethod(key as string)) {\n return;\n }\n delete this[key];\n });\n Object.assign(this, newSchema);\n /* We are replacing the schema with a completely new schema , so it makes sense that TS\n complains that the old schema and the new one are not the same. */\n return this as any;\n },\n $override(newSchema) {\n return deepMerge(this, newSchema);\n },\n $extends(newSchema) {\n return deepMerge({}, this, newSchema);\n },\n toString(includeInternalMethods = false) {\n return serialize(this, !!includeInternalMethods);\n }\n };\n}\n\n/**\n * Checks if the given key is a {@link MutableSchema | mutableSchema} method.\n *\n * @param name - The key to check.\n *\n * @returns True if it is a {@link MutableSchema | mutableSchema} method,\n * false otherwise.\n *\n * @public\n */\nexport function isInternalMethod(name: string): boolean {\n return mutableSchemasInternalMethods.includes(name);\n}\n\n/**\n * Returns a string representing of the given object.\n *\n * @param data - The object to get the string representation from.\n * @param includeInternalMethods - Flag to include in the string representation\n * the internal methods. Disabled by default.\n * @param deep - The level of indentation.\n * @returns The string representation.\n */\nfunction serialize(\n data: Record<string, unknown>,\n includeInternalMethods: boolean,\n deep = 0\n): string {\n const indentation = ' '.repeat(deep);\n let output = '';\n forEach(data, (key, value) => {\n if (isObject(value)) {\n output += `${indentation}${key}: {\\n${serialize(\n value,\n includeInternalMethods,\n ++deep\n )}${indentation}},\\n`;\n } else if (!isFunction(value) || !isInternalMethod(key) || includeInternalMethods) {\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n output += `${indentation}${key}: ${value},\\n`;\n }\n });\n return output;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-mapper.factory.js","sourceRoot":"","sources":["../../../src/mappers/schema-mapper.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAGL,oBAAoB,EACpB,OAAO,EACP,UAAU,EACV,QAAQ,EACR,MAAM,EACN,MAAM,EACP,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzE;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,
|
|
1
|
+
{"version":3,"file":"schema-mapper.factory.js","sourceRoot":"","sources":["../../../src/mappers/schema-mapper.factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { deepMerge } from '@empathyco/x-deep-merge';\nimport {\n Dictionary,\n ExtractPath,\n getSafePropertyChain,\n isArray,\n isFunction,\n isObject,\n isPath,\n reduce\n} from '@empathyco/x-utils';\nimport { MutableSchema, Schema, SubSchemaTransformer } from '../schemas/types';\nimport { createMutableSchema, isInternalMethod } from '../schemas/utils';\nimport { Mapper, MapperContext } from './types';\n\n/**\n * The 'schemaMapperFactory' function creates a {@link Mapper | mapper function} for a given\n * {@link Schema | schema}.\n *\n * @param schema - The {@link Schema | schema} to apply in the {@link Mapper | mapper function}.\n * @returns A {@link Mapper | mapper function} that applies the given {@link Schema | schema}.\n * @public\n */\nexport function schemaMapperFactory<Source, Target>(\n schema: Schema<Source, Target> | MutableSchema<Source, Target>\n): Mapper<Source, Target> {\n return function mapper(source: Source, context: MapperContext): Target {\n return mapSchema(source, schema, context);\n };\n}\n\n/**\n * The `mapSchema()` function creates a new object populated with the transformations defined by a\n * {@link Schema} applied to a source object.\n *\n * @param source - The object to apply the transformations to.\n * @param schema - The object that defines the transformations to apply.\n * @param context - The {@link MapperContext | mapper context} to feed the transformations with.\n * @returns A new object with each element being the result of the applied transformation.\n * @internal\n */\nfunction mapSchema<Source, Target>(\n source: Source,\n schema: Schema<Source, Target>,\n context: MapperContext\n): Target {\n if (!source) {\n //eslint-disable-next-line no-console\n console.warn('This schema cannot be applied', createMutableSchema(schema));\n return undefined as any;\n }\n return reduce(\n schema,\n (target, key, transformer) => {\n type TargetKey = Target[keyof Target];\n if (typeof transformer === 'string' && isPath(source, transformer)) {\n target[key] = getSafePropertyChain(source, transformer) as TargetKey;\n } else if (isFunction(transformer) && !isInternalMethod(transformer.name)) {\n target[key] = transformer(source, context);\n } else if (isObject(transformer)) {\n const value =\n '$subSchema' in transformer\n ? (applySubSchemaTransformer<Source, TargetKey>(\n source,\n transformer as SubSchemaTransformer<Source, TargetKey>,\n context,\n schema as unknown as Schema<Source, TargetKey>\n ) as TargetKey)\n : mapSchema<Source, TargetKey>(source, transformer, context);\n\n if (value) {\n target[key] = value;\n }\n }\n return target;\n },\n {} as Target\n );\n}\n\n/**\n * The `applySubSchemaTransformer()` function executes a `mapSchema()` function applying the defined\n * {@link SubSchemaTransformer.$subSchema}.\n *\n * @param source - The object to feed the schema.\n * @param subSchemaTransformer - The {@link SubSchemaTransformer} object with a $path, $subSchema\n * and $context options.\n * @param subSchemaTransformer.$path\n * @param subSchemaTransformer.$subSchema\n * @param rawContext - The {@link MapperContext | mapper context} to feed the mapSchema function.\n * @param subSchemaTransformer.$context\n * @param schema - The {@link Schema} to apply.\n * @returns The result of calling `mapSchema()` with the source, schema and context arguments.\n * @internal\n */\nfunction applySubSchemaTransformer<Source, Target>(\n source: Source,\n { $subSchema, $path, $context }: SubSchemaTransformer<Source, Target>,\n rawContext: MapperContext,\n schema: Schema<Source, Target>\n): Target | Target[] | undefined {\n const subSource = getSafePropertyChain(source, $path);\n\n if (!subSource) {\n return;\n }\n\n const extendedContext: Dictionary = {};\n if ($context) {\n Object.entries($context).forEach(([key, value]) => {\n if (['requestParameters', 'endpoint', 'mappedValue'].includes(key)) {\n return;\n }\n extendedContext[key] = isFunction(value)\n ? value(source)\n : getSafePropertyChain(source, value as ExtractPath<typeof source>);\n });\n }\n\n const context = deepMerge({}, rawContext, $context, extendedContext);\n let subSchema: typeof $subSchema | typeof schema;\n if ($subSchema === '$self') {\n subSchema = schema;\n } else if (isFunction($subSchema)) {\n subSchema = $subSchema(source);\n } else {\n subSchema = $subSchema;\n }\n return isArray(subSource)\n ? subSource.map(item => mapSchema(item, subSchema, context) as Target)\n : mapSchema<typeof subSource, Target>(\n subSource,\n subSchema as Schema<typeof subSource, Target>,\n context\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/schemas/types.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AnyFunction,\n ExtractPath,\n ExtractPathByType,\n ExtractType,\n Primitive\n} from '@empathyco/x-utils';\nimport { MapperContext } from '../mappers/types';\n\n// TODO: EX-5830 - Enhance Schema type to support optional properties in the Source object\n/**\n * Template object to transform a source object to a target object.\n *\n * @remarks The source object must not have optional properties, as it could cause infinite\n * type instantiations.\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * price: {\n * max: number;\n * min: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * maxPrice: number;\n * minPrice: number;\n * img: string[]\n * }\n *\n * const schema: Schema<Source, Target> = {\n * identifier: 'id',\n * maxPrice: 'price.max',\n * minPrice: ({ price }) => Math.min(0, price.min),\n * img: 'images'\n * };\n * ```\n * @public\n */\nexport type Schema<Source = any, Target = any> = {\n [TargetKey in keyof Target]: SchemaTransformer<Source, Target, TargetKey>;\n};\n\n/**\n * A {@link Schema | schema} with extended functionality to: completely replace\n * the original schema, partially override it or create a new one.\n *\n * @param OriginalSchema - The {@link Schema | schema} that will be mutable.\n * @public\n */\nexport type MutableSchema<
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/schemas/types.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AnyFunction,\n DeepPartial,\n ExtractPath,\n ExtractPathByType,\n ExtractType,\n Primitive\n} from '@empathyco/x-utils';\nimport { MapperContext } from '../mappers/types';\n\n// TODO: EX-5830 - Enhance Schema type to support optional properties in the Source object\n/**\n * Template object to transform a source object to a target object.\n *\n * @remarks The source object must not have optional properties, as it could cause infinite\n * type instantiations.\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * price: {\n * max: number;\n * min: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * maxPrice: number;\n * minPrice: number;\n * img: string[]\n * }\n *\n * const schema: Schema<Source, Target> = {\n * identifier: 'id',\n * maxPrice: 'price.max',\n * minPrice: ({ price }) => Math.min(0, price.min),\n * img: 'images'\n * };\n * ```\n * @public\n */\nexport type Schema<Source = any, Target = any> = {\n [TargetKey in keyof Target]: SchemaTransformer<Source, Target, TargetKey>;\n};\n\n/**\n * A {@link Schema | schema} with extended functionality to: completely replace\n * the original schema, partially override it or create a new one.\n *\n * @param OriginalSchema - The {@link Schema | schema} that will be mutable.\n * @public\n */\nexport type MutableSchema<Source, Target> = Schema<Source, Target> & {\n /**\n * Replaces all usages of the original {@link Schema | schema} with the given one.\n *\n * @param newSchema - The {@link Schema | schema} to use instead of the original one.\n * @returns The new {@link Schema | schema} that will be used.\n */\n $replace<NewSource, NewTarget>(\n newSchema: Schema<NewSource, NewTarget>\n ): MutableSchema<NewSource, NewTarget>;\n /**\n * Merges the original {@link Schema | schema} with the given one.\n *\n * @param newSchema - The {@link Schema | schema} to use to merge with the original one.\n * @returns The {@link Schema | schema} returned by the merge.\n */\n // eslint-disable-next-line @typescript-eslint/ban-types\n $override<NewSource, NewTarget = {}>(\n newSchema: DeepPartial<Schema<Source & NewSource, Target>> &\n Schema<Source & NewSource, NewTarget>\n ): MutableSchema<Source & NewSource, Target & NewTarget>;\n /**\n * Creates a new {@link Schema | schema} using the original one as starting point.\n * The original {@link Schema | schema} will remain unchanged.\n *\n * @param newSchema - The {@link Schema | schema} to be used to extend the original one.\n * @returns The {@link Schema | schema} created.\n */\n // eslint-disable-next-line @typescript-eslint/ban-types\n $extends<NewSource, NewTarget = {}>(\n newSchema: DeepPartial<Schema<Source & NewSource, Target>> &\n Schema<Source & NewSource, NewTarget>\n ): MutableSchema<Source & NewSource, Target & NewTarget>;\n /**\n * Returns a string representing of the {@link Schema | schema}.\n *\n * @param includeInternalMethods - Flag to include in the string representation\n * the internal methods. Disabled by default.\n * @returns The string representation.\n */\n toString(includeInternalMethods?: boolean): string;\n};\n/**\n * The possible transformers to apply to the target key.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @param TargetKey - The target key to apply the transformation.\n * @public\n */\nexport type SchemaTransformer<Source, Target, TargetKey extends keyof Target> =\n | PathTransformer<Source, Target[TargetKey]>\n | FunctionTransformer<Source, Target[TargetKey]>\n | SubSchemaTransformer<Source, Target[TargetKey]>\n | Schema<Source, Exclude<Target[TargetKey], AnyFunction | Primitive>>;\n\n/**\n * A function with the source object and mapper context as parameters that returns the value of a\n * target's property.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * count: number;\n * }\n *\n * interface Target {\n * title: string;\n * hits: number;\n * }\n *\n * const subSchema: Schema<Source, Target> = {\n * title: 'id',\n * hits: 'count'\n * };\n *\n * const wrongSubSchema: Schema<Source, Target> = {\n * // @ts-expect-error\n * title: 'count', // This raises a TS error\n * hits: 'count'\n * };\n * ```\n * @public\n */\nexport type PathTransformer<Source, Target> = ExtractPathByType<Source, Target>;\n\n/**\n * A function with the source object and mapper context as parameters that returns the value of a\n * target's property.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * count: number;\n * }\n *\n * interface Target {\n * title: string;\n * hits: number;\n * }\n *\n * const subSchema: Schema<Source, Target> = {\n * title: source => source.id,\n * hits: (source, context) => context.requestParameters.query === 'example'\n * ? source.count\n * : 0\n * };\n * ```\n * @public\n */\nexport type FunctionTransformer<Source, Target> = (\n source: Source,\n context?: MapperContext\n) => Target;\n\n/**\n * An object containing a schema narrowing its source object based on the given path.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @example\n * ```typescript\n * interface Source {\n * id: string;\n * facets: {\n * name: string;\n * count: number;\n * };\n * images: string[];\n * }\n *\n * interface Target {\n * identifier: string;\n * filters: {\n * id: string;\n * numFound: number;\n * };\n * img: string[]\n * }\n *\n * const subSchema: SubSchemaTransformer<Source, Target['filters']> = {\n * $path: 'facets',\n * $subSchema: {\n * id: 'name',\n * numFound: 'count'\n * }\n * };\n * ```\n * @public\n */\nexport type SubSchemaTransformer<Source, Target> = {\n [Path in ExtractPath<Source>]: {\n $context?: MapperContext;\n $path: Path;\n $subSchema:\n | SubSchema<Source, Target, Path>\n | '$self'\n | ((source: Source) => SubSchema<Source, Target, Path>);\n };\n}[ExtractPath<Source>];\n\n/**\n * A {@link Schema | schema} that will be applied to an inner path of an object.\n *\n * @param Source - The source object.\n * @param Target - The target object.\n * @param Path - The path where the schema will be applied.\n * @public\n */\nexport type SubSchema<Source, Target, Path extends ExtractPath<Source>> = ExtractType<\n Source,\n Path\n> extends (infer SourceArrayType)[]\n ? Target extends (infer TargetArrayType)[]\n ? Schema<SourceArrayType, TargetArrayType>\n : never\n : Target extends []\n ? never\n : Schema<ExtractType<Source, Path>, Target>;\n"]}
|
|
@@ -16,23 +16,25 @@ const mutableSchemasInternalMethods = ['$replace', '$override', '$extends', 'toS
|
|
|
16
16
|
export function createMutableSchema(schema) {
|
|
17
17
|
return {
|
|
18
18
|
...schema,
|
|
19
|
-
$replace
|
|
20
|
-
|
|
19
|
+
$replace(newSchema) {
|
|
20
|
+
forEach(this, key => {
|
|
21
21
|
if (isInternalMethod(key)) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
delete this[key];
|
|
25
25
|
});
|
|
26
26
|
Object.assign(this, newSchema);
|
|
27
|
+
/* We are replacing the schema with a completely new schema , so it makes sense that TS
|
|
28
|
+
complains that the old schema and the new one are not the same. */
|
|
27
29
|
return this;
|
|
28
30
|
},
|
|
29
|
-
$override
|
|
31
|
+
$override(newSchema) {
|
|
30
32
|
return deepMerge(this, newSchema);
|
|
31
33
|
},
|
|
32
|
-
$extends
|
|
34
|
+
$extends(newSchema) {
|
|
33
35
|
return deepMerge({}, this, newSchema);
|
|
34
36
|
},
|
|
35
|
-
toString
|
|
37
|
+
toString(includeInternalMethods = false) {
|
|
36
38
|
return serialize(this, !!includeInternalMethods);
|
|
37
39
|
}
|
|
38
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/schemas/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnE;;GAEG;AACH,MAAM,6BAA6B,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/schemas/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnE;;GAEG;AACH,MAAM,6BAA6B,GAAa,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAElG;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA8B;IAE9B,OAAO;QACL,GAAG,MAAM;QACT,QAAQ,CAAC,SAAS;YAChB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;gBAClB,IAAI,gBAAgB,CAAC,GAAa,CAAC,EAAE;oBACnC,OAAO;iBACR;gBACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC/B;+EACmE;YACnE,OAAO,IAAW,CAAC;QACrB,CAAC;QACD,SAAS,CAAC,SAAS;YACjB,OAAO,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,QAAQ,CAAC,SAAS;YAChB,OAAO,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QACD,QAAQ,CAAC,sBAAsB,GAAG,KAAK;YACrC,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,sBAAsB,CAAC,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,6BAA6B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAChB,IAA6B,EAC7B,sBAA+B,EAC/B,IAAI,GAAG,CAAC;IAER,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QAC3B,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE;YACnB,MAAM,IAAI,GAAG,WAAW,GAAG,GAAG,QAAQ,SAAS,CAC7C,KAAK,EACL,sBAAsB,EACtB,EAAE,IAAI,CACP,GAAG,WAAW,MAAM,CAAC;SACvB;aAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,sBAAsB,EAAE;YACjF,4EAA4E;YAC5E,MAAM,IAAI,GAAG,WAAW,GAAG,GAAG,KAAK,KAAK,KAAK,CAAC;SAC/C;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { deepMerge } from '@empathyco/x-deep-merge';\nimport { forEach, isFunction, isObject } from '@empathyco/x-utils';\nimport { MutableSchema, Schema } from './types';\n\n/**\n * Collection of internal method names for {@link MutableSchema | mutable schemas}.\n */\nconst mutableSchemasInternalMethods: string[] = ['$replace', '$override', '$extends', 'toString'];\n\n/**\n * Creates a {@link MutableSchema | mutable schema } version of a given {@link Schema | schema}.\n *\n * @param schema - The {@link Schema | schema} to make mutable.\n *\n * @returns A {@link MutableSchema | mutable schema} version of the given {@link Schema | schema}.\n *\n * @public\n */\nexport function createMutableSchema<Source, Target>(\n schema: Schema<Source, Target>\n): MutableSchema<Source, Target> {\n return {\n ...schema,\n $replace(newSchema) {\n forEach(this, key => {\n if (isInternalMethod(key as string)) {\n return;\n }\n delete this[key];\n });\n Object.assign(this, newSchema);\n /* We are replacing the schema with a completely new schema , so it makes sense that TS\n complains that the old schema and the new one are not the same. */\n return this as any;\n },\n $override(newSchema) {\n return deepMerge(this, newSchema);\n },\n $extends(newSchema) {\n return deepMerge({}, this, newSchema);\n },\n toString(includeInternalMethods = false) {\n return serialize(this, !!includeInternalMethods);\n }\n };\n}\n\n/**\n * Checks if the given key is a {@link MutableSchema | mutableSchema} method.\n *\n * @param name - The key to check.\n *\n * @returns True if it is a {@link MutableSchema | mutableSchema} method,\n * false otherwise.\n *\n * @public\n */\nexport function isInternalMethod(name: string): boolean {\n return mutableSchemasInternalMethods.includes(name);\n}\n\n/**\n * Returns a string representing of the given object.\n *\n * @param data - The object to get the string representation from.\n * @param includeInternalMethods - Flag to include in the string representation\n * the internal methods. Disabled by default.\n * @param deep - The level of indentation.\n * @returns The string representation.\n */\nfunction serialize(\n data: Record<string, unknown>,\n includeInternalMethods: boolean,\n deep = 0\n): string {\n const indentation = ' '.repeat(deep);\n let output = '';\n forEach(data, (key, value) => {\n if (isObject(value)) {\n output += `${indentation}${key}: {\\n${serialize(\n value,\n includeInternalMethods,\n ++deep\n )}${indentation}},\\n`;\n } else if (!isFunction(value) || !isInternalMethod(key) || includeInternalMethods) {\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n output += `${indentation}${key}: ${value},\\n`;\n }\n });\n return output;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Schema } from '../schemas/types';
|
|
1
|
+
import { MutableSchema, Schema } from '../schemas/types';
|
|
2
2
|
import { Mapper } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* The 'schemaMapperFactory' function creates a {@link Mapper | mapper function} for a given
|
|
@@ -8,4 +8,4 @@ import { Mapper } from './types';
|
|
|
8
8
|
* @returns A {@link Mapper | mapper function} that applies the given {@link Schema | schema}.
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
-
export declare function schemaMapperFactory<Source, Target>(schema: Schema<Source, Target>): Mapper<Source, Target>;
|
|
11
|
+
export declare function schemaMapperFactory<Source, Target>(schema: Schema<Source, Target> | MutableSchema<Source, Target>): Mapper<Source, Target>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyFunction, ExtractPath, ExtractPathByType, ExtractType, Primitive } from '@empathyco/x-utils';
|
|
1
|
+
import { AnyFunction, DeepPartial, ExtractPath, ExtractPathByType, ExtractType, Primitive } from '@empathyco/x-utils';
|
|
2
2
|
import { MapperContext } from '../mappers/types';
|
|
3
3
|
/**
|
|
4
4
|
* Template object to transform a source object to a target object.
|
|
@@ -44,21 +44,21 @@ export declare type Schema<Source = any, Target = any> = {
|
|
|
44
44
|
* @param OriginalSchema - The {@link Schema | schema} that will be mutable.
|
|
45
45
|
* @public
|
|
46
46
|
*/
|
|
47
|
-
export declare type MutableSchema<
|
|
47
|
+
export declare type MutableSchema<Source, Target> = Schema<Source, Target> & {
|
|
48
48
|
/**
|
|
49
49
|
* Replaces all usages of the original {@link Schema | schema} with the given one.
|
|
50
50
|
*
|
|
51
51
|
* @param newSchema - The {@link Schema | schema} to use instead of the original one.
|
|
52
52
|
* @returns The new {@link Schema | schema} that will be used.
|
|
53
53
|
*/
|
|
54
|
-
$replace
|
|
54
|
+
$replace<NewSource, NewTarget>(newSchema: Schema<NewSource, NewTarget>): MutableSchema<NewSource, NewTarget>;
|
|
55
55
|
/**
|
|
56
56
|
* Merges the original {@link Schema | schema} with the given one.
|
|
57
57
|
*
|
|
58
58
|
* @param newSchema - The {@link Schema | schema} to use to merge with the original one.
|
|
59
59
|
* @returns The {@link Schema | schema} returned by the merge.
|
|
60
60
|
*/
|
|
61
|
-
$override
|
|
61
|
+
$override<NewSource, NewTarget = {}>(newSchema: DeepPartial<Schema<Source & NewSource, Target>> & Schema<Source & NewSource, NewTarget>): MutableSchema<Source & NewSource, Target & NewTarget>;
|
|
62
62
|
/**
|
|
63
63
|
* Creates a new {@link Schema | schema} using the original one as starting point.
|
|
64
64
|
* The original {@link Schema | schema} will remain unchanged.
|
|
@@ -66,7 +66,7 @@ export declare type MutableSchema<OriginalSchema extends Schema> = OriginalSchem
|
|
|
66
66
|
* @param newSchema - The {@link Schema | schema} to be used to extend the original one.
|
|
67
67
|
* @returns The {@link Schema | schema} created.
|
|
68
68
|
*/
|
|
69
|
-
$extends
|
|
69
|
+
$extends<NewSource, NewTarget = {}>(newSchema: DeepPartial<Schema<Source & NewSource, Target>> & Schema<Source & NewSource, NewTarget>): MutableSchema<Source & NewSource, Target & NewTarget>;
|
|
70
70
|
/**
|
|
71
71
|
* Returns a string representing of the {@link Schema | schema}.
|
|
72
72
|
*
|
|
@@ -74,7 +74,7 @@ export declare type MutableSchema<OriginalSchema extends Schema> = OriginalSchem
|
|
|
74
74
|
* the internal methods. Disabled by default.
|
|
75
75
|
* @returns The string representation.
|
|
76
76
|
*/
|
|
77
|
-
toString
|
|
77
|
+
toString(includeInternalMethods?: boolean): string;
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
80
|
* The possible transformers to apply to the target key.
|
|
@@ -8,7 +8,7 @@ import { MutableSchema, Schema } from './types';
|
|
|
8
8
|
*
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
-
export declare function createMutableSchema<
|
|
11
|
+
export declare function createMutableSchema<Source, Target>(schema: Schema<Source, Target>): MutableSchema<Source, Target>;
|
|
12
12
|
/**
|
|
13
13
|
* Checks if the given key is a {@link MutableSchema | mutableSchema} method.
|
|
14
14
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empathyco/x-adapter",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.16",
|
|
4
4
|
"description": "A utils library to create a client for any API",
|
|
5
5
|
"author": "Empathy Systems Corporation S.L.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "c1a486c3294c41f8ba49be320c3f71e2368eb59c"
|
|
54
54
|
}
|