1 | const util = require('util');
|
2 | const validate = require('jsonschema').validate;
|
3 | const commonUtils = require('./common-utils');
|
4 | const Cache = require('./cache.js');
|
5 | const Batch = require('./batch');
|
6 | const SriClientError = require('./sri-client-error');
|
7 |
|
8 | const mergeObjRecursive = (obj, patch) => {
|
9 | const ret = obj ?{...obj} : {};
|
10 | if(patch) {
|
11 | Object.keys(patch).forEach(key => {
|
12 | if (typeof patch[key] === 'object') {
|
13 | Object.assign(ret, {[key]: mergeObjRecursive(obj[key], patch[key])});
|
14 | } else {
|
15 | Object.assign(ret, {[key]: patch[key]});
|
16 | }
|
17 | });
|
18 | }
|
19 |
|
20 | return ret;
|
21 | };
|
22 |
|
23 | module.exports = class SriClient {
|
24 | constructor(config = {}) {
|
25 | this.configuration = config;
|
26 | this.groupBy = config.groupBy || 100;
|
27 | this.cache = new Cache(config.caching, this);
|
28 | }
|
29 |
|
30 | createBatch() {
|
31 | return new Batch(this);
|
32 | }
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 | setConfiguration(configuration) {
|
39 | this.configuration = configuration;
|
40 | }
|
41 |
|
42 | patchConfiguration(patch) {
|
43 | this.setConfiguration(mergeObjRecursive(this.configuration, patch));
|
44 | }
|
45 |
|
46 | getBaseUrl(options = {}) {
|
47 | const baseUrl = options.baseUrl || this.configuration.baseUrl;
|
48 | if (!baseUrl) {
|
49 | throw Error({message: "There is no baseUrl configured. The specification for the node-sri-client module can be found at https://bitbucket.org/vskovzw/kathondvla-utils"});
|
50 | }
|
51 | return baseUrl;
|
52 | }
|
53 |
|
54 |
|
55 | getRaw() {}
|
56 |
|
57 | get(href, params, options = {}) {
|
58 | return this.wrapGet(href, params, options, true);
|
59 | }
|
60 |
|
61 | async wrapGet(href, params, options, isSingleResource) {
|
62 | try {
|
63 | let result;
|
64 | if(options.inBatch && !this.cache.has(href, params, options.caching)) {
|
65 | const batch = [{
|
66 | href: commonUtils.parametersToString(href, params),
|
67 | verb: 'GET'
|
68 | }];
|
69 | const batchResp = await this.wrapSendPayload(options.inBatch, batch, options, options.batchMethod && options.batchMethod === 'POST' ? 'POST' : 'PUT');
|
70 |
|
71 | if(batchResp[0].status < 300) {
|
72 | result = batchResp[0].body;
|
73 | } else {
|
74 | throw batchResp[0].body;
|
75 | }
|
76 | } else {
|
77 | result = await this.cache.get(href, params, options, !isSingleResource);
|
78 | if(isSingleResource && result.results) {
|
79 | throw Error('Do not use the get method to ask for lists. Use getList or getAll instead. You can also use getRaw but this method does not use caching, client side expansion and inclusion.');
|
80 | }
|
81 | }
|
82 | if(options.expand) {
|
83 | await this.expandJson(result, options.expand, options.caching, options.logging ? options.logging.replace('get', '').replace('expand', 'expand,get') : undefined);
|
84 | }
|
85 | if(options.include) {
|
86 | await this.includeJson(result, options.include, options.caching, options.logging ? options.logging.replace('get', '').replace('expand', 'expand,get') : undefined);
|
87 | }
|
88 | return result;
|
89 | } catch(error) {
|
90 | if (error instanceof SriClientError && options && options.retry && options.retry.retries !== 0 && !(error.status && error.status < 500)) {
|
91 | let wait = options.retry.wait;
|
92 | if (!wait) {
|
93 | wait = options.retry.initialWait ? options.retry.initialWait : 500;
|
94 | } else {
|
95 | wait = wait * (options.retry.factor ? options.retry.factor : 2);
|
96 | }
|
97 | console.log(`[sri-client->RETRY:${wait}] GET to ${commonUtils.parametersToString(href, params)} failed! We will try again in ${wait} miliseconds...`);
|
98 | const newOptions = { ...options, retry: {
|
99 | ...options.retry,
|
100 | retries: options.retry.retries - 1,
|
101 | wait
|
102 | } };
|
103 | await commonUtils.sleep(wait);
|
104 | return this.wrapGet(href, params, newOptions, isSingleResource);
|
105 | } else {
|
106 | throw error;
|
107 | }
|
108 | }
|
109 | }
|
110 |
|
111 | async getAllFromResult(data, options) {
|
112 | var results = data.results;
|
113 | if (data.$$meta.next) {
|
114 | const nextResult = await this.wrapGet(data.$$meta.next, undefined, options);
|
115 | const nextResults = await this.getAllFromResult(nextResult, options);
|
116 | results = results.concat(nextResults);
|
117 | }
|
118 | return results;
|
119 | }
|
120 |
|
121 | async getAll(href, params = {}, options = {}) {
|
122 |
|
123 |
|
124 | const expand = options.expand;
|
125 | options.expand = undefined;
|
126 | if(!params.limit && params.limit !== null) {
|
127 | params.limit = 500;
|
128 | }
|
129 | const result = await this.wrapGet(href, params, options);
|
130 | if(!result || !result.$$meta) {
|
131 | console.log('no results for ' + href);
|
132 | }
|
133 | var allResults = await this.getAllFromResult(result, options);
|
134 | if (!options.raw && !(params && params.expand && params.expand === 'NONE')) {
|
135 | allResults = allResults.map(function (item) {
|
136 | return item.$$expanded;
|
137 | });
|
138 | }
|
139 | if(result.$$meta) {
|
140 | allResults.count = result.$$meta.count;
|
141 | }
|
142 | if(expand) {
|
143 | await this.expandJson(allResults, expand, options.caching, options.logging ? options.logging.replace('get', '').replace('expand', 'expand,get') : undefined);
|
144 | }
|
145 | return allResults;
|
146 | }
|
147 |
|
148 | async getList(href, params, options = {}) {
|
149 | const result = await this.wrapGet(href, params, options);
|
150 | let results = result.results;
|
151 | if (!options.raw && !(params && params.expand && params.expand === 'NONE')) {
|
152 | results = results.map(function (item) {
|
153 | return item.$$expanded;
|
154 | });
|
155 | }
|
156 | if(result.$$meta) {
|
157 | results.count = result.$$meta.count;
|
158 | results.next = result.$$meta.next;
|
159 | }
|
160 | return results;
|
161 | }
|
162 |
|
163 | async getAllHrefsWithoutBatch(baseHref, parameterName, hrefs, params, options) {
|
164 | if (hrefs.length === 0) {
|
165 | return [];
|
166 | }
|
167 |
|
168 | const map = {};
|
169 | let allResults = [];
|
170 | if (options.inBatch) {
|
171 | const thisParams = Object.assign({}, params);
|
172 | const thisOptions = Object.assign({}, options);
|
173 | thisParams[parameterName] = hrefs.join(',');
|
174 | allResults = await this.getAll(baseHref, thisParams, thisOptions);
|
175 | } else {
|
176 | const groupBy = options.groupBy || Math.floor((6900 - commonUtils.parametersToString(baseHref, params).length - parameterName.length - 1) / (encodeURIComponent(hrefs[0]).length + 3));
|
177 | var total = 0;
|
178 | while(total < hrefs.length) {
|
179 |
|
180 | let parameterValue = '';
|
181 | for(var i = 0; i < groupBy && total < hrefs.length; i++) {
|
182 | map[hrefs[i]] = null;
|
183 | parameterValue += (i === 0 ? '' : ',')+hrefs[total];
|
184 | total++;
|
185 | }
|
186 | const thisParams = Object.assign({}, params);
|
187 | const thisOptions = Object.assign({}, options);
|
188 | thisParams[parameterName] = parameterValue;
|
189 | const results = await this.getAll(baseHref, thisParams, thisOptions);
|
190 | allResults = allResults.concat(results);
|
191 | }
|
192 | }
|
193 |
|
194 | if(options.raw) {
|
195 | throw new Error('You can not get a raw result for getAllHrefs or getAllReferencesTo');
|
196 | } else if(options.asMap) {
|
197 | allResults.forEach(function (item) {
|
198 | map[item.$$meta.permalink] = item;
|
199 | });
|
200 | return map;
|
201 | } else {
|
202 | return allResults;
|
203 | }
|
204 | }
|
205 |
|
206 | getAllReferencesTo(baseHref, params = {}, parameterName, values, options = {}) {
|
207 | if(!params.limit && params.limit !== null) {
|
208 | params.limit = 500;
|
209 | }
|
210 | if (options.inBatch) {
|
211 |
|
212 | }
|
213 | return this.getAllHrefsWithoutBatch(baseHref, parameterName, values, params, options);
|
214 | }
|
215 |
|
216 | async getAllHrefs(hrefs, batchHref, params = {}, options = {}) {
|
217 | if(hrefs.length === 0) {
|
218 | return [];
|
219 | }
|
220 | if(batchHref && typeof batchHref !== 'string' && !(batchHref instanceof String)) {
|
221 | options = params;
|
222 | params = batchHref;
|
223 | batchHref = null;
|
224 | }
|
225 | params.limit = 500;
|
226 | const baseHref = commonUtils.getPathFromPermalink(hrefs[0]);
|
227 | if(!batchHref) {
|
228 | return this.getAllHrefsWithoutBatch(baseHref, 'hrefs', hrefs, params, options);
|
229 | }
|
230 | const batch = [];
|
231 | const map = {};
|
232 | let remainingHrefs = [].concat(hrefs);
|
233 |
|
234 | while(remainingHrefs.length) {
|
235 | var query = commonUtils.parametersToString(baseHref, params) + '&hrefs=';
|
236 |
|
237 | const thisBatchHrefs = remainingHrefs.slice(0, params.limit);
|
238 | remainingHrefs = remainingHrefs.slice(params.limit, remainingHrefs.length);
|
239 |
|
240 | for (let href in thisBatchHrefs) {
|
241 | map[href] = null;
|
242 | }
|
243 | query += thisBatchHrefs.join(',');
|
244 |
|
245 | var part = {
|
246 | verb: "GET",
|
247 | href: query
|
248 | };
|
249 | batch.push(part);
|
250 | }
|
251 | const batchResp = await this.sendPayload(batchHref, batch, options, batchHref === '/persons/batch' ? 'PUT' : 'POST');
|
252 | if(options.expand) {
|
253 | await this.expandJson(batchResp, options.expand, options.caching, options.logging ? options.logging.replace('get', '').replace('expand', 'expand,get') : undefined);
|
254 | }
|
255 | if(options.include) {
|
256 | await this.includeJson(batchResp, options.include, options.logging ? options.logging.replace('get', '').replace('expand', 'expand,get') : undefined);
|
257 | }
|
258 | return new Promise(function(resolve, reject) {
|
259 | var ret = [];
|
260 | for(var i = 0; i < batchResp.length; i++) {
|
261 | if(batchResp[i].status === 200) {
|
262 | var results = batchResp[i].body.results;
|
263 | if(options.asMap) {
|
264 | results.forEach(function (item) {
|
265 | map[item.href] = item.$$expanded;
|
266 | });
|
267 | } else {
|
268 | ret = ret.concat(results);
|
269 | }
|
270 | } else {
|
271 | reject(batchResp);
|
272 | }
|
273 | }
|
274 | if(options.asMap) {
|
275 | resolve(map);
|
276 | } else {
|
277 | resolve(ret);
|
278 | }
|
279 | });
|
280 | }
|
281 |
|
282 |
|
283 | sendPayload() {}
|
284 |
|
285 | async wrapSendPayload(href, payload, options = {}, method) {
|
286 | try {
|
287 | const originallyFullResponse = options.fullResponse;
|
288 | if (options.keepBatchAlive) {
|
289 | if (!href.match(/batch$/)) {
|
290 | throw new Error({ message: 'You can only add the streaming option for batch requests' });
|
291 | }
|
292 | const batchResp = await this.sendPayload(href + '_streaming', payload, { ...options, fullResponse: true }, method);
|
293 | if (batchResp.status) {
|
294 |
|
295 | if (batchResp.status >= 300) {
|
296 | throw new SriClientError({
|
297 | status: batchResp.status,
|
298 | body: batchResp.results
|
299 | });
|
300 | } else {
|
301 | return batchResp.results;
|
302 | }
|
303 | }
|
304 | if (batchResp.body.status >= 300) {
|
305 | throw new SriClientError({
|
306 | status: batchResp.body.status,
|
307 | body: batchResp.body.results,
|
308 | headers: batchResp.headers
|
309 | });
|
310 | } else {
|
311 | return originallyFullResponse ? batchResp.body : batchResp.body.results;
|
312 | }
|
313 | }
|
314 | const resp = await this.sendPayload(href, payload, options, method);
|
315 | this.cache.onDataAltered(href, payload, method);
|
316 | return resp;
|
317 | } catch(error) {
|
318 | if (error instanceof SriClientError && options && options.retry && options.retry.retries !== 0 && !(error.status && error.status < 500)) {
|
319 | let wait = options.retry.wait;
|
320 | if (!wait) {
|
321 | wait = options.retry.initialWait ? options.retry.initialWait : 500;
|
322 | } else {
|
323 | wait = wait * (options.retry.factor ? options.retry.factor : 2);
|
324 | }
|
325 | console.log(`[sri-client->RETRY:${wait}] ${method} to ${href} failed! We will try again in ${wait} miliseconds...`);
|
326 | const newOptions = { ...options, retry: {
|
327 | ...options.retry,
|
328 | retries: options.retry.retries - 1,
|
329 | wait
|
330 | } };
|
331 | await commonUtils.sleep(wait);
|
332 | return this.wrapSendPayload(href, payload, newOptions, method);
|
333 | } else {
|
334 | throw error;
|
335 | }
|
336 | }
|
337 | }
|
338 |
|
339 | put(href, payload, options) {
|
340 | return this.wrapSendPayload(href, payload, options, 'PUT');
|
341 | }
|
342 | patch(href, payload, options) {
|
343 | return this.wrapSendPayload(href, payload, options, 'PATCH');
|
344 | }
|
345 | updateResource(resource, options) {
|
346 | return this.put(resource.$$meta.permalink, resource, options);
|
347 | }
|
348 | post(href, payload, options) {
|
349 | return this.wrapSendPayload(href, payload, options, 'POST');
|
350 | }
|
351 |
|
352 |
|
353 | delete() {}
|
354 |
|
355 | async wrapDelete(href, options) {
|
356 | const resp = await this.delete(href, options);
|
357 | this.cache.onDataAltered(href, null, 'DELETE');
|
358 | return resp;
|
359 | }
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 | async add$$expanded(hrefs, json, properties, includeOptions, expandOptions, cachingOptions, loggingOptions) {
|
367 |
|
368 | const cachedResources = [];
|
369 | const uncachedHrefs = {};
|
370 | const hrefsMap = {};
|
371 | for(let href of hrefs) {
|
372 | if(this.cache.has(href, undefined, cachingOptions)) {
|
373 | hrefsMap[href] = await this.cache.get(href, undefined, {caching: cachingOptions, logging: loggingOptions}, false);
|
374 | cachedResources.push(hrefsMap[href]);
|
375 | } else {
|
376 | const path = commonUtils.getPathFromPermalink(href);
|
377 | if(!uncachedHrefs[path]) {
|
378 | uncachedHrefs[path] = [];
|
379 | }
|
380 | uncachedHrefs[path].push(href);
|
381 | }
|
382 | }
|
383 | if(expandOptions && cachedResources.length > 0) {
|
384 | await this.expandJson(cachedResources, expandOptions, cachingOptions, loggingOptions);
|
385 | }
|
386 | const promises = [];
|
387 | for(let path of Object.keys(uncachedHrefs)) {
|
388 |
|
389 |
|
390 | promises.push(this.getAllHrefs(uncachedHrefs[path], null, {}, {asMap: true, include: includeOptions, expand: expandOptions, caching: cachingOptions, logging: loggingOptions}).then(function(newMap) {
|
391 | Object.assign(hrefsMap, newMap);
|
392 | }));
|
393 | }
|
394 | await Promise.all(promises);
|
395 | let newHrefs = new Set();
|
396 | for(let property of properties) {
|
397 | let propertyName = property;
|
398 | let required = true;
|
399 | if (!(typeof property === 'string' || property instanceof String)) {
|
400 | propertyName = property.property;
|
401 | required = property.required;
|
402 | }
|
403 | const localHrefs = travelHrefsOfJson(json, propertyName.split('.'), {
|
404 | required: required,
|
405 | handlerFunction: function(object, propertyArray, resource, isDirectReference) {
|
406 | let expandedObject = hrefsMap[object.href];
|
407 | if(!expandedObject) {
|
408 | return [object.href];
|
409 | }
|
410 | if(isDirectReference) {
|
411 | object['$$'+propertyArray[0]] = hrefsMap[object[propertyArray[0]]];
|
412 | return travelHrefsOfJson(object['$$'+propertyArray[0]], propertyArray);
|
413 | }
|
414 | object.$$expanded = expandedObject;
|
415 | return travelHrefsOfJson(object.$$expanded, propertyArray);
|
416 | }
|
417 | });
|
418 | newHrefs = new Set([...newHrefs, ...localHrefs]);
|
419 | };
|
420 | newHrefs = [...newHrefs];
|
421 | hrefs = [...hrefs];
|
422 | let converged = hrefs.size === newHrefs.size;
|
423 | if(converged) {
|
424 | for(let i = 0; i < hrefs.length; i++) {
|
425 | if(hrefs[i] !== newHrefs[i]) {
|
426 | converged = false;
|
427 | break;
|
428 | }
|
429 | }
|
430 | }
|
431 | if(converged) {
|
432 | console.warn('[WARNING] The data is inconsistent. There are hrefs that can not be retrieved because they do not exist or because they are deleted. hrefs: ' + JSON.stringify([...newHrefs]));
|
433 | }
|
434 | if(newHrefs.length > 0 && !converged) {
|
435 | await this.add$$expanded(newHrefs, json, properties, null, null, cachingOptions, loggingOptions);
|
436 | }
|
437 | }
|
438 |
|
439 | async expandJson(json, properties, cachingOptions, loggingOptions) {
|
440 | if(!Array.isArray(properties)) {
|
441 | properties = [properties];
|
442 | }
|
443 | let allHrefs = new Set();
|
444 | for(let property of properties) {
|
445 | let propertyName = property;
|
446 | let includeOptions = null;
|
447 | let expandOptions = [];
|
448 | let required = true;
|
449 | let localCachingOptions = null;
|
450 | if (!(typeof property === 'string' || property instanceof String)) {
|
451 | propertyName = property.property;
|
452 | includeOptions = property.include;
|
453 | required = property.required;
|
454 | localCachingOptions = property.caching;
|
455 | expandOptions = property.expand;
|
456 | }
|
457 | if(includeOptions || localCachingOptions || expandOptions) {
|
458 | let localHrefs = travelHrefsOfJson(json, propertyName.split('.'), {required: required});
|
459 | if(localHrefs.length > 0) {
|
460 | await this.add$$expanded(localHrefs, json, [property], includeOptions, expandOptions, localCachingOptions || cachingOptions, loggingOptions);
|
461 | }
|
462 | } else {
|
463 | allHrefs = new Set([...allHrefs, ...travelHrefsOfJson(json, propertyName.split('.'), {required: required})]);
|
464 | }
|
465 | };
|
466 | if(allHrefs.size > 0) {
|
467 | await this.add$$expanded(allHrefs, json, properties, undefined, undefined, cachingOptions, loggingOptions);
|
468 | }
|
469 | }
|
470 |
|
471 | async includeJson(json, inclusions, cachingOptions = {}, loggingOptions) {
|
472 | if(!Array.isArray(inclusions)) {
|
473 | inclusions = [inclusions];
|
474 | }
|
475 | for(let options of inclusions) {
|
476 | validate(options, includeOptionsSchema);
|
477 | options.expanded = options.expanded ? options.expanded : true;
|
478 |
|
479 |
|
480 | let referenceProperty = options.reference;
|
481 | let referenceParameterName = options.reference;
|
482 | const localCachingOptions = options.caching;
|
483 | if (!(typeof options.reference === 'string' || options.reference instanceof String)) {
|
484 | referenceProperty = options.reference.property;
|
485 | referenceParameterName = options.reference.parameterName ? options.reference.parameterName : referenceProperty;
|
486 | }
|
487 | if(!options.expanded) {
|
488 |
|
489 | const promises = [];
|
490 | travelHrefsOfJson(json, ('$$meta.permalink').split('.'), {
|
491 | required: true,
|
492 | handlerFunction: function(object, propertyArray, resource) {
|
493 | options.filters = options.filters || {};
|
494 | if(options.collapsed) {
|
495 | options.filters.expand = 'NONE';
|
496 | }
|
497 | options.filters[referenceParameterName] = object[propertyArray[0]];
|
498 | promises.push(this.getAll(options.href, options.filters, {include: options.include, caching: localCachingOptions || cachingOptions, logging: loggingOptions}).then(function(results) {
|
499 | resource[options.alias] = options.singleton ? (results.length === 0 ? null : results[0]) : results;
|
500 | }));
|
501 | return [];
|
502 | }
|
503 | });
|
504 | await Promise.all(promises);
|
505 | } else {
|
506 | const hrefs = travelHrefsOfJson(json, ('$$meta.permalink').split('.'));
|
507 | const results = await this.getAllReferencesTo(options.href, options.filters, referenceParameterName, hrefs, {expand: options.expand, include: options.include, caching: localCachingOptions || cachingOptions, logging: loggingOptions});
|
508 |
|
509 | |
510 |
|
511 |
|
512 | const map = {};
|
513 | for(let result of results) {
|
514 | const permalinks = travelHrefsOfJson(result, referenceProperty.split('.'), {required: true});
|
515 | if(permalinks.length > 1) {
|
516 | console.warn('SRI_CLIENT_INCLUDE: we do not support yet the possibility that options.reference references an array property. Contact us to request that we add this feature.');
|
517 | }
|
518 | const permalink = permalinks[0];
|
519 | if(!map[permalink]) {
|
520 | map[permalink] = [];
|
521 | }
|
522 | map[permalink].push(result);
|
523 | }
|
524 |
|
525 | const resources = travelResourcesOfJson(json);
|
526 | for(let resource of resources) {
|
527 | let inclusions = map[resource.$$meta.permalink];
|
528 | if(!inclusions) {
|
529 | inclusions = [];
|
530 | }
|
531 | resource[options.alias] = options.singleton ? (inclusions.length === 0 ? null : inclusions[0]) : inclusions;
|
532 | }
|
533 | }
|
534 | }
|
535 | }
|
536 |
|
537 |
|
538 | };
|
539 |
|
540 | const travelHrefsOfObject = function(object, propertyArray, options) {
|
541 | if(propertyArray.length === 1 && typeof object[propertyArray[0]] === 'string' && object[propertyArray[0]].match(/^(\/[-a-zA-Z0-9@:%_\+.~#?&=]+)+$/g)) {
|
542 | if(options.handlerFunction) {
|
543 | return options.handlerFunction(object, propertyArray, options.resource, true);
|
544 | } else {
|
545 | return [object[propertyArray[0]]];
|
546 | }
|
547 | }
|
548 | if(object.href) {
|
549 | if(object.$$expanded) {
|
550 | options.resource = options.resource ? options.resource : object.$$expanded;
|
551 | |
552 |
|
553 |
|
554 | return travelHrefsOfJson(object.$$expanded, propertyArray, options);
|
555 | } else if (!options.resource && object.body) {
|
556 | return travelHrefsOfJson(object.body, propertyArray, options);
|
557 | }
|
558 | if(options.handlerFunction) {
|
559 | return options.handlerFunction(object, propertyArray, options.resource);
|
560 | } else {
|
561 | return [object.href];
|
562 | }
|
563 | } else {
|
564 | return travelHrefsOfJson(object, propertyArray, options);
|
565 | }
|
566 | };
|
567 |
|
568 | const travelHrefsOfJson = function(json, propertyArray, options = {}) {
|
569 | options.required = options.required === false ? options.required : true;
|
570 | if(propertyArray.length === 0) {
|
571 | return [];
|
572 | }
|
573 | let hrefs = [];
|
574 | if(json.$$meta && json.results) {
|
575 | json = json.results;
|
576 | }
|
577 | if(!options.resource && Array.isArray(json)) {
|
578 | for(let item of json) {
|
579 | hrefs = [...hrefs, ...travelHrefsOfObject(item, [...propertyArray], Object.assign({}, options))];
|
580 | }
|
581 | } else {
|
582 | if(!options.resource) {
|
583 | options.resource = json;
|
584 | }
|
585 | const nextPropertyName = propertyArray.shift();
|
586 | const subResource = json[nextPropertyName];
|
587 | if(!subResource) {
|
588 |
|
589 | if(!options.required) {
|
590 | return [];
|
591 | }
|
592 | throw new Error('There is no property ' + nextPropertyName + ' in the object: \n' + util.inspect(json, {depth: 5}) + '\n Set required = false if the property path contains non required resources.');
|
593 | }
|
594 | if(Array.isArray(subResource)) {
|
595 | for(let item of subResource) {
|
596 | hrefs = [...hrefs, ...travelHrefsOfObject(item, [...propertyArray], Object.assign({}, options))];
|
597 | }
|
598 | } else {
|
599 | hrefs = travelHrefsOfObject(subResource, propertyArray, options);
|
600 | }
|
601 | }
|
602 | return hrefs.filter(href => href.match(/^\//));
|
603 | };
|
604 |
|
605 | const travelResoure = function(resource, handlerFunction) {
|
606 | if(handlerFunction) {
|
607 | return handlerFunction(resource);
|
608 | } else {
|
609 | return resource;
|
610 | }
|
611 | };
|
612 |
|
613 | const travelResourcesOfJson = function(json, handlerFunction) {
|
614 | let resources = [];
|
615 | if(json.$$meta && json.results) {
|
616 | json = json.results;
|
617 | }
|
618 | if(Array.isArray(json)) {
|
619 | for(let item of json) {
|
620 | if(item.href) {
|
621 | if(item.$$expanded) {
|
622 | resources = [...resources, travelResoure(item.$$expanded, handlerFunction)];
|
623 | } else if (item.body) {
|
624 | resources = [...resources, ...travelResourcesOfJson(item.body, handlerFunction)];
|
625 | }
|
626 | } else {
|
627 | resources = [...resources, travelResoure(item, handlerFunction)];
|
628 | }
|
629 | }
|
630 | } else {
|
631 | resources = [travelResoure(json, handlerFunction)];
|
632 | }
|
633 | return resources;
|
634 | };
|
635 |
|
636 | const includeOptionsSchema = {
|
637 | type: "object",
|
638 | properties: {
|
639 | alias: {
|
640 | type: "string"
|
641 | },
|
642 | href: {
|
643 | type: "string",
|
644 | pattern: "^\/.*$"
|
645 | },
|
646 | reference: {
|
647 | oneOf: [{
|
648 | type: "string"
|
649 | }, {
|
650 | type: "object",
|
651 | properties: {
|
652 | property: {
|
653 | type: "string"
|
654 | },
|
655 | parameterName: {
|
656 | type: "string"
|
657 | }
|
658 | },
|
659 | required: ["property"]
|
660 | }]
|
661 | },
|
662 | params: {
|
663 | type:"object"
|
664 | },
|
665 | collapsed: {
|
666 | type: "boolean"
|
667 | },
|
668 | singleton: {
|
669 | type: "boolean"
|
670 | },
|
671 | expand: {
|
672 | type: "array",
|
673 | items: {
|
674 | type: "string"
|
675 | }
|
676 | }
|
677 | },
|
678 | required: ['alias', 'url', 'reference']
|
679 | };
|