@carto/api-client 0.0.1-0

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.
@@ -0,0 +1,1202 @@
1
+ const CLIENT_ID = 'carto-api-client';
2
+ /** @internalRemarks Source: @carto/constants */
3
+ exports.MapType = void 0;
4
+ (function (MapType) {
5
+ MapType["TABLE"] = "table";
6
+ MapType["QUERY"] = "query";
7
+ MapType["TILESET"] = "tileset";
8
+ })(exports.MapType || (exports.MapType = {}));
9
+ /** @internalRemarks Source: @carto/constants */
10
+ exports.ApiVersion = void 0;
11
+ (function (ApiVersion) {
12
+ ApiVersion["V1"] = "v1";
13
+ ApiVersion["V2"] = "v2";
14
+ ApiVersion["V3"] = "v3";
15
+ })(exports.ApiVersion || (exports.ApiVersion = {}));
16
+ /** @internalRemarks Source: @carto/react-core */
17
+ exports.GroupDateType = void 0;
18
+ (function (GroupDateType) {
19
+ GroupDateType["YEARS"] = "year";
20
+ GroupDateType["MONTHS"] = "month";
21
+ GroupDateType["WEEKS"] = "week";
22
+ GroupDateType["DAYS"] = "day";
23
+ GroupDateType["HOURS"] = "hour";
24
+ GroupDateType["MINUTES"] = "minute";
25
+ GroupDateType["SECONDS"] = "second";
26
+ })(exports.GroupDateType || (exports.GroupDateType = {}));
27
+ /** @internalRemarks Source: @carto/react-api, @deck.gl/carto */
28
+ exports.FilterType = void 0;
29
+ (function (FilterType) {
30
+ FilterType["IN"] = "in";
31
+ /** [a, b] both are included. */
32
+ FilterType["BETWEEN"] = "between";
33
+ /** [a, b) a is included, b is not. */
34
+ FilterType["CLOSED_OPEN"] = "closed_open";
35
+ FilterType["TIME"] = "time";
36
+ FilterType["STRING_SEARCH"] = "stringSearch";
37
+ })(exports.FilterType || (exports.FilterType = {}));
38
+
39
+ /**
40
+ * Default client
41
+ * @internalRemarks Source: @carto/react-core
42
+ */
43
+ let client = CLIENT_ID;
44
+ /** @internalRemarks Source: @carto/react-core */
45
+ function getClient() {
46
+ return client;
47
+ }
48
+ /** @internalRemarks Source: @carto/react-core */
49
+ function setClient(c) {
50
+ client = c;
51
+ }
52
+
53
+ /**
54
+ * Threshold to use GET requests, vs POST
55
+ * @internalRemarks Source: @carto/constants
56
+ * @internal
57
+ */
58
+ const REQUEST_GET_MAX_URL_LENGTH = 2048;
59
+ /**
60
+ * @internalRemarks Source: @carto/constants
61
+ * @internal
62
+ */
63
+ const DEFAULT_API_BASE_URL$1 = 'https://gcp-us-east1.api.carto.com';
64
+ /**
65
+ * @internalRemarks Source: @carto/react-api
66
+ * @internal
67
+ */
68
+ const DEFAULT_GEO_COLUMN = 'geom';
69
+
70
+ const FILTER_TYPES = new Set(Object.values(exports.FilterType));
71
+ const isFilterType = type => FILTER_TYPES.has(type);
72
+ /**
73
+ * @privateRemarks Source: @carto/react-widgets
74
+ * @internal
75
+ */
76
+ function getApplicableFilters(owner, filters) {
77
+ if (!filters) return {};
78
+ const applicableFilters = {};
79
+ for (const column in filters) {
80
+ for (const type in filters[column]) {
81
+ if (!isFilterType(type)) continue;
82
+ const filter = filters[column][type];
83
+ if (filter && filter.owner !== owner) {
84
+ applicableFilters[column] ||= {};
85
+ applicableFilters[column][type] = filter;
86
+ }
87
+ }
88
+ }
89
+ return applicableFilters;
90
+ }
91
+ /**
92
+ * Due to each data warehouse having its own behavior with columns,
93
+ * we need to normalize them and transform every key to lowercase.
94
+ *
95
+ * @internalRemarks Source: @carto/react-widgets
96
+ * @internal
97
+ */
98
+ function normalizeObjectKeys(el) {
99
+ if (Array.isArray(el)) {
100
+ return el.map(value => normalizeObjectKeys(value));
101
+ } else if (typeof el !== 'object') {
102
+ return el;
103
+ }
104
+ return Object.entries(el).reduce((acc, _ref) => {
105
+ let [key, value] = _ref;
106
+ acc[key.toLowerCase()] = typeof value === 'object' && value ? normalizeObjectKeys(value) : value;
107
+ return acc;
108
+ }, {});
109
+ }
110
+ /** @internalRemarks Source: @carto/react-core */
111
+ function assert(condition, message) {
112
+ if (!condition) {
113
+ throw new Error(message);
114
+ }
115
+ }
116
+ /**
117
+ * @internalRemarks Source: @carto/react-core
118
+ * @internal
119
+ */
120
+ class InvalidColumnError extends Error {
121
+ constructor(message) {
122
+ super(`${InvalidColumnError.NAME}: ${message}`);
123
+ this.name = InvalidColumnError.NAME;
124
+ }
125
+ static is(error) {
126
+ return error instanceof InvalidColumnError || error.message?.includes(InvalidColumnError.NAME);
127
+ }
128
+ }
129
+ InvalidColumnError.NAME = 'InvalidColumnError';
130
+
131
+ /**
132
+ * Return more descriptive error from API
133
+ * @internalRemarks Source: @carto/react-api
134
+ */
135
+
136
+ /** @internalRemarks Source: @carto/react-api */
137
+
138
+ function _catch(body, recover) {
139
+ try {
140
+ var result = body();
141
+ } catch (e) {
142
+ return recover(e);
143
+ }
144
+ if (result && result.then) {
145
+ return result.then(void 0, recover);
146
+ }
147
+ return result;
148
+ }
149
+ const makeCall = function (_ref2) {
150
+ let {
151
+ url,
152
+ credentials,
153
+ opts
154
+ } = _ref2;
155
+ try {
156
+ let _exit;
157
+ function _temp2(_result) {
158
+ if (_exit) ;
159
+ if (!response.ok) {
160
+ dealWithApiError({
161
+ response,
162
+ data
163
+ });
164
+ }
165
+ return data;
166
+ }
167
+ let response;
168
+ let data;
169
+ const isPost = opts?.method === 'POST';
170
+ const _temp = _catch(function () {
171
+ return Promise.resolve(fetch(url.toString(), {
172
+ headers: {
173
+ Authorization: `Bearer ${credentials.accessToken}`,
174
+ ...(isPost ? {
175
+ 'Content-Type': 'application/json'
176
+ } : {})
177
+ },
178
+ ...(isPost ? {
179
+ method: opts?.method,
180
+ body: opts?.body
181
+ } : {}),
182
+ signal: opts?.abortController?.signal,
183
+ ...opts?.otherOptions
184
+ })).then(function (_fetch) {
185
+ response = _fetch;
186
+ return Promise.resolve(response.json()).then(function (_response$json) {
187
+ data = _response$json;
188
+ });
189
+ });
190
+ }, function (error) {
191
+ if (error.name === 'AbortError') throw error;
192
+ throw new Error(`Failed request: ${error}`);
193
+ });
194
+ return Promise.resolve(_temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp));
195
+ } catch (e) {
196
+ return Promise.reject(e);
197
+ }
198
+ };
199
+ function dealWithApiError(_ref) {
200
+ let {
201
+ response,
202
+ data
203
+ } = _ref;
204
+ if (data.error === 'Column not found') {
205
+ throw new InvalidColumnError(`${data.error} ${data.column_name}`);
206
+ }
207
+ if (data.error?.includes('Missing columns')) {
208
+ throw new InvalidColumnError(data.error);
209
+ }
210
+ switch (response.status) {
211
+ case 401:
212
+ throw new Error('Unauthorized access. Invalid credentials');
213
+ case 403:
214
+ throw new Error('Forbidden access to the requested data');
215
+ default:
216
+ const msg = data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify(data?.hint || data.error?.[0]);
217
+ throw new Error(msg);
218
+ }
219
+ }
220
+ /** @internalRemarks Source: @carto/react-api */
221
+ function checkCredentials(credentials) {
222
+ if (!credentials || !credentials.apiBaseUrl || !credentials.accessToken) {
223
+ throw new Error('Missing or bad credentials provided');
224
+ }
225
+ }
226
+
227
+ /** @internalRemarks Source: @carto/react-api */
228
+ const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'timeseries', 'range', 'scatterplot', 'table'];
229
+ /**
230
+ * Execute a SQL model request.
231
+ * @internalRemarks Source: @carto/react-api
232
+ */
233
+ function executeModel(props) {
234
+ assert(props.source, 'executeModel: missing source');
235
+ assert(props.model, 'executeModel: missing model');
236
+ assert(props.params, 'executeModel: missing params');
237
+ assert(AVAILABLE_MODELS.indexOf(props.model) !== -1, `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
238
+ const {
239
+ source,
240
+ model,
241
+ params,
242
+ spatialFilter,
243
+ opts
244
+ } = props;
245
+ checkCredentials(source.credentials);
246
+ assert(source.credentials.apiVersion === exports.ApiVersion.V3, 'SQL Model API is a feature only available in CARTO 3.');
247
+ assert(source.type !== exports.MapType.TILESET, 'executeModel: Tileset not supported');
248
+ let url = `${source.credentials.apiBaseUrl}/v3/sql/${source.connection}/model/${model}`;
249
+ const {
250
+ filters,
251
+ filtersLogicalOperator = 'and',
252
+ data,
253
+ type
254
+ } = source;
255
+ const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
256
+ const queryParams = {
257
+ type,
258
+ client: getClient(),
259
+ source: data,
260
+ params: JSON.stringify(params),
261
+ queryParameters,
262
+ filters: JSON.stringify(filters),
263
+ filtersLogicalOperator
264
+ };
265
+ // API supports multiple filters, we apply it only to geoColumn
266
+ const spatialFilters = spatialFilter ? {
267
+ [source.geoColumn ? source.geoColumn : DEFAULT_GEO_COLUMN]: spatialFilter
268
+ } : undefined;
269
+ if (spatialFilters) {
270
+ queryParams.spatialFilters = JSON.stringify(spatialFilters);
271
+ }
272
+ const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
273
+ const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
274
+ if (isGet) {
275
+ url = urlWithSearchParams;
276
+ } else {
277
+ // undo the JSON.stringify, @TODO find a better pattern
278
+ queryParams.params = params;
279
+ queryParams.filters = filters;
280
+ queryParams.queryParameters = source.queryParameters;
281
+ if (spatialFilters) {
282
+ queryParams.spatialFilters = spatialFilters;
283
+ }
284
+ }
285
+ return makeCall({
286
+ url,
287
+ credentials: source.credentials,
288
+ opts: {
289
+ ...opts,
290
+ method: isGet ? 'GET' : 'POST',
291
+ ...(!isGet && {
292
+ body: JSON.stringify(queryParams)
293
+ })
294
+ }
295
+ });
296
+ }
297
+
298
+ class WidgetBaseSource {
299
+ constructor(props) {
300
+ this.props = void 0;
301
+ this.credentials = void 0;
302
+ this.connectionName = void 0;
303
+ this.props = {
304
+ ...WidgetBaseSource.defaultProps,
305
+ ...props
306
+ };
307
+ this.connectionName = props.connectionName;
308
+ this.credentials = {
309
+ apiVersion: props.apiVersion || exports.ApiVersion.V3,
310
+ apiBaseUrl: props.apiBaseUrl || DEFAULT_API_BASE_URL$1,
311
+ clientId: props.clientId || getClient(),
312
+ accessToken: props.accessToken,
313
+ geoColumn: props.geoColumn || DEFAULT_GEO_COLUMN
314
+ };
315
+ }
316
+ getSource(owner) {
317
+ return {
318
+ ...this.props,
319
+ credentials: this.credentials,
320
+ connection: this.connectionName,
321
+ filters: getApplicableFilters(owner, this.props.filters)
322
+ };
323
+ }
324
+ getFormula(props) {
325
+ try {
326
+ const _this = this;
327
+ const {
328
+ filterOwner,
329
+ spatialFilter,
330
+ abortController,
331
+ operationExp,
332
+ ...params
333
+ } = props;
334
+ const {
335
+ column,
336
+ operation
337
+ } = params;
338
+ return Promise.resolve(executeModel({
339
+ model: 'formula',
340
+ source: _this.getSource(filterOwner),
341
+ spatialFilter,
342
+ params: {
343
+ column: column ?? '*',
344
+ operation,
345
+ operationExp
346
+ },
347
+ opts: {
348
+ abortController
349
+ }
350
+ }).then(res => normalizeObjectKeys(res.rows[0])));
351
+ } catch (e) {
352
+ return Promise.reject(e);
353
+ }
354
+ }
355
+ getCategories(props) {
356
+ try {
357
+ const _this2 = this;
358
+ const {
359
+ filterOwner,
360
+ spatialFilter,
361
+ abortController,
362
+ ...params
363
+ } = props;
364
+ const {
365
+ column,
366
+ operation,
367
+ operationColumn
368
+ } = params;
369
+ return Promise.resolve(executeModel({
370
+ model: 'category',
371
+ source: _this2.getSource(filterOwner),
372
+ spatialFilter,
373
+ params: {
374
+ column,
375
+ operation,
376
+ operationColumn: operationColumn || column
377
+ },
378
+ opts: {
379
+ abortController
380
+ }
381
+ }).then(res => normalizeObjectKeys(res.rows)));
382
+ } catch (e) {
383
+ return Promise.reject(e);
384
+ }
385
+ }
386
+ getRange(props) {
387
+ try {
388
+ const _this3 = this;
389
+ const {
390
+ filterOwner,
391
+ spatialFilter,
392
+ abortController,
393
+ ...params
394
+ } = props;
395
+ const {
396
+ column
397
+ } = params;
398
+ return Promise.resolve(executeModel({
399
+ model: 'range',
400
+ source: _this3.getSource(filterOwner),
401
+ spatialFilter,
402
+ params: {
403
+ column
404
+ },
405
+ opts: {
406
+ abortController
407
+ }
408
+ }).then(res => normalizeObjectKeys(res.rows[0])));
409
+ } catch (e) {
410
+ return Promise.reject(e);
411
+ }
412
+ }
413
+ getTable(props) {
414
+ try {
415
+ const _this4 = this;
416
+ const {
417
+ filterOwner,
418
+ spatialFilter,
419
+ abortController,
420
+ ...params
421
+ } = props;
422
+ const {
423
+ columns,
424
+ sortBy,
425
+ sortDirection,
426
+ page = 0,
427
+ rowsPerPage = 10
428
+ } = params;
429
+ return Promise.resolve(executeModel({
430
+ model: 'table',
431
+ source: _this4.getSource(filterOwner),
432
+ spatialFilter,
433
+ params: {
434
+ column: columns,
435
+ sortBy,
436
+ sortDirection,
437
+ limit: rowsPerPage,
438
+ offset: page * rowsPerPage
439
+ },
440
+ opts: {
441
+ abortController
442
+ }
443
+ }).then(res => ({
444
+ rows: normalizeObjectKeys(res.rows),
445
+ totalCount: res.metadata.total
446
+ })));
447
+ } catch (e) {
448
+ return Promise.reject(e);
449
+ }
450
+ }
451
+ getScatter(props) {
452
+ try {
453
+ const _this5 = this;
454
+ const {
455
+ filterOwner,
456
+ spatialFilter,
457
+ abortController,
458
+ ...params
459
+ } = props;
460
+ const {
461
+ xAxisColumn,
462
+ xAxisJoinOperation,
463
+ yAxisColumn,
464
+ yAxisJoinOperation
465
+ } = params;
466
+ // Make sure this is sync with the same constant in cloud-native/maps-api
467
+ const HARD_LIMIT = 500;
468
+ return Promise.resolve(executeModel({
469
+ model: 'scatterplot',
470
+ source: _this5.getSource(filterOwner),
471
+ spatialFilter,
472
+ params: {
473
+ xAxisColumn,
474
+ xAxisJoinOperation,
475
+ yAxisColumn,
476
+ yAxisJoinOperation,
477
+ limit: HARD_LIMIT
478
+ },
479
+ opts: {
480
+ abortController
481
+ }
482
+ }).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(_ref => {
483
+ let {
484
+ x,
485
+ y
486
+ } = _ref;
487
+ return [x, y];
488
+ })));
489
+ } catch (e) {
490
+ return Promise.reject(e);
491
+ }
492
+ }
493
+ getTimeSeries(props) {
494
+ try {
495
+ const _this6 = this;
496
+ const {
497
+ filterOwner,
498
+ abortController,
499
+ spatialFilter,
500
+ ...params
501
+ } = props;
502
+ const {
503
+ column,
504
+ operationColumn,
505
+ joinOperation,
506
+ operation,
507
+ stepSize,
508
+ stepMultiplier,
509
+ splitByCategory,
510
+ splitByCategoryLimit,
511
+ splitByCategoryValues
512
+ } = params;
513
+ return Promise.resolve(executeModel({
514
+ model: 'timeseries',
515
+ source: _this6.getSource(filterOwner),
516
+ spatialFilter,
517
+ params: {
518
+ column,
519
+ stepSize,
520
+ stepMultiplier,
521
+ operationColumn: operationColumn || column,
522
+ joinOperation,
523
+ operation,
524
+ splitByCategory,
525
+ splitByCategoryLimit,
526
+ splitByCategoryValues
527
+ },
528
+ opts: {
529
+ abortController
530
+ }
531
+ }).then(res => ({
532
+ rows: normalizeObjectKeys(res.rows),
533
+ categories: res.metadata?.categories
534
+ })));
535
+ } catch (e) {
536
+ return Promise.reject(e);
537
+ }
538
+ }
539
+ getHistogram(props) {
540
+ try {
541
+ const _this7 = this;
542
+ const {
543
+ filterOwner,
544
+ spatialFilter,
545
+ abortController,
546
+ ...params
547
+ } = props;
548
+ const {
549
+ column,
550
+ operation,
551
+ ticks
552
+ } = params;
553
+ return Promise.resolve(executeModel({
554
+ model: 'histogram',
555
+ source: _this7.getSource(filterOwner),
556
+ spatialFilter,
557
+ params: {
558
+ column,
559
+ operation,
560
+ ticks
561
+ },
562
+ opts: {
563
+ abortController
564
+ }
565
+ }).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
566
+ if (data.length) {
567
+ // Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
568
+ // include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
569
+ const result = Array(ticks.length + 1).fill(0);
570
+ data.forEach(_ref2 => {
571
+ let {
572
+ tick,
573
+ value
574
+ } = _ref2;
575
+ return result[tick] = value;
576
+ });
577
+ return result;
578
+ }
579
+ return [];
580
+ });
581
+ } catch (e) {
582
+ return Promise.reject(e);
583
+ }
584
+ }
585
+ }
586
+ WidgetBaseSource.defaultProps = {
587
+ filters: {},
588
+ filtersLogicalOperator: 'and'
589
+ };
590
+
591
+ class WidgetQuerySource extends WidgetBaseSource {
592
+ getSource(owner) {
593
+ return {
594
+ ...super.getSource(owner),
595
+ type: exports.MapType.QUERY,
596
+ data: this.props.sqlQuery
597
+ };
598
+ }
599
+ }
600
+
601
+ class WidgetTableSource extends WidgetBaseSource {
602
+ getSource(owner) {
603
+ return {
604
+ ...super.getSource(owner),
605
+ type: exports.MapType.TABLE,
606
+ data: this.props.tableName
607
+ };
608
+ }
609
+ }
610
+
611
+ // loaders.gl
612
+ const isObject = x => x !== null && typeof x === 'object';
613
+ const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
614
+
615
+ const DEFAULT_TILE_RESOLUTION = 0.5;
616
+ const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
617
+ const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
618
+
619
+ /**
620
+ *
621
+ * Custom error for reported errors in CARTO Maps API.
622
+ * Provides useful debugging information in console and context for applications.
623
+ *
624
+ */
625
+ class CartoAPIError extends Error {
626
+ constructor(error, errorContext, response) {
627
+ let responseString = 'Failed to connect';
628
+ if (response) {
629
+ responseString = 'Server returned: ';
630
+ if (response.status === 400) {
631
+ responseString += 'Bad request';
632
+ } else if (response.status === 401 || response.status === 403) {
633
+ responseString += 'Unauthorized access';
634
+ } else if (response.status === 404) {
635
+ responseString += 'Not found';
636
+ } else {
637
+ responseString += 'Error';
638
+ }
639
+ responseString += ` (${response.status}):`;
640
+ }
641
+ responseString += ` ${error.message || error}`;
642
+ let message = `${errorContext.requestType} API request failed`;
643
+ message += `\n${responseString}`;
644
+ for (const key of Object.keys(errorContext)) {
645
+ if (key === 'requestType') continue; // eslint-disable-line no-continue
646
+ message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
647
+ }
648
+ message += '\n';
649
+ super(message);
650
+ this.name = 'CartoAPIError';
651
+ this.response = response;
652
+ this.error = error;
653
+ this.errorContext = errorContext;
654
+ }
655
+ }
656
+ /**
657
+ * Converts camelCase to Camel Case
658
+ */
659
+ function formatErrorKey(key) {
660
+ return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
661
+ }
662
+
663
+ const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
664
+ const DEFAULT_CLIENT = 'deck-gl-carto';
665
+ const V3_MINOR_VERSION = '3.4';
666
+ const MAX_GET_LENGTH = 8192;
667
+ const DEFAULT_PARAMETERS = {
668
+ v: V3_MINOR_VERSION,
669
+ deckglVersion: "0.0.1-0"
670
+ };
671
+ const DEFAULT_HEADERS = {
672
+ Accept: 'application/json',
673
+ 'Content-Type': 'application/json'
674
+ };
675
+
676
+ function joinPath(...args) {
677
+ return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
678
+ }
679
+ function buildV3Path(apiBaseUrl, version, endpoint, ...rest) {
680
+ return joinPath(apiBaseUrl, version, endpoint, ...rest);
681
+ }
682
+ function buildSourceUrl({
683
+ apiBaseUrl,
684
+ connectionName,
685
+ endpoint
686
+ }) {
687
+ return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
688
+ }
689
+
690
+ /**
691
+ * Simple encode parameter
692
+ */
693
+ function encodeParameter(name, value) {
694
+ if (isPureObject(value) || Array.isArray(value)) {
695
+ return `${name}=${encodeURIComponent(JSON.stringify(value))}`;
696
+ }
697
+ return `${name}=${encodeURIComponent(value)}`;
698
+ }
699
+ const REQUEST_CACHE = new Map();
700
+ async function requestWithParameters({
701
+ baseUrl,
702
+ parameters,
703
+ headers: customHeaders,
704
+ errorContext
705
+ }) {
706
+ parameters = {
707
+ ...DEFAULT_PARAMETERS,
708
+ ...parameters
709
+ };
710
+ const key = createCacheKey(baseUrl, parameters || {}, customHeaders || {});
711
+ if (REQUEST_CACHE.has(key)) {
712
+ return REQUEST_CACHE.get(key);
713
+ }
714
+ const url = parameters ? createURLWithParameters(baseUrl, parameters) : baseUrl;
715
+ const headers = {
716
+ ...DEFAULT_HEADERS,
717
+ ...customHeaders
718
+ };
719
+ /* global fetch */
720
+ const fetchPromise = url.length > MAX_GET_LENGTH ? fetch(baseUrl, {
721
+ method: 'POST',
722
+ body: JSON.stringify(parameters),
723
+ headers
724
+ }) : fetch(url, {
725
+ headers
726
+ });
727
+ let response;
728
+ const jsonPromise = fetchPromise.then(_response => {
729
+ response = _response;
730
+ return response.json();
731
+ }).then(json => {
732
+ if (!response || !response.ok) {
733
+ throw new Error(json.error);
734
+ }
735
+ return json;
736
+ }).catch(error => {
737
+ REQUEST_CACHE.delete(key);
738
+ throw new CartoAPIError(error, errorContext, response);
739
+ });
740
+ REQUEST_CACHE.set(key, jsonPromise);
741
+ return jsonPromise;
742
+ }
743
+ function createCacheKey(baseUrl, parameters, headers) {
744
+ const parameterEntries = Object.entries(parameters).sort(([a], [b]) => a > b ? 1 : -1);
745
+ const headerEntries = Object.entries(headers).sort(([a], [b]) => a > b ? 1 : -1);
746
+ return JSON.stringify({
747
+ baseUrl,
748
+ parameters: parameterEntries,
749
+ headers: headerEntries
750
+ });
751
+ }
752
+ function createURLWithParameters(baseUrl, parameters) {
753
+ const encodedParameters = Object.entries(parameters).map(([key, value]) => encodeParameter(key, value));
754
+ return `${baseUrl}?${encodedParameters.join('&')}`;
755
+ }
756
+
757
+ /* eslint-disable camelcase */
758
+ const SOURCE_DEFAULTS = {
759
+ apiBaseUrl: DEFAULT_API_BASE_URL,
760
+ clientId: DEFAULT_CLIENT,
761
+ format: 'tilejson',
762
+ headers: {}
763
+ };
764
+ async function baseSource(endpoint, options, urlParameters) {
765
+ const {
766
+ accessToken,
767
+ connectionName,
768
+ cache,
769
+ ...optionalOptions
770
+ } = options;
771
+ const mergedOptions = {
772
+ ...SOURCE_DEFAULTS,
773
+ accessToken,
774
+ connectionName,
775
+ endpoint
776
+ };
777
+ for (const key in optionalOptions) {
778
+ if (optionalOptions[key]) {
779
+ mergedOptions[key] = optionalOptions[key];
780
+ }
781
+ }
782
+ const baseUrl = buildSourceUrl(mergedOptions);
783
+ const {
784
+ clientId,
785
+ format
786
+ } = mergedOptions;
787
+ const headers = {
788
+ Authorization: `Bearer ${options.accessToken}`,
789
+ ...options.headers
790
+ };
791
+ const parameters = {
792
+ client: clientId,
793
+ ...urlParameters
794
+ };
795
+ const errorContext = {
796
+ requestType: 'Map instantiation',
797
+ connection: options.connectionName,
798
+ type: endpoint,
799
+ source: JSON.stringify(parameters, undefined, 2)
800
+ };
801
+ const mapInstantiation = await requestWithParameters({
802
+ baseUrl,
803
+ parameters,
804
+ headers,
805
+ errorContext
806
+ });
807
+ const dataUrl = mapInstantiation[format].url[0];
808
+ if (cache) {
809
+ cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
810
+ }
811
+ errorContext.requestType = 'Map data';
812
+ if (format === 'tilejson') {
813
+ const json = await requestWithParameters({
814
+ baseUrl: dataUrl,
815
+ headers,
816
+ errorContext
817
+ });
818
+ if (accessToken) {
819
+ json.accessToken = accessToken;
820
+ }
821
+ return json;
822
+ }
823
+ return await requestWithParameters({
824
+ baseUrl: dataUrl,
825
+ headers,
826
+ errorContext
827
+ });
828
+ }
829
+
830
+ const boundaryQuerySource = async function (options) {
831
+ const {
832
+ columns,
833
+ filters,
834
+ tilesetTableName,
835
+ matchingColumn = 'id',
836
+ propertiesSqlQuery,
837
+ queryParameters
838
+ } = options;
839
+ const urlParameters = {
840
+ tilesetTableName,
841
+ matchingColumn,
842
+ propertiesSqlQuery
843
+ };
844
+ if (columns) {
845
+ urlParameters.columns = columns.join(',');
846
+ }
847
+ if (filters) {
848
+ urlParameters.filters = filters;
849
+ }
850
+ if (queryParameters) {
851
+ urlParameters.queryParameters = queryParameters;
852
+ }
853
+ return baseSource('boundary', options, urlParameters);
854
+ };
855
+
856
+ const boundaryTableSource = async function (options) {
857
+ const {
858
+ filters,
859
+ tilesetTableName,
860
+ columns,
861
+ matchingColumn = 'id',
862
+ propertiesTableName
863
+ } = options;
864
+ const urlParameters = {
865
+ tilesetTableName,
866
+ matchingColumn,
867
+ propertiesTableName
868
+ };
869
+ if (columns) {
870
+ urlParameters.columns = columns.join(',');
871
+ }
872
+ if (filters) {
873
+ urlParameters.filters = filters;
874
+ }
875
+ return baseSource('boundary', options, urlParameters);
876
+ };
877
+
878
+ /* eslint-disable camelcase */
879
+ const h3QuerySource$1 = async function (options) {
880
+ const {
881
+ aggregationExp,
882
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
883
+ sqlQuery,
884
+ spatialDataColumn = 'h3',
885
+ queryParameters
886
+ } = options;
887
+ const urlParameters = {
888
+ aggregationExp,
889
+ spatialDataColumn,
890
+ spatialDataType: 'h3',
891
+ q: sqlQuery
892
+ };
893
+ if (aggregationResLevel) {
894
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
895
+ }
896
+ if (queryParameters) {
897
+ urlParameters.queryParameters = queryParameters;
898
+ }
899
+ return baseSource('query', options, urlParameters);
900
+ };
901
+
902
+ /* eslint-disable camelcase */
903
+ const h3TableSource$1 = async function (options) {
904
+ const {
905
+ aggregationExp,
906
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
907
+ spatialDataColumn = 'h3',
908
+ tableName
909
+ } = options;
910
+ const urlParameters = {
911
+ aggregationExp,
912
+ name: tableName,
913
+ spatialDataColumn,
914
+ spatialDataType: 'h3'
915
+ };
916
+ if (aggregationResLevel) {
917
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
918
+ }
919
+ return baseSource('table', options, urlParameters);
920
+ };
921
+
922
+ const h3TilesetSource$1 = async function (options) {
923
+ const {
924
+ tableName
925
+ } = options;
926
+ const urlParameters = {
927
+ name: tableName
928
+ };
929
+ return baseSource('tileset', options, urlParameters);
930
+ };
931
+
932
+ const rasterSource = async function (options) {
933
+ const {
934
+ tableName
935
+ } = options;
936
+ const urlParameters = {
937
+ name: tableName
938
+ };
939
+ return baseSource('raster', options, urlParameters);
940
+ };
941
+
942
+ /* eslint-disable camelcase */
943
+ const quadbinQuerySource$1 = async function (options) {
944
+ const {
945
+ aggregationExp,
946
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
947
+ sqlQuery,
948
+ spatialDataColumn = 'quadbin',
949
+ queryParameters
950
+ } = options;
951
+ const urlParameters = {
952
+ aggregationExp,
953
+ q: sqlQuery,
954
+ spatialDataColumn,
955
+ spatialDataType: 'quadbin'
956
+ };
957
+ if (aggregationResLevel) {
958
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
959
+ }
960
+ if (queryParameters) {
961
+ urlParameters.queryParameters = queryParameters;
962
+ }
963
+ return baseSource('query', options, urlParameters);
964
+ };
965
+
966
+ /* eslint-disable camelcase */
967
+ const quadbinTableSource$1 = async function (options) {
968
+ const {
969
+ aggregationExp,
970
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
971
+ spatialDataColumn = 'quadbin',
972
+ tableName
973
+ } = options;
974
+ const urlParameters = {
975
+ aggregationExp,
976
+ name: tableName,
977
+ spatialDataColumn,
978
+ spatialDataType: 'quadbin'
979
+ };
980
+ if (aggregationResLevel) {
981
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
982
+ }
983
+ return baseSource('table', options, urlParameters);
984
+ };
985
+
986
+ const quadbinTilesetSource$1 = async function (options) {
987
+ const {
988
+ tableName
989
+ } = options;
990
+ const urlParameters = {
991
+ name: tableName
992
+ };
993
+ return baseSource('tileset', options, urlParameters);
994
+ };
995
+
996
+ /* eslint-disable camelcase */
997
+ const vectorQuerySource$1 = async function (options) {
998
+ const {
999
+ columns,
1000
+ filters,
1001
+ spatialDataColumn = 'geom',
1002
+ sqlQuery,
1003
+ tileResolution = DEFAULT_TILE_RESOLUTION,
1004
+ queryParameters
1005
+ } = options;
1006
+ const urlParameters = {
1007
+ spatialDataColumn,
1008
+ spatialDataType: 'geo',
1009
+ tileResolution: tileResolution.toString(),
1010
+ q: sqlQuery
1011
+ };
1012
+ if (columns) {
1013
+ urlParameters.columns = columns.join(',');
1014
+ }
1015
+ if (filters) {
1016
+ urlParameters.filters = filters;
1017
+ }
1018
+ if (queryParameters) {
1019
+ urlParameters.queryParameters = queryParameters;
1020
+ }
1021
+ return baseSource('query', options, urlParameters);
1022
+ };
1023
+
1024
+ /* eslint-disable camelcase */
1025
+ const vectorTableSource$1 = async function (options) {
1026
+ const {
1027
+ columns,
1028
+ filters,
1029
+ spatialDataColumn = 'geom',
1030
+ tableName,
1031
+ tileResolution = DEFAULT_TILE_RESOLUTION
1032
+ } = options;
1033
+ const urlParameters = {
1034
+ name: tableName,
1035
+ spatialDataColumn,
1036
+ spatialDataType: 'geo',
1037
+ tileResolution: tileResolution.toString()
1038
+ };
1039
+ if (columns) {
1040
+ urlParameters.columns = columns.join(',');
1041
+ }
1042
+ if (filters) {
1043
+ urlParameters.filters = filters;
1044
+ }
1045
+ return baseSource('table', options, urlParameters);
1046
+ };
1047
+
1048
+ const vectorTilesetSource$1 = async function (options) {
1049
+ const {
1050
+ tableName
1051
+ } = options;
1052
+ const urlParameters = {
1053
+ name: tableName
1054
+ };
1055
+ return baseSource('tileset', options, urlParameters);
1056
+ };
1057
+
1058
+ ({
1059
+ boundaryQuerySource,
1060
+ boundaryTableSource,
1061
+ h3QuerySource: h3QuerySource$1,
1062
+ h3TableSource: h3TableSource$1,
1063
+ h3TilesetSource: h3TilesetSource$1,
1064
+ rasterSource,
1065
+ quadbinQuerySource: quadbinQuerySource$1,
1066
+ quadbinTableSource: quadbinTableSource$1,
1067
+ quadbinTilesetSource: quadbinTilesetSource$1,
1068
+ vectorQuerySource: vectorQuerySource$1,
1069
+ vectorTableSource: vectorTableSource$1,
1070
+ vectorTilesetSource: vectorTilesetSource$1
1071
+ });
1072
+
1073
+ /******************************************************************************
1074
+ * VECTOR SOURCES
1075
+ */
1076
+ /** Wrapper adding widget support to {@link _vectorTableSource}. */
1077
+
1078
+ /** Wrapper adding widget support to {@link _quadbinTilesetSource}. */
1079
+ const quadbinTilesetSource = function () {
1080
+ try {
1081
+ throw new Error('not implemented');
1082
+ } catch (e) {
1083
+ return Promise.reject(e);
1084
+ }
1085
+ };
1086
+ /** Wrapper adding widget support to {@link _quadbinQuerySource}. */
1087
+ const quadbinQuerySource = function (props) {
1088
+ try {
1089
+ return Promise.resolve(quadbinQuerySource$1(props)).then(function (response) {
1090
+ return {
1091
+ ...response,
1092
+ widgetSource: new WidgetQuerySource(props)
1093
+ };
1094
+ });
1095
+ } catch (e) {
1096
+ return Promise.reject(e);
1097
+ }
1098
+ };
1099
+ /******************************************************************************
1100
+ * QUADBIN SOURCES
1101
+ */
1102
+ /** Wrapper adding widget support to {@link _quadbinTableSource}. */
1103
+ const quadbinTableSource = function (props) {
1104
+ try {
1105
+ return Promise.resolve(quadbinTableSource$1(props)).then(function (response) {
1106
+ return {
1107
+ ...response,
1108
+ widgetSource: new WidgetTableSource(props)
1109
+ };
1110
+ });
1111
+ } catch (e) {
1112
+ return Promise.reject(e);
1113
+ }
1114
+ };
1115
+ /** Wrapper adding widget support to {@link _h3TilesetSource}. */
1116
+ const h3TilesetSource = function () {
1117
+ try {
1118
+ throw new Error('not implemented');
1119
+ } catch (e) {
1120
+ return Promise.reject(e);
1121
+ }
1122
+ };
1123
+ /** Wrapper adding widget support to {@link _h3QuerySource}. */
1124
+ const h3QuerySource = function (props) {
1125
+ try {
1126
+ return Promise.resolve(h3QuerySource$1(props)).then(function (response) {
1127
+ return {
1128
+ ...response,
1129
+ widgetSource: new WidgetQuerySource(props)
1130
+ };
1131
+ });
1132
+ } catch (e) {
1133
+ return Promise.reject(e);
1134
+ }
1135
+ };
1136
+ /******************************************************************************
1137
+ * H3 SOURCES
1138
+ */
1139
+ /** Wrapper adding widget support to {@link _h3TableSource}. */
1140
+ const h3TableSource = function (props) {
1141
+ try {
1142
+ return Promise.resolve(h3TableSource$1(props)).then(function (response) {
1143
+ return {
1144
+ ...response,
1145
+ widgetSource: new WidgetTableSource(props)
1146
+ };
1147
+ });
1148
+ } catch (e) {
1149
+ return Promise.reject(e);
1150
+ }
1151
+ };
1152
+ /** Wrapper adding widget support to {@link _vectorTilesetSource}. */
1153
+ const vectorTilesetSource = function () {
1154
+ try {
1155
+ throw new Error('not implemented');
1156
+ } catch (e) {
1157
+ return Promise.reject(e);
1158
+ }
1159
+ };
1160
+ /** Wrapper adding widget support to {@link _vectorQuerySource}. */
1161
+ const vectorQuerySource = function (props) {
1162
+ try {
1163
+ return Promise.resolve(vectorQuerySource$1(props)).then(function (response) {
1164
+ return {
1165
+ ...response,
1166
+ widgetSource: new WidgetQuerySource(props)
1167
+ };
1168
+ });
1169
+ } catch (e) {
1170
+ return Promise.reject(e);
1171
+ }
1172
+ };
1173
+ const vectorTableSource = function (props) {
1174
+ try {
1175
+ return Promise.resolve(vectorTableSource$1(props)).then(function (response) {
1176
+ return {
1177
+ ...response,
1178
+ widgetSource: new WidgetTableSource(props)
1179
+ };
1180
+ });
1181
+ } catch (e) {
1182
+ return Promise.reject(e);
1183
+ }
1184
+ };
1185
+
1186
+ exports.CLIENT_ID = CLIENT_ID;
1187
+ exports.WidgetBaseSource = WidgetBaseSource;
1188
+ exports.WidgetQuerySource = WidgetQuerySource;
1189
+ exports.WidgetTableSource = WidgetTableSource;
1190
+ exports.executeModel = executeModel;
1191
+ exports.getClient = getClient;
1192
+ exports.h3QuerySource = h3QuerySource;
1193
+ exports.h3TableSource = h3TableSource;
1194
+ exports.h3TilesetSource = h3TilesetSource;
1195
+ exports.quadbinQuerySource = quadbinQuerySource;
1196
+ exports.quadbinTableSource = quadbinTableSource;
1197
+ exports.quadbinTilesetSource = quadbinTilesetSource;
1198
+ exports.setClient = setClient;
1199
+ exports.vectorQuerySource = vectorQuerySource;
1200
+ exports.vectorTableSource = vectorTableSource;
1201
+ exports.vectorTilesetSource = vectorTilesetSource;
1202
+ //# sourceMappingURL=api-client.cjs.map