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