@nextgis/ngw-connector 3.0.0-alpha.1 → 3.0.0-alpha.13

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.
@@ -1,411 +1,589 @@
1
- /** Bundle of @nextgis/ngw-connector; version: 3.0.0-alpha.1; author: NextGIS */
1
+ /** Bundle of @nextgis/ngw-connector; version: 3.0.0-alpha.13; author: NextGIS */
2
2
  'use strict';
3
3
 
4
- var events = require('events');
5
- var Cache = require('@nextgis/cache');
6
4
  var utils = require('@nextgis/utils');
5
+ var Cache = require('@nextgis/cache');
6
+ var events = require('events');
7
7
 
8
- var __defProp$4 = Object.defineProperty;
9
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
- var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
8
+ const templateRe = /\{ *([\w_-]+) *\}/g;
9
+ function template(str, data) {
10
+ return str.replace(templateRe, (s, key) => {
11
+ let value = data[key];
12
+ if (value === void 0) {
13
+ throw new Error("No value provided for letiable " + s);
14
+ } else if (typeof value === "function") {
15
+ value = value(data);
16
+ }
17
+ return value;
18
+ });
19
+ }
20
+
21
+ async function apiRequest(opt) {
22
+ const { params, name, connector, requestOptions } = opt;
23
+ const apiItems = await connector.connect();
24
+ let apiItem = apiItems && apiItems[name];
25
+ if (apiItem) {
26
+ apiItem = [...apiItem];
27
+ let url = apiItem.shift();
28
+ if (apiItem.length) {
29
+ const replaceParams = {};
30
+ for (let fry = 0; fry < apiItem.length; fry++) {
31
+ const arg = apiItem[fry];
32
+ replaceParams[fry] = `{${arg}}`;
33
+ if (params[arg] === void 0) {
34
+ throw new Error(`\`${arg}\` URL API argument is not specified`);
35
+ }
36
+ }
37
+ if (url) {
38
+ url = template(url, replaceParams);
39
+ }
40
+ }
41
+ if (params) {
42
+ const paramArray = [];
43
+ const paramList = params.paramList;
44
+ if (Array.isArray(paramList)) {
45
+ paramList.forEach(([key, value]) => {
46
+ paramArray.push(`${key}=${value}`);
47
+ });
48
+ }
49
+ for (const p in params) {
50
+ if (apiItem.indexOf(p) === -1) {
51
+ paramArray.push(`${p}=${params[p]}`);
52
+ }
53
+ }
54
+ if (paramArray.length) {
55
+ url = `${url}?${paramArray.join("&")}`;
56
+ }
57
+ }
58
+ if (url) {
59
+ return connector.makeQuery(url, params, {
60
+ cacheName: name,
61
+ ...requestOptions
62
+ });
63
+ } else {
64
+ throw new Error("Request URL is not set");
65
+ }
66
+ } else {
67
+ return void 0;
68
+ }
69
+ }
70
+
71
+ var pkg = {
72
+ name: "@nextgis/ngw-connector",
73
+ version: "3.0.0-alpha.13",
74
+ _priority: 12,
75
+ description: "A lightweight HTTP client optimized for use with NextGIS Web API",
76
+ main: "index.js",
77
+ module: "lib/ngw-connector.esm-bundler.js",
78
+ unpkg: "lib/ngw-connector.global.prod.js",
79
+ jsdelivr: "lib/ngw-connector.global.prod.js",
80
+ types: "lib/index.d.ts",
81
+ dependencies: {
82
+ "@nextgis/cache": "^3.0.0-alpha.13",
83
+ "@nextgis/cancelable-promise": "^3.0.0-alpha.13",
84
+ "@nextgis/utils": "^3.0.0-alpha.13",
85
+ "@types/events": "^3.0.0",
86
+ events: "*",
87
+ "form-data": "^4.0.0"
88
+ },
89
+ devDependencies: {
90
+ "@nextgis/build-tools": "^3.0.0-alpha.13"
91
+ },
92
+ scripts: {
93
+ clean: "rimraf ./lib",
94
+ dev: "node ../build-tools/lib/build.js",
95
+ prod: "npm run dev -- --release",
96
+ lint: "eslint ./src/**/*.ts --fix --c ../../.eslintrc",
97
+ watch: "npm run dev -- --watch",
98
+ "gen:types": "node ./scripts/generator"
99
+ },
100
+ buildOptions: {
101
+ name: "NgwConnector",
102
+ formats: [
103
+ "esm-bundler",
104
+ "esm-browser",
105
+ "cjs",
106
+ "global"
107
+ ]
108
+ },
109
+ keywords: [
110
+ "NextGIS",
111
+ "MAP"
112
+ ],
113
+ author: "NextGIS",
114
+ files: [
115
+ "index.js",
116
+ "lib"
117
+ ],
118
+ license: "MIT",
119
+ homepage: "https://github.com/nextgis/nextgis_frontend/tree/master/packages/ngw-connector#readme",
120
+ repository: {
121
+ type: "git",
122
+ url: "git://github.com/nextgis/nextgis_frontend.git"
123
+ },
124
+ engines: {
125
+ node: ">=18.20.4"
126
+ },
127
+ gitHead: "edc68033f914f6c95c4125b9738bba6d36e990b4"
128
+ };
129
+
130
+ class AbortError extends Error {
131
+ constructor(message = "AbortError") {
132
+ super(message);
133
+ this.name = "AbortError";
134
+ }
135
+ }
136
+
137
+ var __defProp$8 = Object.defineProperty;
138
+ var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
139
+ var __publicField$8 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
11
140
  class NgwError extends Error {
12
141
  constructor(er) {
13
142
  super();
14
- __publicField$4(this, "name", "NgwError");
15
- __publicField$4(this, "title");
16
- __publicField$4(this, "message");
17
- __publicField$4(this, "detail");
18
- __publicField$4(this, "exception");
19
- __publicField$4(this, "status_code");
20
- __publicField$4(this, "data");
21
- __publicField$4(this, "guru_meditation");
143
+ __publicField$8(this, "name", "NgwError");
144
+ __publicField$8(this, "title");
145
+ __publicField$8(this, "message");
146
+ __publicField$8(this, "detail");
147
+ __publicField$8(this, "exception");
148
+ __publicField$8(this, "status_code");
149
+ __publicField$8(this, "data");
150
+ __publicField$8(this, "guru_meditation");
22
151
  Object.assign(this, er);
23
152
  Object.setPrototypeOf(this, NgwError.prototype);
24
153
  }
25
154
  }
26
155
 
