@nextgis/ngw-connector 3.0.0-alpha.2 → 3.0.0-alpha.20

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