27
- var __defProp$3 = Object.defineProperty;
28
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
29
- var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
156
+ var __defProp$7 = Object.defineProperty;
157
+ var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
158
+ var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
159
+ class InsufficientPermissionsError extends NgwError {
160
+ constructor(obj) {
161
+ super(obj);
162
+ __publicField$7(this, "name", "InsufficientPermissionsError");
163
+ __publicField$7(this, "exception", "nextgisweb.core.exception.InsufficientPermissions");
164
+ Object.setPrototypeOf(this, InsufficientPermissionsError.prototype);
165
+ }
166
+ }
167
+
168
+ var __defProp$6 = Object.defineProperty;
169
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
170
+ var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
171
+ let NetworksResponseError$1 = class NetworksResponseError extends NgwError {
172
+ constructor(obj) {
173
+ super(obj);
174
+ __publicField$6(this, "message", "There is no response from the server or problem connecting to server.");
175
+ __publicField$6(this, "title", "Network error");
176
+ __publicField$6(this, "detail", "Check network connectivity and try again later.");
177
+ Object.setPrototypeOf(this, NetworksResponseError.prototype);
178
+ }
179
+ };
180
+
181
+ function extractError(error) {
182
+ if (utils.isObject(error)) {
183
+ if (error.name && error.message && error.title) {
184
+ return {
185
+ title: error.title,
186
+ message: error.message,
187
+ detail: error.detail || null,
188
+ data: error.data && error.data.data ? error.data.data : null
189
+ };
190
+ } else if (error.exception) {
191
+ if (error.status === void 0 || error.status === 0 || error.data === void 0) {
192
+ return new NetworksResponseError$1({
193
+ title: error.title,
194
+ status_code: error.status_code,
195
+ exception: error.exception
196
+ });
197
+ }
198
+ }
199
+ return {
200
+ title: typeof error.title === "string" ? error.title : "Unexpected error",
201
+ message: typeof error.message === "string" ? error.message : "Something went wrong."
202
+ };
203
+ }
204
+ }
205
+
206
+ function isError(error) {
207
+ if (utils.isObject(error)) {
208
+ return error.status_code && error.exception && error.title;
209
+ }
210
+ return false;
211
+ }
212
+
213
+ var __defProp$5 = Object.defineProperty;
214
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
215
+ var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, key + "" , value);
216
+ class NetworkError extends Error {
217
+ constructor(url) {
218
+ super();
219
+ __publicField$5(this, "name", "NetworkError");
220
+ Object.setPrototypeOf(this, NetworkError.prototype);
221
+ this.message = `Unable to request ${url}.
222
+ Possibly invalid NGW URL entered or CORS not configured to get request from ${location.origin}`;
223
+ }
224
+ }
225
+
226
+ var __defProp$4 = Object.defineProperty;
227
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
228
+ var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
30
229
  class ResourceNotFoundError extends NgwError {
31
230
  constructor(obj) {
32
231
  super(obj);
33
- __publicField$3(this, "name", "ResourceNotFoundError");
34
- __publicField$3(this, "exception", "nextgisweb.resource.exception.ResourceNotFound");
232
+ __publicField$4(this, "name", "ResourceNotFoundError");
233
+ __publicField$4(this, "exception", "nextgisweb.resource.exception.ResourceNotFound");
35
234
  Object.setPrototypeOf(this, ResourceNotFoundError.prototype);
36
235
  }
37
236
  }
38
237
 
39
- function isObject(val) {
40
- return Object.prototype.toString.call(val) === "[object Object]";
41
- }
238
+ var errors = /*#__PURE__*/Object.freeze({
239
+ __proto__: null,
240
+ AbortError: AbortError,
241
+ InsufficientPermissionsError: InsufficientPermissionsError,
242
+ NetworkError: NetworkError,
243
+ NetworksResponseError: NetworksResponseError$1,
244
+ NgwError: NgwError,
245
+ ResourceNotFoundError: ResourceNotFoundError,
246
+ extractError: extractError,
247
+ isError: isError
248
+ });
42
249
 
43
- function resourceCompare(res1, res2) {
44
- return utils.objectDeepEqual(res1, res2);
250
+ var __defProp$3 = Object.defineProperty;
251
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
252
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
253
+ class BaseAPIError extends Error {
254
+ constructor(message) {
255
+ super(message || "Something went wrong.");
256
+ __publicField$3(this, "title");
257
+ this.name = "BaseAPIError";
258
+ this.title = "Unknown API error";
259
+ if (Error.captureStackTrace) {
260
+ Error.captureStackTrace(this, BaseAPIError);
261
+ }
262
+ }
263
+ }
264
+ class NetworksResponseError extends BaseAPIError {
265
+ // prettier-ignore
266
+ constructor(message) {
267
+ super(message || "There is no response from the server or problem connecting to server.");
268
+ __publicField$3(this, "detail");
269
+ this.title = "Network error";
270
+ this.detail = "Check network connectivity and try again later.";
271
+ }
272
+ }
273
+ class InvalidResponseError extends BaseAPIError {
274
+ constructor(message) {
275
+ super(message || "Something went wrong.");
276
+ this.title = "Unexpected server response";
277
+ }
278
+ }
279
+ class ServerResponseError extends BaseAPIError {
280
+ constructor(data) {
281
+ super(data.message);
282
+ __publicField$3(this, "detail");
283
+ __publicField$3(this, "data");
284
+ this.title = data.title || this.title;
285
+ this.detail = data.detail || null;
286
+ this.data = data;
287
+ }
288
+ }
289
+ class LunkwillError extends Error {
290
+ // prettier-ignore
291
+ constructor(message, data = {}) {
292
+ super(message || "Unexpected error while processing long-running request.");
293
+ __publicField$3(this, "title");
294
+ __publicField$3(this, "data");
295
+ this.name = "LunkwillError";
296
+ this.title = "Long-running request error";
297
+ this.data = data;
298
+ if (Error.captureStackTrace) {
299
+ Error.captureStackTrace(this, LunkwillError);
300
+ }
301
+ }
302
+ }
303
+ class LunkwillRequestCancelled extends LunkwillError {
304
+ constructor(data) {
305
+ super("Long-running request was cancelled.", data);
306
+ }
307
+ }
308
+ class LunkwillRequestFailed extends LunkwillError {
309
+ constructor(data) {
310
+ super(void 0, data);
311
+ }
45
312
  }
46
313
 
47
- const exclude = ["description"];
48
- function resourceToQuery(resource, prefix = "") {
49
- prefix = prefix ? prefix + "__" : "";
50
- const query = {};
51
- for (const [key, value] of Object.entries(resource)) {
52
- if (exclude.indexOf(key) === -1) {
53
- if (isObject(value)) {
54
- if (key === "owner_user") {
55
- const children = resourceToQuery(value, key);
56
- Object.assign(query, children);
57
- } else if (key === "parent" && "id" in value) {
58
- query.parent_id = value.id;
314
+ function lunkwillCheckResponse(lwResp) {
315
+ const ct = lwResp.headers.get("content-type");
316
+ return ct !== void 0 && ct !== null && ct.includes("application/vnd.lunkwill.request-summary+json");
317
+ }
318
+ async function responseJson(response) {
319
+ try {
320
+ return response.json();
321
+ } catch {
322
+ throw new Error();
323
+ }
324
+ }
325
+ async function lunkwillResponseUrl(lwResp) {
326
+ const lwData = await responseJson(lwResp);
327
+ let delay = lwData.delay_ms;
328
+ const retry = lwData.retry_ms !== void 0 ? lwData.retry_ms : 2e3;
329
+ const sum = `/api/lunkwill/${lwData.id}/summary`;
330
+ const res = `/api/lunkwill/${lwData.id}/response`;
331
+ const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec));
332
+ let failed = false;
333
+ let ready = false;
334
+ while (!ready) {
335
+ await sleep(failed ? retry : delay);
336
+ failed = false;
337
+ let lwResp2;
338
+ let lwData2;
339
+ try {
340
+ lwResp2 = await fetch(sum, { credentials: "same-origin" });
341
+ lwData2 = await lwResp2.json();
342
+ } catch {
343
+ failed = true;
344
+ continue;
345
+ }
346
+ switch (lwData2.status) {
347
+ case void 0:
348
+ throw new LunkwillError(void 0, lwData2);
349
+ case "ready":
350
+ ready = true;
351
+ break;
352
+ case "cancelled":
353
+ throw new LunkwillRequestCancelled(lwData2);
354
+ case "failed":
355
+ throw new LunkwillRequestFailed(lwData2);
356
+ case "spooled":
357
+ case "processing":
358
+ case "buffering":
359
+ delay = lwData2.delay_ms;
360
+ break;
361
+ default:
362
+ throw new LunkwillError(void 0, lwData2);
363
+ }
364
+ }
365
+ return res;
366
+ }
367
+ async function lunkwillFetch(lwRespUrl) {
368
+ try {
369
+ return await window.fetch(lwRespUrl, { credentials: "same-origin" });
370
+ } catch {
371
+ throw new NetworksResponseError();
372
+ }
373
+ }
374
+ const mediaContentTypes = [
375
+ "application/pdf",
376
+ "image/png",
377
+ "image/jpeg",
378
+ "image/tiff",
379
+ "text/csv"
380
+ ];
381
+ const mediaContentTypesRegex = new RegExp(mediaContentTypes.join("|"));
382
+ function isMediaContentType(contentType) {
383
+ return mediaContentTypesRegex.test(contentType);
384
+ }
385
+ function encodeQueryParams(value) {
386
+ const result = [];
387
+ for (const [k, v] of Object.entries(value)) {
388
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
389
+ result.push(`${k}=${encodeURIComponent(v)}`);
390
+ } else if (Array.isArray(v)) {
391
+ result.push(`${k}=${v.map(encodeURIComponent).join(",")}`);
392
+ } else {
393
+ for (const [sk, sv] of Object.entries(v)) {
394
+ const ske = `${k}[${encodeURIComponent(sk)}]`;
395
+ if (typeof sv === "string" || typeof sv === "number" || typeof sv === "boolean") {
396
+ result.push(`${ske}=${encodeURIComponent(sv)}`);
397
+ } else if (Array.isArray(sv)) {
398
+ const sve = sv.map(encodeURIComponent);
399
+ result.push(`${ske}=${sve.join(",")}`);
59
400
  }
60
- } else if (utils.defined(value)) {
61
- query[prefix + key] = value;
62
401
  }
63
402
  }
64
403
  }
65
- return query;
404
+ return result.join("&");
66
405
  }
67
-
68
- var __defProp$2 = Object.defineProperty;
69
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
70
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
71
- class ResourcesControl {
72
- constructor({
73
- connector,
74
- cacheId
75
- }) {
76
- __publicField$2(this, "cache");
77
- __publicField$2(this, "connector");
78
- this.connector = connector;
79
- this.cache = new Cache({ namespace: cacheId });
406
+ function generateUrl(path, query) {
407
+ let urlParams = "";
408
+ if (query !== void 0) {
409
+ urlParams = "?" + encodeQueryParams(query);
80
410
  }
81
- // -------------------------------------------------------------------------
82
- // Resource Methods
83
- // -------------------------------------------------------------------------
84
- /**
85
- * Receive resource from NGW by id, keyname or search-object parameter.
86
- * @param resource - Resource id, keyname or search-object
87
- *
88
- * @remarks
89
- * Fetching resource would be cached to speed up next call
90
- */
91
- getOne(resource, requestOptions) {
92
- const opt = { ...requestOptions };
93
- if (typeof resource === "string") ; else if (typeof resource === "number") ; else if (isObject(resource)) {
94
- if (resource.id !== void 0) {
95
- resource.id;
96
- } else {
97
- if (resource.keyname) {
98
- resource.keyname;
99
- }
100
- if (resource.display_name) {
101
- resource.display_name;
102
- }
411
+ return path + urlParams;
412
+ }
413
+ async function request(path, options, cacheEngine) {
414
+ var _a;
415
+ const defaults = {
416
+ method: "GET",
417
+ credentials: "same-origin",
418
+ headers: {}
419
+ };
420
+ const {
421
+ withCredentials,
422
+ responseType,
423
+ cacheProps,
424
+ cacheName,
425
+ lunkwill,
426
+ cache,
427
+ query,
428
+ json,
429
+ ...opt
430
+ } = {
431
+ ...defaults,
432
+ ...options
433
+ };
434
+ if (withCredentials) {
435
+ opt.credentials = "include";
436
+ }
437
+ let useLunkwill = false;
438
+ if (lunkwill !== void 0) {
439
+ lunkwill.toHeaders(opt.headers || {});
440
+ useLunkwill = true;
441
+ }
442
+ const lunkwillReturnUrl = !!opt.lunkwillReturnUrl;
443
+ delete opt.lunkwillReturnUrl;
444
+ if (json !== void 0) {
445
+ opt.body = JSON.stringify(json);
446
+ const headers = opt.headers || {};
447
+ headers["Content-Type"] = "application/json";
448
+ opt.headers = headers;
449
+ }
450
+ const url = generateUrl(path, query);
451
+ const makeRequest = async () => {
452
+ let response;
453
+ try {
454
+ response = await fetch(url, opt);
455
+ } catch (e) {
456
+ if (opt.signal && opt.signal.aborted) {
457
+ throw e;
103
458
  }
459
+ throw new NetworksResponseError();
104
460
  }
105
- if (typeof resource === "string") {
106
- return this._fetchResourceBy({ keyname: resource }, opt);
107
- } else if (typeof resource === "number") {
108
- return this._fetchResourceById(resource, opt);
109
- } else if (isObject(resource)) {
110
- return this._fetchResourceBy(resource, opt);
461
+ if (useLunkwill && lunkwillCheckResponse(response)) {
462
+ const lwRespUrl = await lunkwillResponseUrl(response);
463
+ if (lunkwillReturnUrl) {
464
+ return lwRespUrl;
465
+ }
466
+ response = await lunkwillFetch(lwRespUrl);
111
467
  }
112
- return Promise.resolve(void 0);
113
- }
114
- getOneOrFail(resource, requestOptions) {
115
- return this.getOne(resource, requestOptions).then((res) => {
116
- if (res) {
117
- return res;
468
+ const respCType = response.headers.get("content-type");
469
+ const respJSON = respCType && (respCType.includes("application/json") || respCType.includes("application/vnd.lunkwill.request-summary+json"));
470
+ let body;
471
+ try {
472
+ const respMedia = respCType && isMediaContentType(respCType);
473
+ if (responseType === "blob" || respMedia) {
474
+ body = await response.blob();
475
+ } else if (respJSON) {
476
+ body = await response.json();
477
+ } else {
478
+ throw new InvalidResponseError();
118
479
  }
119
- throw new ResourceNotFoundError();
480
+ } catch (e) {
481
+ if (e.name === "AbortError" || e instanceof InvalidResponseError) {
482
+ throw e;
483
+ }
484
+ throw new InvalidResponseError();
485
+ }
486
+ if (400 <= response.status && response.status <= 599) {
487
+ throw new ServerResponseError(body);
488
+ }
489
+ return body;
490
+ };
491
+ if (cacheEngine && cache !== false && ((_a = opt.method) == null ? void 0 : _a.toUpperCase()) === "GET") {
492
+ const props = cacheProps ? cacheProps : {
493
+ ...utils.objectRemoveEmpty({
494
+ withCredentials,
495
+ responseType
496
+ })
497
+ };
498
+ return cacheEngine.add(cacheName || url, makeRequest, {
499
+ props,
500
+ expirationTime: cache ? void 0 : 500
120
501
  });
121
502
  }
122
- /**
123
- * A fast way to retrieve resource ID for any resource definition.
124
- * @param resource - Any available resource definition
125
- *
126
- * @remarks
127
- * There are situations when exactly the resource id is needed
128
- * (for example, to compose the correct request to the api)
129
- * then this method will come in handy to facilitate the extraction of the identifier
130
- * if the resource is specified through a keyname or other parameters.
131
- */
132
- getId(resource, requestOptions) {
133
- if (typeof resource === "number") {
134
- return Promise.resolve(resource);
135
- } else if (typeof resource === "string" || isObject(resource)) {
136
- return this.getOne(resource, requestOptions).then((res) => {
137
- if (res) {
138
- return res.resource.id;
139
- }
140
- });
503
+ return makeRequest();
504
+ }
505
+
506
+ function routeURL(name, baseUrl, routeData, ...rest) {
507
+ const [template, ...params] = routeData[name];
508
+ const first = rest[0];
509
+ let sub;
510
+ if (first === void 0) {
511
+ sub = [];
512
+ } else if (typeof first === "object" && first !== null) {
513
+ if (rest.length > 1) {
514
+ throw new Error("Too many arguments for route(name, object)!");
141
515
  }
142
- return Promise.resolve(void 0);
516
+ sub = [];
517
+ for (const [p, v] of Object.entries(first)) {
518
+ sub[params.indexOf(p)] = String(v);
519
+ }
520
+ } else {
521
+ sub = rest.map((v) => String(v));
143
522
  }
144
- /**
145
- * A fast way to retrieve resource ID for any resource definition.
146
- * @param resource - Any available resource definition
147
- *
148
- * @remarks
149
- * Similar with {@link NgwConnector.getResourceId | getResourceId} but rise error if resource is not exist.
150
- * To not make one more checks if the resource is definitely exists
151
- */
152
- getIdOrFail(resource, requestOptions) {
153
- return this.getId(resource, requestOptions).then((resp) => {
154
- if (resp === void 0) {
155
- throw new Error();
156
- }
157
- return resp;
158
- });
159
- }
160
- getMany(resource, requestOptions) {
161
- return this._resourceCacheFilter(resource).then((items) => {
162
- if (!items.length) {
163
- const query = {};
164
- if (resource.keyname) {
165
- query.keyname = resource.keyname;
166
- } else {
167
- Object.assign(query, resourceToQuery(resource));
168
- }
169
- return this.connector.get("resource.search", requestOptions, {
170
- serialization: "full",
171
- ...query
172
- }).then((resources) => {
173
- if ((requestOptions == null ? void 0 : requestOptions.cache) && resources) {
174
- for (const x of resources) {
175
- this.cache.add("resource.item", Promise.resolve(x), {
176
- id: x.resource.id
177
- });
178
- }
179
- }
180
- return resources;
181
- });
182
- }
183
- return items;
184
- });
185
- }
186
- getParent(resource, requestOptions) {
187
- return this.getOne(resource, requestOptions).then((child) => {
188
- if (child) {
189
- return this.getOne(child.resource.parent.id, requestOptions);
190
- }
191
- return Promise.resolve(void 0);
192
- });
193
- }
194
- getChildrenOf(resource, requestOptions) {
195
- return this.getIdOrFail(resource).then(
196
- (parent) => this._getChildrenOf(parent, requestOptions)
197
- );
198
- }
199
- update(resource, data) {
200
- return this.getId(resource).then((id) => {
201
- if (id !== void 0) {
202
- return this.connector.put("resource.item", { data }, { id });
203
- }
204
- });
205
- }
206
- /**
207
- * Fast way to delete resource from NGW and clean cache.
208
- * @param resource - Resource definition
209
- */
210
- delete(resource) {
211
- return this.getId(resource).then((id) => {
212
- if (id !== void 0) {
213
- return this.connector.delete("resource.item", null, { id }).then(() => {
214
- this._cleanResourceItemCache(id);
215
- return void 0;
216
- });
217
- }
218
- });
219
- }
220
- _getChildrenOf(parent, requestOptions, _items = []) {
221
- return this.connector.get("resource.collection", requestOptions, {
222
- parent
223
- }).then((items) => {
224
- const recursivePromises = [];
225
- for (const item of items) {
226
- if (requestOptions == null ? void 0 : requestOptions.cache) {
227
- this.cache.add("resource.item", Promise.resolve(item), {
228
- id: item.resource.id
229
- });
230
- }
231
- _items.push(item);
232
- if ((requestOptions == null ? void 0 : requestOptions.recursive) && item.resource.children) {
233
- recursivePromises.push(
234
- this._getChildrenOf(item.resource.id, requestOptions, _items)
235
- );
236
- }
237
- }
238
- if (recursivePromises.length) {
239
- return Promise.all(recursivePromises).then(() => {
240
- return _items;
241
- });
242
- }
243
- return _items;
244
- });
245
- }
246
- async _cleanResourceItemCache(id) {
247
- var _a;
248
- const all = this.cache.all();
249
- const toDelete = [];
250
- for (const c of all) {
251
- const cid = (_a = c.props) == null ? void 0 : _a.id;
252
- if (["resource.item", "resource"].includes(c.key) && cid !== void 0) {
253
- if (typeof cid === "number") {
254
- if (cid === id) {
255
- toDelete.push(c);
256
- }
257
- } else {
258
- const rid = await this.getId(cid);
259
- if (rid === id) {
260
- toDelete.push(c);
261
- }
262
- }
263
- }
264
- }
265
- for (const d of toDelete) {
266
- this.cache.delete(d);
267
- }
268
- }
269
- _fetchResourceById(id, requestOptions) {
270
- return this.connector.get("resource.item", requestOptions, { id });
271
- }
272
- _fetchResourceBy(resource, requestOptions) {
273
- return this.getMany(resource, requestOptions).then((resources) => {
274
- return resources[0];
275
- });
276
- }
277
- _resourceCacheFilter(resource) {
278
- return Promise.all(this.cache.matchAll("resource.item")).then(
279
- (resources) => {
280
- const items = [];
281
- resources.filter((x) => {
282
- if (x) {
283
- if (resource.keyname && x.resource.keyname) {
284
- return resource.keyname === x.resource.keyname;
285
- }
286
- if (utils.defined(resource.id) && utils.defined(x.resource.id)) {
287
- return resource.id === x.resource.id;
288
- }
289
- return resourceCompare(resource, x.resource);
290
- }
291
- });
292
- return items;
293
- }
294
- );
295
- }
296
- }
297
-
298
- const CONNECTORS = [];
299
- function addConnector(connector) {
300
- CONNECTORS.push(connector);
301
- }
302
- function findConnector(options) {
303
- return CONNECTORS.find((x) => {
304
- if (x.options.baseUrl === options.baseUrl) {
305
- if (options.auth) {
306
- if (x.options.auth) {
307
- return utils.objectDeepEqual(x.options.auth, options.auth);
308
- }
309
- } else {
310
- return true;
523
+ return utils.fixUrlStr(
524
+ baseUrl + template.replace(/\{(\w+)\}/g, function(m, a) {
525
+ const idx = parseInt(a);
526
+ const value = sub[idx];
527
+ if (value === void 0) {
528
+ const msg = `Undefined parameter ${idx} in "${template}".`;
529
+ throw new Error(msg);
311
530
  }
312
- }
313
- });
314
- }
315
- function removeConnector(connector) {
316
- const index = CONNECTORS.indexOf(connector);
317
- if (index !== -1) {
318
- CONNECTORS.splice(index, 1);
319
- }
320
- }
321
-
322
- class AbortError extends Error {
323
- constructor(message = "AbortError") {
324
- super(message);
325
- this.name = "AbortError";
326
- }
327
- }
328
-
329
- var __defProp$1 = Object.defineProperty;
330
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
331
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
332
- class InsufficientPermissionsError extends NgwError {
333
- constructor(obj) {
334
- super(obj);
335
- __publicField$1(this, "name", "InsufficientPermissionsError");
336
- __publicField$1(this, "exception", "nextgisweb.core.exception.InsufficientPermissions");
337
- Object.setPrototypeOf(this, InsufficientPermissionsError.prototype);
338
- }
531
+ return String(value);
532
+ })
533
+ );
339
534
  }
340
-
341
- const templateRe = /\{ *([\w_-]+) *\}/g;
342
- function template(str, data) {
343
- return str.replace(templateRe, (s, key) => {
344
- let value = data[key];
345
- if (value === void 0) {
346
- throw new Error("No value provided for letiable " + s);
347
- } else if (typeof value === "function") {
348
- value = value(data);
535
+ function route(name, connector, ...rest) {
536
+ const result = {
537
+ url: async (opt) => {
538
+ var _a;
539
+ const routeData = await connector.connect();
540
+ const template = routeURL(
541
+ name,
542
+ (_a = connector.options.baseUrl) != null ? _a : "",
543
+ routeData,
544
+ ...rest
545
+ );
546
+ return generateUrl(template, opt == null ? void 0 : opt.query);
349
547
  }
350
- return value;
351
- });
352
- }
353
-
354
- async function apiRequest(opt) {
355
- const { params, name, connector, requestOptions } = opt;
356
- const apiItems = await connector.connect();
357
- let apiItem = apiItems && apiItems[name];
358
- if (apiItem) {
359
- apiItem = [...apiItem];
360
- let url = apiItem.shift();
361
- if (apiItem.length) {
362
- const replaceParams = {};
363
- for (let fry = 0; fry < apiItem.length; fry++) {
364
- const arg = apiItem[fry];
365
- replaceParams[fry] = `{${arg}}`;
366
- if (params[arg] === void 0) {
367
- throw new Error(`\`${arg}\` URL API argument is not specified`);
368
- }
369
- }
370
- if (url) {
371
- url = template(url, replaceParams);
548
+ };
549
+ const methods = ["get", "post", "put", "delete", "patch"];
550
+ for (const method of methods) {
551
+ const methodResp = (requestOptions) => {
552
+ var _a;
553
+ const { headers: optHeaders, ...restOpt } = requestOptions || {};
554
+ if ((_a = requestOptions == null ? void 0 : requestOptions.signal) == null ? void 0 : _a.aborted) {
555
+ throw new AbortError();
372
556
  }
373
- }
374
- if (params) {
375
- const paramArray = [];
376
- const paramList = params.paramList;
377
- if (Array.isArray(paramList)) {
378
- paramList.forEach(([key, value]) => {
379
- paramArray.push(`${key}=${value}`);
557
+ return connector.connect().then((routeData) => {
558
+ var _a2;
559
+ const template = routeURL(
560
+ name,
561
+ (_a2 = connector.options.baseUrl) != null ? _a2 : "",
562
+ routeData,
563
+ ...rest
564
+ );
565
+ const headers = utils.objectRemoveEmpty({
566
+ ...connector.getAuthorizationHeaders(),
567
+ ...optHeaders != null ? optHeaders : {}
380
568
  });
381
- }
382
- for (const p in params) {
383
- if (apiItem.indexOf(p) === -1) {
384
- paramArray.push(`${p}=${params[p]}`);
385
- }
386
- }
387
- if (paramArray.length) {
388
- url = `${url}?${paramArray.join("&")}`;
389
- }
390
- }
391
- if (url) {
392
- return connector.makeQuery(url, params, {
393
- cacheName: name,
394
- ...requestOptions
569
+ return request(
570
+ template,
571
+ {
572
+ headers,
573
+ ...restOpt,
574
+ method
575
+ },
576
+ connector.cache
577
+ );
395
578
  });
396
- } else {
397
- throw new Error("Request URL is not set");
398
- }
399
- } else {
400
- return void 0;
579
+ };
580
+ result[method] = methodResp;
401
581
  }
582
+ return result;
402
583
  }
403
584
 
404
- function isError(error) {
405
- if (utils.isObject(error)) {
406
- return error.status_code && error.exception && error.title;
407
- }
408
- return false;
585
+ function isObject(val) {
586
+ return Object.prototype.toString.call(val) === "[object Object]";
409
587
  }
410
588
 
411
589
  let loadData;
@@ -529,54 +707,70 @@ let loadData;
529
707
  };
530
708
  }
531
709
 
532
- var __defProp = Object.defineProperty;
533
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
534
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
535
- let ID = 0;
536
- let REQUEST_ID = 0;
537
- class NgwConnector {
538
- constructor(options) {
539
- this.options = options;
540
- __publicField(this, "id", ID++);
541
- __publicField(this, "emitter", new events.EventEmitter());
542
- __publicField(this, "user");
543
- __publicField(this, "resources");
544
- __publicField(this, "cache");
545
- __publicField(this, "withCredentials");
546
- __publicField(this, "routeStr", "/api/component/pyramid/route");
547
- __publicField(this, "activeRequests", {});
548
- __publicField(this, "requestTransform");
549
- const exist = findConnector(options);
550
- this.cache = new Cache({ namespace: options.cacheId });
551
- if (exist) {
552
- return exist;
553
- } else {
554
- const { route, requestTransform, withCredentials } = this.options;
555
- if (route) {
556
- this.routeStr = route;
557
- }
558
- if (requestTransform) {
559
- this.requestTransform = requestTransform;
560
- }
561
- if (withCredentials !== void 0) {
562
- this.withCredentials = withCredentials;
563
- }
564
- this.resources = new ResourcesControl({
565
- connector: this,
566
- cacheId: options.cacheId
567
- });
568
- addConnector(this);
710
+ const CONNECTORS = [];
711
+ function addConnector(connector) {
712
+ CONNECTORS.push(connector);
713
+ }
714
+ function findConnector(options) {
715
+ return CONNECTORS.find((x) => {
716
+ if (x.options.baseUrl === options.baseUrl) {
717
+ if (options.auth) {
718
+ if (x.options.auth) {
719
+ return utils.objectDeepEqual(x.options.auth, options.auth);
720
+ }
721
+ } else {
722
+ return true;
723
+ }
569
724
  }
725
+ });
726
+ }
727
+ function removeConnector(connector) {
728
+ const index = CONNECTORS.indexOf(connector);
729
+ if (index !== -1) {
730
+ CONNECTORS.splice(index, 1);
570
731
  }
571
- static create(options) {
572
- return new this(options);
732
+ }
733
+
734
+ var __defProp$2 = Object.defineProperty;
735
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
736
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
737
+ let ID = 0;
738
+ let REQUEST_ID = 0;
739
+ class NgwConnector {
740
+ constructor(options) {
741
+ this.options = options;
742
+ __publicField$2(this, "id", ID++);
743
+ __publicField$2(this, "emitter", new events.EventEmitter());
744
+ __publicField$2(this, "user");
745
+ __publicField$2(this, "cache");
746
+ __publicField$2(this, "withCredentials");
747
+ __publicField$2(this, "client", `NextGIS-NGW-Connector/${pkg.version}`);
748
+ __publicField$2(this, "routeStr", "/api/component/pyramid/route");
749
+ __publicField$2(this, "activeRequests", {});
750
+ __publicField$2(this, "requestTransform");
751
+ const exist = findConnector(options);
752
+ this.cache = new Cache({ namespace: options.cacheId });
753
+ if (exist) {
754
+ return exist;
755
+ } else {
756
+ const { route: route2, requestTransform, withCredentials } = this.options;
757
+ if (route2) {
758
+ this.routeStr = route2;
759
+ }
760
+ if (requestTransform) {
761
+ this.requestTransform = requestTransform;
762
+ }
763
+ if (withCredentials !== void 0) {
764
+ this.withCredentials = withCredentials;
765
+ }
766
+ addConnector(this);
767
+ }
573
768
  }
574
769
  /**
575
770
  * Clear the cache.
576
771
  */
577
772
  clearCache() {
578
773
  this.cache.clean();
579
- this.resources.cache.clean();
580
774
  }
581
775
  setRequestTransform(requestTransform) {
582
776
  this.requestTransform = requestTransform;
@@ -598,13 +792,15 @@ class NgwConnector {
598
792
  * Can be used to check connection.
599
793
  * @example
600
794
  * ```javascript
601
- * const connector = new NgwConnector({baseUrl: 'https://demo.nextgis.com'});
795
+ * const connector = new NgwConnector({ baseUrl: 'https://demo.nextgis.com' });
602
796
  * connector.connect()
603
797
  * .then(() => console.log('Ok'))
604
798
  * .catch((er) => console.log('Connection problem', er));
605
799
  * ```
606
800
  */
607
- async connect() {
801
+ async connect({
802
+ signal
803
+ } = {}) {
608
804
  const auth = this.options.auth;
609
805
  if (auth) {
610
806
  const { login, password } = auth;
@@ -612,11 +808,13 @@ class NgwConnector {
612
808
  await this._login({ login, password });
613
809
  }
614
810
  }
615
- return await this.makeQuery(
616
- this.routeStr,
811
+ const routeUrl = `${this.routeStr}?client=${this.client}`;
812
+ return this.makeQuery(
813
+ routeUrl,
617
814
  {},
618
815
  {
619
816
  cacheName: "route",
817
+ signal,
620
818
  cache: true,
621
819
  cacheProps: {
622
820
  id: this.id,
@@ -636,64 +834,496 @@ class NgwConnector {
636
834
  return this._login(credentials, options);
637
835
  }
638
836
  /**
639
- * Disconnecting a user. Aborting all current requests
837
+ * Disconnecting a user. Aborting all current requests
838
+ */
839
+ logout() {
840
+ this.abort();
841
+ removeConnector(this);
842
+ this.options.auth = void 0;
843
+ this.user = void 0;
844
+ this.clearCache();
845
+ this.emitter.emit("logout");
846
+ }
847
+ async getUserInfo(credentials, options) {
848
+ if (this.user && this.user.id) {
849
+ return this.user;
850
+ }
851
+ if (credentials) {
852
+ this.options.auth = credentials;
853
+ }
854
+ const options_ = {
855
+ headers: this.getAuthorizationHeaders(credentials),
856
+ cache: true,
857
+ ...options
858
+ };
859
+ return this.makeQuery(
860
+ "/api/component/auth/current_user",
861
+ {},
862
+ options_
863
+ );
864
+ }
865
+ /**
866
+ * Obtaining the required Headers for authentication of requests in the NGW.
867
+ */
868
+ getAuthorizationHeaders(credentials) {
869
+ const client = this.makeClientId(credentials);
870
+ if (client) {
871
+ return {
872
+ Authorization: `Basic ${client}`
873
+ };
874
+ }
875
+ return {};
876
+ }
877
+ makeClientId(credentials) {
878
+ credentials = credentials || this.options.auth;
879
+ if (credentials) {
880
+ const { login, password } = credentials;
881
+ const encodedStr = [login, password].map(encodeURIComponent).join(":");
882
+ {
883
+ return Buffer.from(encodedStr).toString("base64");
884
+ }
885
+ }
886
+ }
887
+ /** Stop all api requests */
888
+ abort() {
889
+ for (const abortController of Object.values(this.activeRequests)) {
890
+ abortController.abort();
891
+ }
892
+ this.activeRequests = {};
893
+ }
894
+ getActiveApiRequests() {
895
+ return { ...this.activeRequests };
896
+ }
897
+ route(name, ...rest) {
898
+ return route(name, this, ...rest);
899
+ }
900
+ /**
901
+ * Send request to NGW.
902
+ * @param url - URL address to NGW
903
+ * @param params - Query params
904
+ * @param options - Request options
905
+ */
906
+ async makeQuery(url, params, options = {}) {
907
+ var _a;
908
+ url = (this.options.baseUrl ? this.options.baseUrl : "") + url;
909
+ if (!url) {
910
+ throw new Error("Empty `url` not allowed");
911
+ }
912
+ if (params) {
913
+ const { paramList, ...restParams } = params;
914
+ url = template(url, restParams);
915
+ }
916
+ url = encodeURI(utils.fixUrlStr(url));
917
+ options = { withCredentials: this.withCredentials, ...options };
918
+ const {
919
+ cache,
920
+ signal: externalSignal,
921
+ method = "GET",
922
+ headers,
923
+ cacheName,
924
+ cacheProps,
925
+ responseType,
926
+ withCredentials
927
+ } = options;
928
+ const internalAbortController = new AbortController();
929
+ const internalSignal = internalAbortController.signal;
930
+ if (externalSignal) {
931
+ if (externalSignal.aborted) {
932
+ throw new AbortError();
933
+ }
934
+ externalSignal.addEventListener("abort", () => {
935
+ internalAbortController.abort();
936
+ });
937
+ }
938
+ options.signal = internalSignal;
939
+ const createPromise = async () => {
940
+ const id = REQUEST_ID++;
941
+ this.activeRequests[id] = internalAbortController;
942
+ try {
943
+ return this._loadData(url, options);
944
+ } finally {
945
+ this._cleanActiveRequest(id);
946
+ }
947
+ };
948
+ if (method === "GET") {
949
+ const cacheOptions = cacheProps ? cacheProps : {
950
+ ...utils.objectRemoveEmpty({
951
+ headers,
952
+ withCredentials,
953
+ responseType,
954
+ baseUrl: this.options.baseUrl,
955
+ userId: (_a = this.user) == null ? void 0 : _a.id
956
+ }),
957
+ params
958
+ };
959
+ return this.cache.add(cacheName || url, createPromise, {
960
+ props: cacheOptions,
961
+ expirationTime: cache ? void 0 : 500
962
+ });
963
+ }
964
+ return createPromise();
965
+ }
966
+ _loadData(url, options) {
967
+ options.responseType = options.responseType || "json";
968
+ return new Promise((resolve, reject) => {
969
+ var _a;
970
+ if (this.user) {
971
+ options = options || {};
972
+ options.headers = {
973
+ ...this.getAuthorizationHeaders(),
974
+ ...options.headers
975
+ };
976
+ }
977
+ if (this.requestTransform) {
978
+ const [transUrl, transOptions] = this.requestTransform(url, options);
979
+ url = transUrl;
980
+ options = transOptions;
981
+ }
982
+ let runOnAbort = void 0;
983
+ loadData(url, resolve, options, reject, (handler) => {
984
+ runOnAbort = handler;
985
+ });
986
+ (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
987
+ if (runOnAbort !== void 0) {
988
+ runOnAbort();
989
+ }
990
+ reject(new AbortError());
991
+ });
992
+ }).catch((httpError) => {
993
+ if (httpError.name !== "AbortError") {
994
+ {
995
+ console.warn("DEV WARN", httpError);
996
+ }
997
+ const er = this._handleHttpError(httpError);
998
+ if (er) {
999
+ throw er;
1000
+ }
1001
+ }
1002
+ throw httpError;
1003
+ });
1004
+ }
1005
+ async _login(credentials, options) {
1006
+ try {
1007
+ const data = await this.getUserInfo(credentials, options);
1008
+ this.user = data;
1009
+ this.emitter.emit("login", data);
1010
+ return data;
1011
+ } catch (er) {
1012
+ this.emitter.emit("login:error", er);
1013
+ throw er;
1014
+ }
1015
+ }
1016
+ _cleanActiveRequest(requestId) {
1017
+ delete this.activeRequests[requestId];
1018
+ }
1019
+ _handleHttpError(er) {
1020
+ if (er) {
1021
+ if (er instanceof NgwError) {
1022
+ if (er.exception === "nextgisweb.resource.exception.ResourceNotFound") {
1023
+ throw new ResourceNotFoundError(er);
1024
+ } else if (er.exception === "nextgisweb.core.exception.InsufficientPermissions") {
1025
+ throw new InsufficientPermissionsError(er);
1026
+ }
1027
+ }
1028
+ }
1029
+ return er;
1030
+ }
1031
+ }
1032
+ __publicField$2(NgwConnector, "errors", errors);
1033
+
1034
+ function resourceCompare(res1, res2) {
1035
+ return utils.objectDeepEqual(res1, res2);
1036
+ }
1037
+
1038
+ const exclude = ["description"];
1039
+ function resourceToQuery(resource, prefix = "") {
1040
+ prefix = prefix ? prefix + "__" : "";
1041
+ const query = {};
1042
+ for (const [key, value] of Object.entries(resource)) {
1043
+ if (exclude.indexOf(key) === -1) {
1044
+ if (isObject(value)) {
1045
+ if (key === "owner_user") {
1046
+ const children = resourceToQuery(
1047
+ value,
1048
+ key
1049
+ );
1050
+ Object.assign(query, children);
1051
+ } else if (key === "parent" && "id" in value) {
1052
+ query.parent_id = value.id;
1053
+ }
1054
+ } else if (utils.defined(value)) {
1055
+ query[prefix + key] = value;
1056
+ }
1057
+ }
1058
+ }
1059
+ return query;
1060
+ }
1061
+
1062
+ var __defProp$1 = Object.defineProperty;
1063
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1064
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1065
+ class ResourcesControl {
1066
+ constructor({
1067
+ connector,
1068
+ cacheId
1069
+ }) {
1070
+ __publicField$1(this, "cache");
1071
+ __publicField$1(this, "connector");
1072
+ this.connector = connector;
1073
+ this.cache = new Cache({ namespace: cacheId });
1074
+ }
1075
+ // -------------------------------------------------------------------------
1076
+ // Resource Methods
1077
+ // -------------------------------------------------------------------------
1078
+ /**
1079
+ * Receive resource from NGW by id, keyname or search-object parameter.
1080
+ * @param resource - Resource id, keyname or search-object
1081
+ *
1082
+ * @remarks
1083
+ * Fetching resource would be cached to speed up next call
1084
+ */
1085
+ getOne(resource, requestOptions) {
1086
+ const opt = { ...requestOptions };
1087
+ if (typeof resource === "string") ; else if (typeof resource === "number") ; else if (isObject(resource)) {
1088
+ if (resource.id !== void 0) {
1089
+ resource.id;
1090
+ } else {
1091
+ if (resource.keyname) {
1092
+ resource.keyname;
1093
+ }
1094
+ if (resource.display_name) {
1095
+ resource.display_name;
1096
+ }
1097
+ }
1098
+ }
1099
+ if (typeof resource === "string") {
1100
+ return this._fetchResourceBy({ keyname: resource }, opt);
1101
+ } else if (typeof resource === "number") {
1102
+ return this._fetchResourceById(resource, opt);
1103
+ } else if (isObject(resource)) {
1104
+ return this._fetchResourceBy(resource, opt);
1105
+ }
1106
+ return Promise.resolve(void 0);
1107
+ }
1108
+ getOneOrFail(resource, requestOptions) {
1109
+ return this.getOne(resource, requestOptions).then((res) => {
1110
+ if (res) {
1111
+ return res;
1112
+ }
1113
+ throw new ResourceNotFoundError();
1114
+ });
1115
+ }
1116
+ /**
1117
+ * A fast way to retrieve resource ID for any resource definition.
1118
+ * @param resource - Any available resource definition
1119
+ *
1120
+ * @remarks
1121
+ * There are situations when exactly the resource id is needed
1122
+ * (for example, to compose the correct request to the api)
1123
+ * then this method will come in handy to facilitate the extraction of the identifier
1124
+ * if the resource is specified through a keyname or other parameters.
1125
+ */
1126
+ getId(resource, requestOptions) {
1127
+ if (typeof resource === "number") {
1128
+ return Promise.resolve(resource);
1129
+ } else if (typeof resource === "string" || isObject(resource)) {
1130
+ return this.getOne(resource, requestOptions).then((res) => {
1131
+ if (res) {
1132
+ return res.resource.id;
1133
+ }
1134
+ });
1135
+ }
1136
+ return Promise.resolve(void 0);
1137
+ }
1138
+ /**
1139
+ * A fast way to retrieve resource ID for any resource definition.
1140
+ * @param resource - Any available resource definition
1141
+ *
1142
+ * @remarks
1143
+ * Similar with {@link NgwConnector.getResourceId | getResourceId} but rise error if resource is not exist.
1144
+ * To not make one more checks if the resource is definitely exists
1145
+ */
1146
+ getIdOrFail(resource, requestOptions) {
1147
+ return this.getId(resource, requestOptions).then((resp) => {
1148
+ if (resp === void 0) {
1149
+ throw new Error();
1150
+ }
1151
+ return resp;
1152
+ });
1153
+ }
1154
+ getMany(resource, requestOptions) {
1155
+ return this._resourceCacheFilter(resource).then((items) => {
1156
+ if (!items.length) {
1157
+ const query = {};
1158
+ if (resource.keyname) {
1159
+ query.keyname = resource.keyname;
1160
+ } else {
1161
+ Object.assign(query, resourceToQuery(resource));
1162
+ }
1163
+ return this.connector.route("resource.search").get({
1164
+ ...requestOptions,
1165
+ query: {
1166
+ serialization: "full",
1167
+ ...query
1168
+ }
1169
+ }).then((resources) => {
1170
+ if ((requestOptions == null ? void 0 : requestOptions.cache) && resources) {
1171
+ for (const x of resources) {
1172
+ this.cache.add("resource.item", Promise.resolve(x), {
1173
+ id: x.resource.id
1174
+ });
1175
+ }
1176
+ }
1177
+ return resources;
1178
+ });
1179
+ }
1180
+ return items;
1181
+ });
1182
+ }
1183
+ getParent(resource, requestOptions) {
1184
+ return this.getOne(resource, requestOptions).then((child) => {
1185
+ var _a, _b;
1186
+ if ((_b = (_a = child == null ? void 0 : child.resource) == null ? void 0 : _a.parent) == null ? void 0 : _b.id) {
1187
+ return this.getOne(child.resource.parent.id, requestOptions);
1188
+ }
1189
+ return Promise.resolve(void 0);
1190
+ });
1191
+ }
1192
+ getChildrenOf(resource, requestOptions) {
1193
+ return this.getIdOrFail(resource).then(
1194
+ (parent) => this._getChildrenOf(parent, requestOptions)
1195
+ );
1196
+ }
1197
+ update(resource, data) {
1198
+ return this.getId(resource).then((id) => {
1199
+ if (id !== void 0) {
1200
+ return this.connector.put("resource.item", { data }, { id });
1201
+ }
1202
+ });
1203
+ }
1204
+ /**
1205
+ * Fast way to delete resource from NGW and clean cache.
1206
+ * @param resource - Resource definition
640
1207
  */
641
- logout() {
642
- this.abort();
643
- removeConnector(this);
644
- this.options.auth = void 0;
645
- this.user = void 0;
646
- this.clearCache();
647
- this.emitter.emit("logout");
1208
+ delete(resource) {
1209
+ return this.getId(resource).then((id) => {
1210
+ if (id !== void 0) {
1211
+ return this.connector.delete("resource.item", null, { id }).then(() => {
1212
+ this._cleanResourceItemCache(id);
1213
+ return void 0;
1214
+ });
1215
+ }
1216
+ });
648
1217
  }
649
- async getUserInfo(credentials, options) {
650
- if (this.user && this.user.id) {
651
- return this.user;
1218
+ async _getChildrenOf(parentDef, requestOptions, _items = []) {
1219
+ let parent = void 0;
1220
+ if (typeof parentDef === "string") {
1221
+ parent = await this.getId(parentDef, requestOptions);
1222
+ } else if (typeof parentDef === "object") {
1223
+ parent = parentDef.id;
1224
+ } else {
1225
+ parent = parentDef;
652
1226
  }
653
- if (credentials) {
654
- this.options.auth = credentials;
1227
+ const items = await this.connector.route("resource.collection").get({
1228
+ ...requestOptions,
1229
+ query: {
1230
+ parent
1231
+ }
1232
+ });
1233
+ const recursivePromises = [];
1234
+ for (const item of items) {
1235
+ if (requestOptions == null ? void 0 : requestOptions.cache) {
1236
+ this.cache.add("resource.item", Promise.resolve(item), {
1237
+ id: item.resource.id
1238
+ });
1239
+ }
1240
+ _items.push(item);
1241
+ if ((requestOptions == null ? void 0 : requestOptions.recursive) && item.resource.children) {
1242
+ recursivePromises.push(
1243
+ this._getChildrenOf(item.resource.id, requestOptions, _items)
1244
+ );
1245
+ }
655
1246
  }
656
- const options_ = {
657
- headers: this.getAuthorizationHeaders(credentials),
658
- ...options
659
- };
660
- return this.makeQuery(
661
- "/api/component/auth/current_user",
662
- {},
663
- options_
664
- );
665
- }
666
- /**
667
- * Obtaining the required Headers for authentication of requests in the NGW.
668
- */
669
- getAuthorizationHeaders(credentials) {
670
- const client = this.makeClientId(credentials);
671
- if (client) {
672
- return {
673
- Authorization: `Basic ${client}`
674
- };
1247
+ if (recursivePromises.length) {
1248
+ return Promise.all(recursivePromises).then(() => {
1249
+ return _items;
1250
+ });
675
1251
  }
1252
+ return _items;
676
1253
  }
677
- makeClientId(credentials) {
678
- credentials = credentials || this.options.auth;
679
- if (credentials) {
680
- const { login, password } = credentials;
681
- const str = `${login}:${password}`;
682
- const encodedStr = encodeURIComponent(str);
683
- {
684
- return Buffer.from(encodedStr).toString("base64");
1254
+ async _cleanResourceItemCache(id) {
1255
+ var _a;
1256
+ const all = this.cache.all();
1257
+ const toDelete = [];
1258
+ for (const c of all) {
1259
+ const cid = (_a = c.props) == null ? void 0 : _a.id;
1260
+ if (["resource.item", "resource"].includes(c.key) && cid !== void 0) {
1261
+ if (typeof cid === "number") {
1262
+ if (cid === id) {
1263
+ toDelete.push(c);
1264
+ }
1265
+ } else {
1266
+ const rid = await this.getId(cid);
1267
+ if (rid === id) {
1268
+ toDelete.push(c);
1269
+ }
1270
+ }
685
1271
  }
686
1272
  }
687
- }
688
- /** Stop all api requests */
689
- abort() {
690
- for (const abortController of Object.values(this.activeRequests)) {
691
- abortController.abort();
1273
+ for (const d of toDelete) {
1274
+ this.cache.delete(d);
692
1275
  }
693
- this.activeRequests = {};
694
1276
  }
695
- getActiveApiRequests() {
696
- return { ...this.activeRequests };
1277
+ _fetchResourceById(id, requestOptions) {
1278
+ return this.connector.route("resource.item", { id }).get(requestOptions);
1279
+ }
1280
+ _fetchResourceBy(resource, requestOptions) {
1281
+ return this.getMany(resource, requestOptions).then((resources) => {
1282
+ return resources[0];
1283
+ });
1284
+ }
1285
+ _resourceCacheFilter(resource) {
1286
+ return Promise.all(this.cache.matchAll("resource.item")).then(
1287
+ (resources) => {
1288
+ const items = [];
1289
+ resources.filter((x) => {
1290
+ if (x) {
1291
+ if (resource.keyname && x.resource.keyname) {
1292
+ return resource.keyname === x.resource.keyname;
1293
+ }
1294
+ if (utils.defined(resource.id) && utils.defined(x.resource.id)) {
1295
+ return resource.id === x.resource.id;
1296
+ }
1297
+ return resourceCompare(resource, x.resource);
1298
+ }
1299
+ });
1300
+ return items;
1301
+ }
1302
+ );
1303
+ }
1304
+ }
1305
+
1306
+ var __defProp = Object.defineProperty;
1307
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1308
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
1309
+ class NgwConnectorExtended extends NgwConnector {
1310
+ constructor(options) {
1311
+ super(options);
1312
+ __publicField(this, "resources");
1313
+ this.resources = new ResourcesControl({
1314
+ connector: this,
1315
+ cacheId: options.cacheId
1316
+ });
1317
+ }
1318
+ static create(options) {
1319
+ return new this(options);
1320
+ }
1321
+ /**
1322
+ * Clear the cache.
1323
+ */
1324
+ clearCache() {
1325
+ super.clearCache();
1326
+ this.resources.cache.clean();
697
1327
  }
698
1328
  /**
699
1329
  * Send request to NGW api router.
@@ -812,74 +1442,6 @@ class NgwConnector {
812
1442
  options
813
1443
  );
814
1444
  }
815
- /**
816
- * Send request to NGW.
817
- * @param url - URL address to NGW
818
- * @param params - Query params
819
- * @param options - Request options
820
- */
821
- async makeQuery(url, params, options = {}) {
822
- var _a;
823
- url = (this.options.baseUrl ? this.options.baseUrl : "") + url;
824
- if (!url) {
825
- throw new Error("Empty `url` not allowed");
826
- }
827
- if (params) {
828
- const { paramList, ...restParams } = params;
829
- url = template(url, restParams);
830
- }
831
- url = encodeURI(utils.fixUrlStr(url));
832
- options = { withCredentials: this.withCredentials, ...options };
833
- const {
834
- cache,
835
- signal: externalSignal,
836
- method = "GET",
837
- headers,
838
- cacheName,
839
- cacheProps,
840
- responseType,
841
- withCredentials
842
- } = options;
843
- const internalAbortController = new AbortController();
844
- const internalSignal = internalAbortController.signal;
845
- if (externalSignal) {
846
- if (externalSignal.aborted) {
847
- throw new AbortError();
848
- }
849
- externalSignal.addEventListener("abort", () => {
850
- internalAbortController.abort();
851
- });
852
- }
853
- options.signal = internalSignal;
854
- const createPromise = async () => {
855
- const id = REQUEST_ID++;
856
- this.activeRequests[id] = internalAbortController;
857
- try {
858
- return this._loadData(url, options);
859
- } finally {
860
- this._cleanActiveRequest(id);
861
- }
862
- };
863
- if (cache && method === "GET") {
864
- const cacheOptions = cacheProps ? cacheProps : {
865
- ...utils.objectRemoveEmpty({
866
- headers,
867
- withCredentials,
868
- responseType,
869
- baseUrl: this.options.baseUrl,
870
- userId: (_a = this.user) == null ? void 0 : _a.id
871
- }),
872
- params
873
- };
874
- return this.cache.add(
875
- cacheName || url,
876
- createPromise,
877
- cacheOptions,
878
- false
879
- );
880
- }
881
- return createPromise();
882
- }
883
1445
  // -------------------------------------------------------------------------
884
1446
  // Resource Methods
885
1447
  // -------------------------------------------------------------------------
@@ -955,79 +1517,7 @@ class NgwConnector {
955
1517
  deleteResource(resource) {
956
1518
  return this.resources.delete(resource);
957
1519
  }
958
- /**
959
- * @internal
960
- */
961
- _loadData(url, options) {
962
- options.responseType = options.responseType || "json";
963
- return new Promise((resolve, reject) => {
964
- var _a;
965
- if (this.user) {
966
- options = options || {};
967
- options.headers = {
968
- ...this.getAuthorizationHeaders(),
969
- ...options.headers
970
- };
971
- }
972
- if (this.requestTransform) {
973
- const [transUrl, transOptions] = this.requestTransform(url, options);
974
- url = transUrl;
975
- options = transOptions;
976
- }
977
- let runOnAbort = void 0;
978
- loadData(url, resolve, options, reject, (handler) => {
979
- runOnAbort = handler;
980
- });
981
- (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
982
- if (runOnAbort !== void 0) {
983
- runOnAbort();
984
- }
985
- reject(new AbortError());
986
- });
987
- }).catch((httpError) => {
988
- if (httpError.name !== "AbortError") {
989
- {
990
- console.warn("DEV WARN", httpError);
991
- }
992
- const er = this._handleHttpError(httpError);
993
- if (er) {
994
- throw er;
995
- }
996
- }
997
- throw httpError;
998
- });
999
- }
1000
- async _login(credentials, options) {
1001
- try {
1002
- const data = await this.getUserInfo(credentials, options);
1003
- this.user = data;
1004
- this.emitter.emit("login", data);
1005
- return data;
1006
- } catch (er) {
1007
- this.emitter.emit("login:error", er);
1008
- throw er;
1009
- }
1010
- }
1011
- _cleanActiveRequest(requestId) {
1012
- delete this.activeRequests[requestId];
1013
- }
1014
- _handleHttpError(er) {
1015
- if (er) {
1016
- if (er instanceof NgwError) {
1017
- if (er.exception === "nextgisweb.resource.exception.ResourceNotFound") {
1018
- throw new ResourceNotFoundError(er);
1019
- } else if (er.exception === "nextgisweb.core.exception.InsufficientPermissions") {
1020
- throw new InsufficientPermissionsError(er);
1021
- }
1022
- }
1023
- }
1024
- return er;
1025
- }
1026
1520
  }
1027
- __publicField(NgwConnector, "errors", {
1028
- NgwError,
1029
- ResourceNotFoundError
1030
- });
1031
1521
 
1032
- module.exports = NgwConnector;
1522
+ module.exports = NgwConnectorExtended;
1033
1523
  //# sourceMappingURL=ngw-connector.cjs.js.map