@carto/api-client 0.4.2-alpha.0 → 0.4.3
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.
- package/CHANGELOG.md +9 -1
- package/build/api/query.d.ts +1 -1
- package/build/api-client.cjs +990 -1163
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +876 -1044
- package/build/api-client.modern.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/models/model.d.ts +1 -7
- package/build/sources/boundary-query-source.d.ts +2 -1
- package/build/sources/boundary-table-source.d.ts +2 -1
- package/build/sources/h3-query-source.d.ts +2 -1
- package/build/sources/h3-table-source.d.ts +2 -1
- package/build/sources/h3-tileset-source.d.ts +2 -1
- package/build/sources/index.d.ts +13 -14
- package/build/sources/quadbin-query-source.d.ts +2 -1
- package/build/sources/quadbin-table-source.d.ts +2 -1
- package/build/sources/quadbin-tileset-source.d.ts +2 -1
- package/build/sources/raster-source.d.ts +2 -1
- package/build/sources/types.d.ts +57 -36
- package/build/sources/vector-query-source.d.ts +2 -1
- package/build/sources/vector-table-source.d.ts +2 -1
- package/build/sources/vector-tileset-source.d.ts +2 -1
- package/build/utils.d.ts +1 -1
- package/build/widget-sources/types.d.ts +1 -8
- package/build/widget-sources/widget-base-source.d.ts +1 -0
- package/package.json +1 -1
- package/src/api/query.ts +2 -1
- package/src/index.ts +1 -36
- package/src/models/model.ts +24 -47
- package/src/sources/boundary-query-source.ts +4 -2
- package/src/sources/boundary-table-source.ts +4 -2
- package/src/sources/h3-query-source.ts +4 -8
- package/src/sources/h3-table-source.ts +4 -7
- package/src/sources/h3-tileset-source.ts +4 -2
- package/src/sources/index.ts +54 -24
- package/src/sources/quadbin-query-source.ts +5 -7
- package/src/sources/quadbin-table-source.ts +5 -7
- package/src/sources/quadbin-tileset-source.ts +4 -2
- package/src/sources/raster-source.ts +4 -2
- package/src/sources/types.ts +63 -41
- package/src/sources/vector-query-source.ts +10 -5
- package/src/sources/vector-table-source.ts +10 -5
- package/src/sources/vector-tileset-source.ts +4 -2
- package/src/utils.ts +1 -1
- package/src/widget-sources/types.ts +1 -9
- package/src/widget-sources/widget-base-source.ts +21 -190
- package/build/spatial-index.d.ts +0 -11
- package/src/spatial-index.ts +0 -119
package/build/api-client.cjs
CHANGED
|
@@ -381,1206 +381,1051 @@ const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
|
381
381
|
*/
|
|
382
382
|
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
383
383
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
/** @internalRemarks Source: @carto/react-api */
|
|
390
|
-
|
|
391
|
-
function _catch(body, recover) {
|
|
392
|
-
try {
|
|
393
|
-
var result = body();
|
|
394
|
-
} catch (e) {
|
|
395
|
-
return recover(e);
|
|
396
|
-
}
|
|
397
|
-
if (result && result.then) {
|
|
398
|
-
return result.then(void 0, recover);
|
|
399
|
-
}
|
|
400
|
-
return result;
|
|
384
|
+
// deck.gl
|
|
385
|
+
// SPDX-License-Identifier: MIT
|
|
386
|
+
// Copyright (c) vis.gl contributors
|
|
387
|
+
function joinPath() {
|
|
388
|
+
return [].slice.call(arguments).map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
|
|
401
389
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
} = _ref2;
|
|
408
|
-
try {
|
|
409
|
-
let _exit;
|
|
410
|
-
function _temp2(_result) {
|
|
411
|
-
if (_exit) ;
|
|
412
|
-
if (!response.ok) {
|
|
413
|
-
dealWithApiError({
|
|
414
|
-
response,
|
|
415
|
-
data
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
return data;
|
|
419
|
-
}
|
|
420
|
-
let response;
|
|
421
|
-
let data;
|
|
422
|
-
const isPost = opts?.method === 'POST';
|
|
423
|
-
const _temp = _catch(function () {
|
|
424
|
-
return Promise.resolve(fetch(url.toString(), {
|
|
425
|
-
headers: {
|
|
426
|
-
Authorization: `Bearer ${accessToken}`,
|
|
427
|
-
...(isPost && {
|
|
428
|
-
'Content-Type': 'application/json'
|
|
429
|
-
})
|
|
430
|
-
},
|
|
431
|
-
...(isPost && {
|
|
432
|
-
method: opts?.method,
|
|
433
|
-
body: opts?.body
|
|
434
|
-
}),
|
|
435
|
-
signal: opts?.abortController?.signal,
|
|
436
|
-
...opts?.otherOptions
|
|
437
|
-
})).then(function (_fetch) {
|
|
438
|
-
response = _fetch;
|
|
439
|
-
return Promise.resolve(response.json()).then(function (_response$json) {
|
|
440
|
-
data = _response$json;
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
}, function (error) {
|
|
444
|
-
if (error.name === 'AbortError') throw error;
|
|
445
|
-
throw new Error(`Failed request: ${error}`);
|
|
446
|
-
});
|
|
447
|
-
return Promise.resolve(_temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp));
|
|
448
|
-
} catch (e) {
|
|
449
|
-
return Promise.reject(e);
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
function dealWithApiError(_ref) {
|
|
390
|
+
function buildV3Path(apiBaseUrl, version, endpoint) {
|
|
391
|
+
return joinPath(apiBaseUrl, version, endpoint, ...[].slice.call(arguments, 3));
|
|
392
|
+
}
|
|
393
|
+
/** @internal Required by fetchMap(). */
|
|
394
|
+
function buildPublicMapUrl(_ref) {
|
|
453
395
|
let {
|
|
454
|
-
|
|
455
|
-
|
|
396
|
+
apiBaseUrl,
|
|
397
|
+
cartoMapId
|
|
456
398
|
} = _ref;
|
|
457
|
-
|
|
458
|
-
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
459
|
-
}
|
|
460
|
-
if (typeof data.error === 'string' && data.error?.includes('Missing columns')) {
|
|
461
|
-
throw new InvalidColumnError(data.error);
|
|
462
|
-
}
|
|
463
|
-
switch (response.status) {
|
|
464
|
-
case 401:
|
|
465
|
-
throw new Error('Unauthorized access. Invalid credentials');
|
|
466
|
-
case 403:
|
|
467
|
-
throw new Error('Forbidden access to the requested data');
|
|
468
|
-
default:
|
|
469
|
-
const msg = data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify(data?.hint || data.error?.[0]);
|
|
470
|
-
throw new Error(msg);
|
|
471
|
-
}
|
|
399
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
472
400
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
V3
|
|
478
|
-
} = exports.ApiVersion;
|
|
479
|
-
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
480
|
-
/**
|
|
481
|
-
* Execute a SQL model request.
|
|
482
|
-
* @internalRemarks Source: @carto/react-api
|
|
483
|
-
*/
|
|
484
|
-
function executeModel(props) {
|
|
485
|
-
assert(props.source, 'executeModel: missing source');
|
|
486
|
-
assert(props.model, 'executeModel: missing model');
|
|
487
|
-
assert(props.params, 'executeModel: missing params');
|
|
488
|
-
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
489
|
-
const {
|
|
490
|
-
model,
|
|
491
|
-
source,
|
|
492
|
-
params,
|
|
493
|
-
opts
|
|
494
|
-
} = props;
|
|
495
|
-
const {
|
|
496
|
-
type,
|
|
497
|
-
apiVersion,
|
|
401
|
+
/** @internal Required by fetchMap(). */
|
|
402
|
+
function buildStatsUrl(_ref2) {
|
|
403
|
+
let {
|
|
404
|
+
attribute,
|
|
498
405
|
apiBaseUrl,
|
|
499
|
-
accessToken,
|
|
500
406
|
connectionName,
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
507
|
-
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
508
|
-
const {
|
|
509
|
-
data,
|
|
510
|
-
filters,
|
|
511
|
-
filtersLogicalOperator = 'and',
|
|
512
|
-
spatialDataType = 'geo',
|
|
513
|
-
spatialFiltersMode = 'intersects',
|
|
514
|
-
spatialFiltersResolution = 0
|
|
515
|
-
} = source;
|
|
516
|
-
const queryParams = {
|
|
517
|
-
type,
|
|
518
|
-
client: clientId,
|
|
519
|
-
source: data,
|
|
520
|
-
params,
|
|
521
|
-
queryParameters: source.queryParameters || '',
|
|
522
|
-
filters,
|
|
523
|
-
filtersLogicalOperator
|
|
524
|
-
};
|
|
525
|
-
const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
|
|
526
|
-
// Picking Model API requires 'spatialDataColumn'.
|
|
527
|
-
if (model === 'pick') {
|
|
528
|
-
queryParams.spatialDataColumn = spatialDataColumn;
|
|
529
|
-
}
|
|
530
|
-
// API supports multiple filters, we apply it only to spatialDataColumn
|
|
531
|
-
const spatialFilters = source.spatialFilter ? {
|
|
532
|
-
[spatialDataColumn]: source.spatialFilter
|
|
533
|
-
} : undefined;
|
|
534
|
-
if (spatialFilters) {
|
|
535
|
-
queryParams.spatialFilters = spatialFilters; // JSON.stringify(spatialFilters);
|
|
536
|
-
queryParams.spatialDataColumn = spatialDataColumn;
|
|
537
|
-
queryParams.spatialDataType = spatialDataType;
|
|
538
|
-
}
|
|
539
|
-
if (spatialDataType !== 'geo') {
|
|
540
|
-
if (spatialFiltersResolution > 0) {
|
|
541
|
-
queryParams.spatialFiltersResolution = spatialFiltersResolution;
|
|
542
|
-
}
|
|
543
|
-
queryParams.spatialFiltersMode = spatialFiltersMode;
|
|
544
|
-
}
|
|
545
|
-
const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
|
|
546
|
-
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
547
|
-
if (isGet) {
|
|
548
|
-
url = urlWithSearchParams;
|
|
549
|
-
}
|
|
550
|
-
return makeCall({
|
|
551
|
-
url,
|
|
552
|
-
accessToken: source.accessToken,
|
|
553
|
-
opts: {
|
|
554
|
-
...opts,
|
|
555
|
-
method: isGet ? 'GET' : 'POST',
|
|
556
|
-
...(!isGet && {
|
|
557
|
-
body: JSON.stringify(queryParams)
|
|
558
|
-
})
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
function objectToURLSearchParams(object) {
|
|
563
|
-
const params = new URLSearchParams();
|
|
564
|
-
for (const key in object) {
|
|
565
|
-
if (isPureObject(object[key])) {
|
|
566
|
-
params.append(key, JSON.stringify(object[key]));
|
|
567
|
-
} else if (Array.isArray(object[key])) {
|
|
568
|
-
params.append(key, JSON.stringify(object[key]));
|
|
569
|
-
} else if (object[key] === null) {
|
|
570
|
-
params.append(key, 'null');
|
|
571
|
-
} else if (object[key] !== undefined) {
|
|
572
|
-
params.append(key, String(object[key]));
|
|
573
|
-
}
|
|
407
|
+
source,
|
|
408
|
+
type
|
|
409
|
+
} = _ref2;
|
|
410
|
+
if (type === 'query') {
|
|
411
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
574
412
|
}
|
|
575
|
-
|
|
413
|
+
// type === 'table'
|
|
414
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
576
415
|
}
|
|
577
|
-
|
|
578
|
-
const DEFAULT_TILE_SIZE = 512;
|
|
579
|
-
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
580
|
-
function getSpatialFiltersResolution(_ref) {
|
|
416
|
+
function buildSourceUrl(_ref3) {
|
|
581
417
|
let {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
588
|
-
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
589
|
-
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
590
|
-
if (source.spatialDataType === 'h3') {
|
|
591
|
-
const tileSize = DEFAULT_TILE_SIZE;
|
|
592
|
-
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref2 => {
|
|
593
|
-
let [zoom] = _ref2;
|
|
594
|
-
return zoom === currentZoomInt;
|
|
595
|
-
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
596
|
-
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
597
|
-
const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
598
|
-
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
599
|
-
}
|
|
600
|
-
if (source.spatialDataType === 'quadbin') {
|
|
601
|
-
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
602
|
-
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
603
|
-
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
604
|
-
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
605
|
-
}
|
|
606
|
-
return undefined;
|
|
418
|
+
apiBaseUrl,
|
|
419
|
+
connectionName,
|
|
420
|
+
endpoint
|
|
421
|
+
} = _ref3;
|
|
422
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
607
423
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
// similar
|
|
615
|
-
function getHexagonResolution(viewport, tileSize) {
|
|
616
|
-
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
617
|
-
// expressed as an offset to the viewport zoom.
|
|
618
|
-
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
619
|
-
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
620
|
-
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
621
|
-
// Clip and bias
|
|
622
|
-
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
424
|
+
function buildQueryUrl(_ref4) {
|
|
425
|
+
let {
|
|
426
|
+
apiBaseUrl,
|
|
427
|
+
connectionName
|
|
428
|
+
} = _ref4;
|
|
429
|
+
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
623
430
|
}
|
|
624
431
|
|
|
432
|
+
// deck.gl
|
|
433
|
+
// SPDX-License-Identifier: MIT
|
|
434
|
+
// Copyright (c) vis.gl contributors
|
|
625
435
|
/**
|
|
626
|
-
* Source for Widget API requests on a data source defined by a SQL query.
|
|
627
436
|
*
|
|
628
|
-
*
|
|
437
|
+
* Custom error for reported errors in CARTO Maps API.
|
|
438
|
+
* Provides useful debugging information in console and context for applications.
|
|
439
|
+
*
|
|
629
440
|
*/
|
|
630
|
-
class
|
|
631
|
-
constructor(
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
441
|
+
class CartoAPIError extends Error {
|
|
442
|
+
constructor(error, errorContext, response, responseJson) {
|
|
443
|
+
let responseString = 'Failed to connect';
|
|
444
|
+
if (response) {
|
|
445
|
+
responseString = 'Server returned: ';
|
|
446
|
+
if (response.status === 400) {
|
|
447
|
+
responseString += 'Bad request';
|
|
448
|
+
} else if (response.status === 401 || response.status === 403) {
|
|
449
|
+
responseString += 'Unauthorized access';
|
|
450
|
+
} else if (response.status === 404) {
|
|
451
|
+
responseString += 'Not found';
|
|
452
|
+
} else {
|
|
453
|
+
responseString += 'Error';
|
|
454
|
+
}
|
|
455
|
+
responseString += ` (${response.status}):`;
|
|
456
|
+
}
|
|
457
|
+
responseString += ` ${error.message || error}`;
|
|
458
|
+
let message = `${errorContext.requestType} API request failed`;
|
|
459
|
+
message += `\n${responseString}`;
|
|
460
|
+
for (const key of Object.keys(errorContext)) {
|
|
461
|
+
if (key === 'requestType') continue;
|
|
462
|
+
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
463
|
+
}
|
|
464
|
+
message += '\n';
|
|
465
|
+
super(message);
|
|
466
|
+
/** Source error from server */
|
|
467
|
+
this.error = void 0;
|
|
468
|
+
/** Context (API call & parameters) in which error occured */
|
|
469
|
+
this.errorContext = void 0;
|
|
470
|
+
/** Response from server */
|
|
471
|
+
this.response = void 0;
|
|
472
|
+
/** JSON Response from server */
|
|
473
|
+
this.responseJson = void 0;
|
|
474
|
+
this.name = 'CartoAPIError';
|
|
475
|
+
this.response = response;
|
|
476
|
+
this.responseJson = responseJson;
|
|
477
|
+
this.error = error;
|
|
478
|
+
this.errorContext = errorContext;
|
|
637
479
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Converts camelCase to Camel Case
|
|
483
|
+
*/
|
|
484
|
+
function formatErrorKey(key) {
|
|
485
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// deck.gl
|
|
489
|
+
const requestWithParameters = function (_ref) {
|
|
490
|
+
let {
|
|
491
|
+
baseUrl,
|
|
492
|
+
parameters = {},
|
|
493
|
+
headers: customHeaders = {},
|
|
494
|
+
errorContext,
|
|
495
|
+
maxLengthURL = DEFAULT_MAX_LENGTH_URL,
|
|
496
|
+
localCache
|
|
497
|
+
} = _ref;
|
|
498
|
+
try {
|
|
499
|
+
// Parameters added to all requests issued with `requestWithParameters()`.
|
|
500
|
+
// These parameters override parameters already in the base URL, but not
|
|
501
|
+
// user-provided parameters.
|
|
502
|
+
parameters = {
|
|
503
|
+
v: V3_MINOR_VERSION,
|
|
504
|
+
client: getClient(),
|
|
505
|
+
...(typeof deck !== 'undefined' && deck.VERSION && {
|
|
506
|
+
deckglVersion: deck.VERSION
|
|
507
|
+
}),
|
|
508
|
+
...parameters
|
|
651
509
|
};
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
try {
|
|
662
|
-
const _this = this;
|
|
663
|
-
const {
|
|
664
|
-
filterOwner,
|
|
665
|
-
spatialFilter,
|
|
666
|
-
spatialFiltersMode,
|
|
667
|
-
abortController,
|
|
668
|
-
viewState,
|
|
669
|
-
...params
|
|
670
|
-
} = options;
|
|
671
|
-
const {
|
|
672
|
-
column,
|
|
673
|
-
operation,
|
|
674
|
-
operationColumn
|
|
675
|
-
} = params;
|
|
676
|
-
const source = _this.getModelSource(filterOwner);
|
|
677
|
-
let spatialFiltersResolution;
|
|
678
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
679
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
680
|
-
source,
|
|
681
|
-
viewState
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
return Promise.resolve(executeModel({
|
|
685
|
-
model: 'category',
|
|
686
|
-
source: {
|
|
687
|
-
...source,
|
|
688
|
-
spatialFiltersResolution,
|
|
689
|
-
spatialFiltersMode,
|
|
690
|
-
spatialFilter
|
|
691
|
-
},
|
|
692
|
-
params: {
|
|
693
|
-
column,
|
|
694
|
-
operation,
|
|
695
|
-
operationColumn: operationColumn || column
|
|
696
|
-
},
|
|
697
|
-
opts: {
|
|
698
|
-
abortController
|
|
699
|
-
}
|
|
700
|
-
}).then(res => normalizeObjectKeys(res.rows)));
|
|
701
|
-
} catch (e) {
|
|
702
|
-
return Promise.reject(e);
|
|
510
|
+
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
511
|
+
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
512
|
+
const {
|
|
513
|
+
cache: REQUEST_CACHE,
|
|
514
|
+
canReadCache,
|
|
515
|
+
canStoreInCache
|
|
516
|
+
} = getCacheSettings(localCache);
|
|
517
|
+
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
518
|
+
return Promise.resolve(REQUEST_CACHE.get(key));
|
|
703
519
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
} = options;
|
|
727
|
-
const {
|
|
728
|
-
columns,
|
|
729
|
-
dataType,
|
|
730
|
-
featureIds,
|
|
731
|
-
z,
|
|
732
|
-
limit,
|
|
733
|
-
tileResolution
|
|
734
|
-
} = params;
|
|
735
|
-
const source = _this2.getModelSource(filterOwner);
|
|
736
|
-
let spatialFiltersResolution;
|
|
737
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
738
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
739
|
-
source,
|
|
740
|
-
viewState
|
|
741
|
-
});
|
|
520
|
+
const url = createURLWithParameters(baseUrl, parameters);
|
|
521
|
+
const headers = {
|
|
522
|
+
...DEFAULT_HEADERS,
|
|
523
|
+
...customHeaders
|
|
524
|
+
};
|
|
525
|
+
/* global fetch */
|
|
526
|
+
const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
|
|
527
|
+
method: 'POST',
|
|
528
|
+
body: JSON.stringify(parameters),
|
|
529
|
+
headers
|
|
530
|
+
}) : fetch(url, {
|
|
531
|
+
headers
|
|
532
|
+
});
|
|
533
|
+
let response;
|
|
534
|
+
let responseJson;
|
|
535
|
+
const jsonPromise = fetchPromise.then(_response => {
|
|
536
|
+
response = _response;
|
|
537
|
+
return response.json();
|
|
538
|
+
}).then(json => {
|
|
539
|
+
responseJson = json;
|
|
540
|
+
if (!response || !response.ok) {
|
|
541
|
+
throw new Error(json.error);
|
|
742
542
|
}
|
|
743
|
-
return
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
columns,
|
|
753
|
-
dataType,
|
|
754
|
-
featureIds,
|
|
755
|
-
z,
|
|
756
|
-
limit: limit || 1000,
|
|
757
|
-
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
758
|
-
},
|
|
759
|
-
opts: {
|
|
760
|
-
abortController
|
|
761
|
-
}
|
|
762
|
-
}).then(res => ({
|
|
763
|
-
rows: normalizeObjectKeys(res.rows)
|
|
764
|
-
})));
|
|
765
|
-
} catch (e) {
|
|
766
|
-
return Promise.reject(e);
|
|
543
|
+
return json;
|
|
544
|
+
}).catch(error => {
|
|
545
|
+
if (canStoreInCache) {
|
|
546
|
+
REQUEST_CACHE.delete(key);
|
|
547
|
+
}
|
|
548
|
+
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
549
|
+
});
|
|
550
|
+
if (canStoreInCache) {
|
|
551
|
+
REQUEST_CACHE.set(key, jsonPromise);
|
|
767
552
|
}
|
|
553
|
+
return Promise.resolve(jsonPromise);
|
|
554
|
+
} catch (e) {
|
|
555
|
+
return Promise.reject(e);
|
|
768
556
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
abortController
|
|
815
|
-
}
|
|
816
|
-
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
817
|
-
} catch (e) {
|
|
818
|
-
return Promise.reject(e);
|
|
557
|
+
};
|
|
558
|
+
const DEFAULT_HEADERS = {
|
|
559
|
+
Accept: 'application/json',
|
|
560
|
+
'Content-Type': 'application/json'
|
|
561
|
+
};
|
|
562
|
+
const DEFAULT_REQUEST_CACHE = new Map();
|
|
563
|
+
function getCacheSettings(localCache) {
|
|
564
|
+
const canReadCache = localCache?.cacheControl?.includes('no-cache') ? false : true;
|
|
565
|
+
const canStoreInCache = localCache?.cacheControl?.includes('no-store') ? false : true;
|
|
566
|
+
const cache = localCache?.cache || DEFAULT_REQUEST_CACHE;
|
|
567
|
+
return {
|
|
568
|
+
cache,
|
|
569
|
+
canReadCache,
|
|
570
|
+
canStoreInCache
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function createCacheKey(baseUrl, parameters, headers) {
|
|
574
|
+
const parameterEntries = Object.entries(parameters).sort((_ref2, _ref3) => {
|
|
575
|
+
let [a] = _ref2;
|
|
576
|
+
let [b] = _ref3;
|
|
577
|
+
return a > b ? 1 : -1;
|
|
578
|
+
});
|
|
579
|
+
const headerEntries = Object.entries(headers).sort((_ref4, _ref5) => {
|
|
580
|
+
let [a] = _ref4;
|
|
581
|
+
let [b] = _ref5;
|
|
582
|
+
return a > b ? 1 : -1;
|
|
583
|
+
});
|
|
584
|
+
return JSON.stringify({
|
|
585
|
+
baseUrl,
|
|
586
|
+
parameters: parameterEntries,
|
|
587
|
+
headers: headerEntries
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
592
|
+
* unless there is a conflict, in which case the new parameters override
|
|
593
|
+
* those already in the URL.
|
|
594
|
+
*/
|
|
595
|
+
function createURLWithParameters(baseUrlString, parameters) {
|
|
596
|
+
const baseUrl = new URL(baseUrlString);
|
|
597
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
598
|
+
if (isPureObject(value) || Array.isArray(value)) {
|
|
599
|
+
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
600
|
+
} else {
|
|
601
|
+
baseUrl.searchParams.set(key, value.toString());
|
|
819
602
|
}
|
|
820
603
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const {
|
|
832
|
-
filterOwner,
|
|
833
|
-
spatialFilter,
|
|
834
|
-
spatialFiltersMode,
|
|
835
|
-
abortController,
|
|
836
|
-
viewState,
|
|
837
|
-
...params
|
|
838
|
-
} = options;
|
|
839
|
-
const {
|
|
840
|
-
column,
|
|
841
|
-
operation,
|
|
842
|
-
ticks
|
|
843
|
-
} = params;
|
|
844
|
-
const source = _this4.getModelSource(filterOwner);
|
|
845
|
-
let spatialFiltersResolution;
|
|
846
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
847
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
848
|
-
source,
|
|
849
|
-
viewState
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
return Promise.resolve(executeModel({
|
|
853
|
-
model: 'histogram',
|
|
854
|
-
source: {
|
|
855
|
-
...source,
|
|
856
|
-
spatialFiltersResolution,
|
|
857
|
-
spatialFiltersMode,
|
|
858
|
-
spatialFilter
|
|
859
|
-
},
|
|
860
|
-
params: {
|
|
861
|
-
column,
|
|
862
|
-
operation,
|
|
863
|
-
ticks
|
|
864
|
-
},
|
|
865
|
-
opts: {
|
|
866
|
-
abortController
|
|
867
|
-
}
|
|
868
|
-
}).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
|
|
869
|
-
if (data.length) {
|
|
870
|
-
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
871
|
-
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
872
|
-
const result = Array(ticks.length + 1).fill(0);
|
|
873
|
-
data.forEach(_ref => {
|
|
874
|
-
let {
|
|
875
|
-
tick,
|
|
876
|
-
value
|
|
877
|
-
} = _ref;
|
|
878
|
-
return result[tick] = value;
|
|
879
|
-
});
|
|
880
|
-
return result;
|
|
881
|
-
}
|
|
882
|
-
return [];
|
|
883
|
-
});
|
|
884
|
-
} catch (e) {
|
|
885
|
-
return Promise.reject(e);
|
|
604
|
+
return baseUrl.toString();
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Deletes query string parameters from a URL.
|
|
608
|
+
*/
|
|
609
|
+
function excludeURLParameters(baseUrlString, parameters) {
|
|
610
|
+
const baseUrl = new URL(baseUrlString);
|
|
611
|
+
for (const param of parameters) {
|
|
612
|
+
if (baseUrl.searchParams.has(param)) {
|
|
613
|
+
baseUrl.searchParams.delete(param);
|
|
886
614
|
}
|
|
887
615
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
} = params;
|
|
910
|
-
const source = _this5.getModelSource(filterOwner);
|
|
911
|
-
let spatialFiltersResolution;
|
|
912
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
913
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
914
|
-
source,
|
|
915
|
-
viewState
|
|
916
|
-
});
|
|
616
|
+
return baseUrl.toString();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// deck.gl
|
|
620
|
+
const baseSource = function (endpoint, options, urlParameters) {
|
|
621
|
+
try {
|
|
622
|
+
const {
|
|
623
|
+
accessToken,
|
|
624
|
+
connectionName,
|
|
625
|
+
cache,
|
|
626
|
+
...optionalOptions
|
|
627
|
+
} = options;
|
|
628
|
+
const mergedOptions = {
|
|
629
|
+
...SOURCE_DEFAULTS,
|
|
630
|
+
accessToken,
|
|
631
|
+
connectionName,
|
|
632
|
+
endpoint
|
|
633
|
+
};
|
|
634
|
+
for (const key in optionalOptions) {
|
|
635
|
+
if (optionalOptions[key]) {
|
|
636
|
+
mergedOptions[key] = optionalOptions[key];
|
|
917
637
|
}
|
|
918
|
-
return Promise.resolve(executeModel({
|
|
919
|
-
model: 'range',
|
|
920
|
-
source: {
|
|
921
|
-
...source,
|
|
922
|
-
spatialFiltersResolution,
|
|
923
|
-
spatialFiltersMode,
|
|
924
|
-
spatialFilter
|
|
925
|
-
},
|
|
926
|
-
params: {
|
|
927
|
-
column
|
|
928
|
-
},
|
|
929
|
-
opts: {
|
|
930
|
-
abortController
|
|
931
|
-
}
|
|
932
|
-
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
933
|
-
} catch (e) {
|
|
934
|
-
return Promise.reject(e);
|
|
935
638
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
639
|
+
const baseUrl = buildSourceUrl(mergedOptions);
|
|
640
|
+
const {
|
|
641
|
+
clientId,
|
|
642
|
+
maxLengthURL,
|
|
643
|
+
format,
|
|
644
|
+
localCache
|
|
645
|
+
} = mergedOptions;
|
|
646
|
+
const headers = {
|
|
647
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
648
|
+
...options.headers
|
|
649
|
+
};
|
|
650
|
+
const parameters = {
|
|
651
|
+
client: clientId,
|
|
652
|
+
...urlParameters
|
|
653
|
+
};
|
|
654
|
+
const errorContext = {
|
|
655
|
+
requestType: 'Map instantiation',
|
|
656
|
+
connection: options.connectionName,
|
|
657
|
+
type: endpoint,
|
|
658
|
+
source: JSON.stringify(parameters, undefined, 2)
|
|
659
|
+
};
|
|
660
|
+
return Promise.resolve(requestWithParameters({
|
|
661
|
+
baseUrl,
|
|
662
|
+
parameters,
|
|
663
|
+
headers,
|
|
664
|
+
errorContext,
|
|
665
|
+
maxLengthURL,
|
|
666
|
+
localCache
|
|
667
|
+
})).then(function (mapInstantiation) {
|
|
668
|
+
let _exit;
|
|
669
|
+
function _temp2(_result) {
|
|
670
|
+
return _exit ? _result : Promise.resolve(requestWithParameters({
|
|
671
|
+
baseUrl: dataUrl,
|
|
672
|
+
headers,
|
|
673
|
+
errorContext,
|
|
674
|
+
maxLengthURL,
|
|
675
|
+
localCache
|
|
676
|
+
}));
|
|
968
677
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
678
|
+
const dataUrl = mapInstantiation[format].url[0];
|
|
679
|
+
if (cache) {
|
|
680
|
+
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
681
|
+
}
|
|
682
|
+
errorContext.requestType = 'Map data';
|
|
683
|
+
const _temp = function () {
|
|
684
|
+
if (format === 'tilejson') {
|
|
685
|
+
return Promise.resolve(requestWithParameters({
|
|
686
|
+
baseUrl: dataUrl,
|
|
687
|
+
headers,
|
|
688
|
+
errorContext,
|
|
689
|
+
maxLengthURL,
|
|
690
|
+
localCache
|
|
691
|
+
})).then(function (json) {
|
|
692
|
+
if (accessToken) {
|
|
693
|
+
json.accessToken = accessToken;
|
|
694
|
+
}
|
|
695
|
+
_exit = 1;
|
|
696
|
+
return json;
|
|
697
|
+
});
|
|
988
698
|
}
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return [x, y];
|
|
995
|
-
})));
|
|
996
|
-
} catch (e) {
|
|
997
|
-
return Promise.reject(e);
|
|
998
|
-
}
|
|
699
|
+
}();
|
|
700
|
+
return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
|
701
|
+
});
|
|
702
|
+
} catch (e) {
|
|
703
|
+
return Promise.reject(e);
|
|
999
704
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
*/
|
|
1003
|
-
/**
|
|
1004
|
-
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1005
|
-
* sorting. Suitable for displaying tables and lists.
|
|
1006
|
-
*/
|
|
1007
|
-
getTable(options) {
|
|
1008
|
-
try {
|
|
1009
|
-
const _this7 = this;
|
|
1010
|
-
const {
|
|
1011
|
-
filterOwner,
|
|
1012
|
-
spatialFilter,
|
|
1013
|
-
spatialFiltersMode,
|
|
1014
|
-
abortController,
|
|
1015
|
-
viewState,
|
|
1016
|
-
...params
|
|
1017
|
-
} = options;
|
|
1018
|
-
const {
|
|
1019
|
-
columns,
|
|
1020
|
-
sortBy,
|
|
1021
|
-
sortDirection,
|
|
1022
|
-
offset = 0,
|
|
1023
|
-
limit = 10
|
|
1024
|
-
} = params;
|
|
1025
|
-
const source = _this7.getModelSource(filterOwner);
|
|
1026
|
-
let spatialFiltersResolution;
|
|
1027
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
1028
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
1029
|
-
source,
|
|
1030
|
-
viewState
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
return Promise.resolve(executeModel({
|
|
1034
|
-
model: 'table',
|
|
1035
|
-
source: {
|
|
1036
|
-
...source,
|
|
1037
|
-
spatialFiltersResolution,
|
|
1038
|
-
spatialFiltersMode,
|
|
1039
|
-
spatialFilter
|
|
1040
|
-
},
|
|
1041
|
-
params: {
|
|
1042
|
-
column: columns,
|
|
1043
|
-
sortBy,
|
|
1044
|
-
sortDirection,
|
|
1045
|
-
limit,
|
|
1046
|
-
offset
|
|
1047
|
-
},
|
|
1048
|
-
opts: {
|
|
1049
|
-
abortController
|
|
1050
|
-
}
|
|
1051
|
-
}).then(res => ({
|
|
1052
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1053
|
-
rows: res.rows ?? res.ROWS,
|
|
1054
|
-
totalCount: res.metadata?.total ?? res.METADATA?.TOTAL
|
|
1055
|
-
})));
|
|
1056
|
-
} catch (e) {
|
|
1057
|
-
return Promise.reject(e);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
/****************************************************************************
|
|
1061
|
-
* TIME SERIES
|
|
1062
|
-
*/
|
|
1063
|
-
/**
|
|
1064
|
-
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1065
|
-
* time intervals. Suitable for rendering time series charts.
|
|
1066
|
-
*/
|
|
1067
|
-
getTimeSeries(options) {
|
|
1068
|
-
try {
|
|
1069
|
-
const _this8 = this;
|
|
1070
|
-
const {
|
|
1071
|
-
filterOwner,
|
|
1072
|
-
abortController,
|
|
1073
|
-
spatialFilter,
|
|
1074
|
-
spatialFiltersMode,
|
|
1075
|
-
viewState,
|
|
1076
|
-
...params
|
|
1077
|
-
} = options;
|
|
1078
|
-
const {
|
|
1079
|
-
column,
|
|
1080
|
-
operationColumn,
|
|
1081
|
-
joinOperation,
|
|
1082
|
-
operation,
|
|
1083
|
-
stepSize,
|
|
1084
|
-
stepMultiplier,
|
|
1085
|
-
splitByCategory,
|
|
1086
|
-
splitByCategoryLimit,
|
|
1087
|
-
splitByCategoryValues
|
|
1088
|
-
} = params;
|
|
1089
|
-
const source = _this8.getModelSource(filterOwner);
|
|
1090
|
-
let spatialFiltersResolution;
|
|
1091
|
-
if (spatialFilter && source.spatialDataType !== 'geo') {
|
|
1092
|
-
spatialFiltersResolution = getSpatialFiltersResolution({
|
|
1093
|
-
source,
|
|
1094
|
-
viewState
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
return Promise.resolve(executeModel({
|
|
1098
|
-
model: 'timeseries',
|
|
1099
|
-
source: {
|
|
1100
|
-
...source,
|
|
1101
|
-
spatialFiltersResolution,
|
|
1102
|
-
spatialFiltersMode,
|
|
1103
|
-
spatialFilter
|
|
1104
|
-
},
|
|
1105
|
-
params: {
|
|
1106
|
-
column,
|
|
1107
|
-
stepSize,
|
|
1108
|
-
stepMultiplier,
|
|
1109
|
-
operationColumn: operationColumn || column,
|
|
1110
|
-
joinOperation,
|
|
1111
|
-
operation,
|
|
1112
|
-
splitByCategory,
|
|
1113
|
-
splitByCategoryLimit,
|
|
1114
|
-
splitByCategoryValues
|
|
1115
|
-
},
|
|
1116
|
-
opts: {
|
|
1117
|
-
abortController
|
|
1118
|
-
}
|
|
1119
|
-
}).then(res => ({
|
|
1120
|
-
rows: normalizeObjectKeys(res.rows),
|
|
1121
|
-
categories: res.metadata?.categories
|
|
1122
|
-
})));
|
|
1123
|
-
} catch (e) {
|
|
1124
|
-
return Promise.reject(e);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
WidgetBaseSource.defaultProps = {
|
|
1129
|
-
apiVersion: exports.ApiVersion.V3,
|
|
705
|
+
};
|
|
706
|
+
const SOURCE_DEFAULTS = {
|
|
1130
707
|
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1131
708
|
clientId: getClient(),
|
|
1132
|
-
|
|
1133
|
-
|
|
709
|
+
format: 'tilejson',
|
|
710
|
+
headers: {},
|
|
711
|
+
maxLengthURL: DEFAULT_MAX_LENGTH_URL
|
|
1134
712
|
};
|
|
1135
713
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
* const data = vectorQuerySource({
|
|
1150
|
-
* accessToken: '••••',
|
|
1151
|
-
* connectionName: 'carto_dw',
|
|
1152
|
-
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
1153
|
-
* });
|
|
1154
|
-
*
|
|
1155
|
-
* const { widgetSource } = await data;
|
|
1156
|
-
* ```
|
|
1157
|
-
*/
|
|
1158
|
-
class WidgetQuerySource extends WidgetBaseSource {
|
|
1159
|
-
getModelSource(owner) {
|
|
1160
|
-
return {
|
|
1161
|
-
...super._getModelSource(owner),
|
|
1162
|
-
type: 'query',
|
|
1163
|
-
data: this.props.sqlQuery,
|
|
1164
|
-
queryParameters: this.props.queryParameters
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
/**
|
|
1170
|
-
* Source for Widget API requests on a data source defined as a table.
|
|
1171
|
-
*
|
|
1172
|
-
* Generally not intended to be constructed directly. Instead, call
|
|
1173
|
-
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
1174
|
-
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1175
|
-
* for use by widget implementations.
|
|
1176
|
-
*
|
|
1177
|
-
* Example:
|
|
1178
|
-
*
|
|
1179
|
-
* ```javascript
|
|
1180
|
-
* import { vectorTableSource } from '@carto/api-client';
|
|
1181
|
-
*
|
|
1182
|
-
* const data = vectorTableSource({
|
|
1183
|
-
* accessToken: '••••',
|
|
1184
|
-
* connectionName: 'carto_dw',
|
|
1185
|
-
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
1186
|
-
* });
|
|
1187
|
-
*
|
|
1188
|
-
* const { widgetSource } = await data;
|
|
1189
|
-
* ```
|
|
1190
|
-
*/
|
|
1191
|
-
class WidgetTableSource extends WidgetBaseSource {
|
|
1192
|
-
getModelSource(owner) {
|
|
1193
|
-
return {
|
|
1194
|
-
...super._getModelSource(owner),
|
|
1195
|
-
type: 'table',
|
|
1196
|
-
data: this.props.tableName
|
|
714
|
+
// deck.gl
|
|
715
|
+
const boundaryQuerySource = function (options) {
|
|
716
|
+
try {
|
|
717
|
+
const {
|
|
718
|
+
columns,
|
|
719
|
+
filters,
|
|
720
|
+
tilesetTableName,
|
|
721
|
+
propertiesSqlQuery,
|
|
722
|
+
queryParameters
|
|
723
|
+
} = options;
|
|
724
|
+
const urlParameters = {
|
|
725
|
+
tilesetTableName,
|
|
726
|
+
propertiesSqlQuery
|
|
1197
727
|
};
|
|
728
|
+
if (columns) {
|
|
729
|
+
urlParameters.columns = columns.join(',');
|
|
730
|
+
}
|
|
731
|
+
if (filters) {
|
|
732
|
+
urlParameters.filters = filters;
|
|
733
|
+
}
|
|
734
|
+
if (queryParameters) {
|
|
735
|
+
urlParameters.queryParameters = queryParameters;
|
|
736
|
+
}
|
|
737
|
+
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
738
|
+
} catch (e) {
|
|
739
|
+
return Promise.reject(e);
|
|
1198
740
|
}
|
|
1199
|
-
}
|
|
741
|
+
};
|
|
1200
742
|
|
|
1201
743
|
// deck.gl
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
responseString += 'Bad request';
|
|
1217
|
-
} else if (response.status === 401 || response.status === 403) {
|
|
1218
|
-
responseString += 'Unauthorized access';
|
|
1219
|
-
} else if (response.status === 404) {
|
|
1220
|
-
responseString += 'Not found';
|
|
1221
|
-
} else {
|
|
1222
|
-
responseString += 'Error';
|
|
1223
|
-
}
|
|
1224
|
-
responseString += ` (${response.status}):`;
|
|
744
|
+
const boundaryTableSource = function (options) {
|
|
745
|
+
try {
|
|
746
|
+
const {
|
|
747
|
+
filters,
|
|
748
|
+
tilesetTableName,
|
|
749
|
+
columns,
|
|
750
|
+
propertiesTableName
|
|
751
|
+
} = options;
|
|
752
|
+
const urlParameters = {
|
|
753
|
+
tilesetTableName,
|
|
754
|
+
propertiesTableName
|
|
755
|
+
};
|
|
756
|
+
if (columns) {
|
|
757
|
+
urlParameters.columns = columns.join(',');
|
|
1225
758
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
message += `\n${responseString}`;
|
|
1229
|
-
for (const key of Object.keys(errorContext)) {
|
|
1230
|
-
if (key === 'requestType') continue;
|
|
1231
|
-
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
759
|
+
if (filters) {
|
|
760
|
+
urlParameters.filters = filters;
|
|
1232
761
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
this.error = void 0;
|
|
1237
|
-
/** Context (API call & parameters) in which error occured */
|
|
1238
|
-
this.errorContext = void 0;
|
|
1239
|
-
/** Response from server */
|
|
1240
|
-
this.response = void 0;
|
|
1241
|
-
/** JSON Response from server */
|
|
1242
|
-
this.responseJson = void 0;
|
|
1243
|
-
this.name = 'CartoAPIError';
|
|
1244
|
-
this.response = response;
|
|
1245
|
-
this.responseJson = responseJson;
|
|
1246
|
-
this.error = error;
|
|
1247
|
-
this.errorContext = errorContext;
|
|
762
|
+
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
763
|
+
} catch (e) {
|
|
764
|
+
return Promise.reject(e);
|
|
1248
765
|
}
|
|
1249
|
-
}
|
|
766
|
+
};
|
|
767
|
+
|
|
1250
768
|
/**
|
|
1251
|
-
*
|
|
769
|
+
* Return more descriptive error from API
|
|
770
|
+
* @internalRemarks Source: @carto/react-api
|
|
1252
771
|
*/
|
|
1253
|
-
function formatErrorKey(key) {
|
|
1254
|
-
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
1255
|
-
}
|
|
1256
772
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
return joinPath(apiBaseUrl, version, endpoint, ...[].slice.call(arguments, 3));
|
|
1265
|
-
}
|
|
1266
|
-
/** @internal Required by fetchMap(). */
|
|
1267
|
-
function buildPublicMapUrl(_ref) {
|
|
1268
|
-
let {
|
|
1269
|
-
apiBaseUrl,
|
|
1270
|
-
cartoMapId
|
|
1271
|
-
} = _ref;
|
|
1272
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
1273
|
-
}
|
|
1274
|
-
/** @internal Required by fetchMap(). */
|
|
1275
|
-
function buildStatsUrl(_ref2) {
|
|
1276
|
-
let {
|
|
1277
|
-
attribute,
|
|
1278
|
-
apiBaseUrl,
|
|
1279
|
-
connectionName,
|
|
1280
|
-
source,
|
|
1281
|
-
type
|
|
1282
|
-
} = _ref2;
|
|
1283
|
-
if (type === 'query') {
|
|
1284
|
-
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
773
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
774
|
+
|
|
775
|
+
function _catch(body, recover) {
|
|
776
|
+
try {
|
|
777
|
+
var result = body();
|
|
778
|
+
} catch (e) {
|
|
779
|
+
return recover(e);
|
|
1285
780
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
let {
|
|
1291
|
-
apiBaseUrl,
|
|
1292
|
-
connectionName,
|
|
1293
|
-
endpoint
|
|
1294
|
-
} = _ref3;
|
|
1295
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
1296
|
-
}
|
|
1297
|
-
function buildQueryUrl(_ref4) {
|
|
1298
|
-
let {
|
|
1299
|
-
apiBaseUrl,
|
|
1300
|
-
connectionName
|
|
1301
|
-
} = _ref4;
|
|
1302
|
-
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
781
|
+
if (result && result.then) {
|
|
782
|
+
return result.then(void 0, recover);
|
|
783
|
+
}
|
|
784
|
+
return result;
|
|
1303
785
|
}
|
|
1304
|
-
|
|
1305
|
-
// deck.gl
|
|
1306
|
-
const requestWithParameters = function (_ref) {
|
|
786
|
+
const makeCall = function (_ref2) {
|
|
1307
787
|
let {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
maxLengthURL = DEFAULT_MAX_LENGTH_URL,
|
|
1313
|
-
localCache
|
|
1314
|
-
} = _ref;
|
|
788
|
+
url,
|
|
789
|
+
accessToken,
|
|
790
|
+
opts
|
|
791
|
+
} = _ref2;
|
|
1315
792
|
try {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
};
|
|
1327
|
-
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
1328
|
-
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
1329
|
-
const {
|
|
1330
|
-
cache: REQUEST_CACHE,
|
|
1331
|
-
canReadCache,
|
|
1332
|
-
canStoreInCache
|
|
1333
|
-
} = getCacheSettings(localCache);
|
|
1334
|
-
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
1335
|
-
return Promise.resolve(REQUEST_CACHE.get(key));
|
|
793
|
+
let _exit;
|
|
794
|
+
function _temp2(_result) {
|
|
795
|
+
if (_exit) ;
|
|
796
|
+
if (!response.ok) {
|
|
797
|
+
dealWithApiError({
|
|
798
|
+
response,
|
|
799
|
+
data
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
return data;
|
|
1336
803
|
}
|
|
1337
|
-
const url = createURLWithParameters(baseUrl, parameters);
|
|
1338
|
-
const headers = {
|
|
1339
|
-
...DEFAULT_HEADERS,
|
|
1340
|
-
...customHeaders
|
|
1341
|
-
};
|
|
1342
|
-
/* global fetch */
|
|
1343
|
-
const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
|
|
1344
|
-
method: 'POST',
|
|
1345
|
-
body: JSON.stringify(parameters),
|
|
1346
|
-
headers
|
|
1347
|
-
}) : fetch(url, {
|
|
1348
|
-
headers
|
|
1349
|
-
});
|
|
1350
804
|
let response;
|
|
1351
|
-
let
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
return
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
805
|
+
let data;
|
|
806
|
+
const isPost = opts?.method === 'POST';
|
|
807
|
+
const _temp = _catch(function () {
|
|
808
|
+
return Promise.resolve(fetch(url.toString(), {
|
|
809
|
+
headers: {
|
|
810
|
+
Authorization: `Bearer ${accessToken}`,
|
|
811
|
+
...(isPost && {
|
|
812
|
+
'Content-Type': 'application/json'
|
|
813
|
+
})
|
|
814
|
+
},
|
|
815
|
+
...(isPost && {
|
|
816
|
+
method: opts?.method,
|
|
817
|
+
body: opts?.body
|
|
818
|
+
}),
|
|
819
|
+
signal: opts?.abortController?.signal,
|
|
820
|
+
...opts?.otherOptions
|
|
821
|
+
})).then(function (_fetch) {
|
|
822
|
+
response = _fetch;
|
|
823
|
+
return Promise.resolve(response.json()).then(function (_response$json) {
|
|
824
|
+
data = _response$json;
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
}, function (error) {
|
|
828
|
+
if (error.name === 'AbortError') throw error;
|
|
829
|
+
throw new Error(`Failed request: ${error}`);
|
|
1366
830
|
});
|
|
1367
|
-
|
|
1368
|
-
REQUEST_CACHE.set(key, jsonPromise);
|
|
1369
|
-
}
|
|
1370
|
-
return Promise.resolve(jsonPromise);
|
|
831
|
+
return Promise.resolve(_temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp));
|
|
1371
832
|
} catch (e) {
|
|
1372
833
|
return Promise.reject(e);
|
|
1373
834
|
}
|
|
1374
835
|
};
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
const canStoreInCache = localCache?.cacheControl?.includes('no-store') ? false : true;
|
|
1383
|
-
const cache = localCache?.cache || DEFAULT_REQUEST_CACHE;
|
|
1384
|
-
return {
|
|
1385
|
-
cache,
|
|
1386
|
-
canReadCache,
|
|
1387
|
-
canStoreInCache
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
function createCacheKey(baseUrl, parameters, headers) {
|
|
1391
|
-
const parameterEntries = Object.entries(parameters).sort((_ref2, _ref3) => {
|
|
1392
|
-
let [a] = _ref2;
|
|
1393
|
-
let [b] = _ref3;
|
|
1394
|
-
return a > b ? 1 : -1;
|
|
1395
|
-
});
|
|
1396
|
-
const headerEntries = Object.entries(headers).sort((_ref4, _ref5) => {
|
|
1397
|
-
let [a] = _ref4;
|
|
1398
|
-
let [b] = _ref5;
|
|
1399
|
-
return a > b ? 1 : -1;
|
|
1400
|
-
});
|
|
1401
|
-
return JSON.stringify({
|
|
1402
|
-
baseUrl,
|
|
1403
|
-
parameters: parameterEntries,
|
|
1404
|
-
headers: headerEntries
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
/**
|
|
1408
|
-
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
1409
|
-
* unless there is a conflict, in which case the new parameters override
|
|
1410
|
-
* those already in the URL.
|
|
1411
|
-
*/
|
|
1412
|
-
function createURLWithParameters(baseUrlString, parameters) {
|
|
1413
|
-
const baseUrl = new URL(baseUrlString);
|
|
1414
|
-
for (const [key, value] of Object.entries(parameters)) {
|
|
1415
|
-
if (isPureObject(value) || Array.isArray(value)) {
|
|
1416
|
-
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
1417
|
-
} else {
|
|
1418
|
-
baseUrl.searchParams.set(key, value.toString());
|
|
1419
|
-
}
|
|
836
|
+
function dealWithApiError(_ref) {
|
|
837
|
+
let {
|
|
838
|
+
response,
|
|
839
|
+
data
|
|
840
|
+
} = _ref;
|
|
841
|
+
if (data.error === 'Column not found') {
|
|
842
|
+
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
1420
843
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
844
|
+
if (typeof data.error === 'string' && data.error?.includes('Missing columns')) {
|
|
845
|
+
throw new InvalidColumnError(data.error);
|
|
846
|
+
}
|
|
847
|
+
switch (response.status) {
|
|
848
|
+
case 401:
|
|
849
|
+
throw new Error('Unauthorized access. Invalid credentials');
|
|
850
|
+
case 403:
|
|
851
|
+
throw new Error('Forbidden access to the requested data');
|
|
852
|
+
default:
|
|
853
|
+
const msg = data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify(data?.hint || data.error?.[0]);
|
|
854
|
+
throw new Error(msg);
|
|
1432
855
|
}
|
|
1433
|
-
return baseUrl.toString();
|
|
1434
856
|
}
|
|
1435
857
|
|
|
1436
|
-
|
|
1437
|
-
const
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
858
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
859
|
+
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
860
|
+
const {
|
|
861
|
+
V3
|
|
862
|
+
} = exports.ApiVersion;
|
|
863
|
+
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
864
|
+
/**
|
|
865
|
+
* Execute a SQL model request.
|
|
866
|
+
* @internalRemarks Source: @carto/react-api
|
|
867
|
+
*/
|
|
868
|
+
function executeModel(props) {
|
|
869
|
+
assert(props.source, 'executeModel: missing source');
|
|
870
|
+
assert(props.model, 'executeModel: missing model');
|
|
871
|
+
assert(props.params, 'executeModel: missing params');
|
|
872
|
+
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
873
|
+
const {
|
|
874
|
+
model,
|
|
875
|
+
source,
|
|
876
|
+
params,
|
|
877
|
+
opts
|
|
878
|
+
} = props;
|
|
879
|
+
const {
|
|
880
|
+
type,
|
|
881
|
+
apiVersion,
|
|
882
|
+
apiBaseUrl,
|
|
883
|
+
accessToken,
|
|
884
|
+
connectionName,
|
|
885
|
+
clientId
|
|
886
|
+
} = source;
|
|
887
|
+
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
888
|
+
assert(accessToken, 'executeModel: missing accessToken');
|
|
889
|
+
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
890
|
+
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
891
|
+
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
892
|
+
const {
|
|
893
|
+
data,
|
|
894
|
+
filters,
|
|
895
|
+
filtersLogicalOperator = 'and',
|
|
896
|
+
geoColumn = DEFAULT_GEO_COLUMN
|
|
897
|
+
} = source;
|
|
898
|
+
const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
|
|
899
|
+
const queryParams = {
|
|
900
|
+
type,
|
|
901
|
+
client: clientId,
|
|
902
|
+
source: data,
|
|
903
|
+
params: JSON.stringify(params),
|
|
904
|
+
queryParameters,
|
|
905
|
+
filters: JSON.stringify(filters),
|
|
906
|
+
filtersLogicalOperator
|
|
907
|
+
};
|
|
908
|
+
// Picking Model API requires 'spatialDataColumn'.
|
|
909
|
+
if (model === 'pick') {
|
|
910
|
+
queryParams.spatialDataColumn = geoColumn;
|
|
911
|
+
}
|
|
912
|
+
// API supports multiple filters, we apply it only to geoColumn
|
|
913
|
+
const spatialFilters = source.spatialFilter ? {
|
|
914
|
+
[geoColumn]: source.spatialFilter
|
|
915
|
+
} : undefined;
|
|
916
|
+
if (spatialFilters) {
|
|
917
|
+
queryParams.spatialFilters = JSON.stringify(spatialFilters);
|
|
918
|
+
}
|
|
919
|
+
const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
|
|
920
|
+
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
921
|
+
if (isGet) {
|
|
922
|
+
url = urlWithSearchParams;
|
|
923
|
+
} else {
|
|
924
|
+
// undo the JSON.stringify, @TODO find a better pattern
|
|
925
|
+
queryParams.params = params;
|
|
926
|
+
queryParams.filters = filters;
|
|
927
|
+
queryParams.queryParameters = source.queryParameters;
|
|
928
|
+
if (spatialFilters) {
|
|
929
|
+
queryParams.spatialFilters = spatialFilters;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return makeCall({
|
|
933
|
+
url,
|
|
934
|
+
accessToken: source.accessToken,
|
|
935
|
+
opts: {
|
|
936
|
+
...opts,
|
|
937
|
+
method: isGet ? 'GET' : 'POST',
|
|
938
|
+
...(!isGet && {
|
|
939
|
+
body: JSON.stringify(queryParams)
|
|
940
|
+
})
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
947
|
+
*
|
|
948
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
949
|
+
*/
|
|
950
|
+
class WidgetBaseSource {
|
|
951
|
+
constructor(props) {
|
|
952
|
+
this.props = void 0;
|
|
953
|
+
this.props = {
|
|
954
|
+
...WidgetBaseSource.defaultProps,
|
|
955
|
+
...props
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
_getModelSource(owner) {
|
|
959
|
+
const props = this.props;
|
|
960
|
+
return {
|
|
961
|
+
apiVersion: props.apiVersion,
|
|
962
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
963
|
+
clientId: props.clientId,
|
|
964
|
+
accessToken: props.accessToken,
|
|
965
|
+
connectionName: props.connectionName,
|
|
966
|
+
filters: getApplicableFilters(owner, props.filters),
|
|
967
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
968
|
+
geoColumn: props.geoColumn
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
/****************************************************************************
|
|
972
|
+
* CATEGORIES
|
|
973
|
+
*/
|
|
974
|
+
/**
|
|
975
|
+
* Returns a list of labeled datapoints for categorical data. Suitable for
|
|
976
|
+
* charts including grouped bar charts, pie charts, and tree charts.
|
|
977
|
+
*/
|
|
978
|
+
getCategories(options) {
|
|
979
|
+
try {
|
|
980
|
+
const _this = this;
|
|
981
|
+
const {
|
|
982
|
+
filterOwner,
|
|
983
|
+
spatialFilter,
|
|
984
|
+
abortController,
|
|
985
|
+
...params
|
|
986
|
+
} = options;
|
|
987
|
+
const {
|
|
988
|
+
column,
|
|
989
|
+
operation,
|
|
990
|
+
operationColumn
|
|
991
|
+
} = params;
|
|
992
|
+
return Promise.resolve(executeModel({
|
|
993
|
+
model: 'category',
|
|
994
|
+
source: {
|
|
995
|
+
..._this.getModelSource(filterOwner),
|
|
996
|
+
spatialFilter
|
|
997
|
+
},
|
|
998
|
+
params: {
|
|
999
|
+
column,
|
|
1000
|
+
operation,
|
|
1001
|
+
operationColumn: operationColumn || column
|
|
1002
|
+
},
|
|
1003
|
+
opts: {
|
|
1004
|
+
abortController
|
|
1005
|
+
}
|
|
1006
|
+
}).then(res => normalizeObjectKeys(res.rows)));
|
|
1007
|
+
} catch (e) {
|
|
1008
|
+
return Promise.reject(e);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
/****************************************************************************
|
|
1012
|
+
* FEATURES
|
|
1013
|
+
*/
|
|
1014
|
+
/**
|
|
1015
|
+
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
1016
|
+
* matching features. In datasets containing features with duplicate geometries,
|
|
1017
|
+
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
1018
|
+
* results may be returned than IDs in the request.
|
|
1019
|
+
* @internal
|
|
1020
|
+
* @experimental
|
|
1021
|
+
*/
|
|
1022
|
+
getFeatures(options) {
|
|
1023
|
+
try {
|
|
1024
|
+
const _this2 = this;
|
|
1025
|
+
const {
|
|
1026
|
+
filterOwner,
|
|
1027
|
+
spatialFilter,
|
|
1028
|
+
abortController,
|
|
1029
|
+
...params
|
|
1030
|
+
} = options;
|
|
1031
|
+
const {
|
|
1032
|
+
columns,
|
|
1033
|
+
dataType,
|
|
1034
|
+
featureIds,
|
|
1035
|
+
z,
|
|
1036
|
+
limit,
|
|
1037
|
+
tileResolution
|
|
1038
|
+
} = params;
|
|
1039
|
+
return Promise.resolve(executeModel({
|
|
1040
|
+
model: 'pick',
|
|
1041
|
+
source: {
|
|
1042
|
+
..._this2.getModelSource(filterOwner),
|
|
1043
|
+
spatialFilter
|
|
1044
|
+
},
|
|
1045
|
+
params: {
|
|
1046
|
+
columns,
|
|
1047
|
+
dataType,
|
|
1048
|
+
featureIds,
|
|
1049
|
+
z,
|
|
1050
|
+
limit: limit || 1000,
|
|
1051
|
+
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
1052
|
+
},
|
|
1053
|
+
opts: {
|
|
1054
|
+
abortController
|
|
1055
|
+
}
|
|
1056
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1057
|
+
}).then(_ref => {
|
|
1058
|
+
let {
|
|
1059
|
+
rows
|
|
1060
|
+
} = _ref;
|
|
1061
|
+
return {
|
|
1062
|
+
rows
|
|
1063
|
+
};
|
|
1064
|
+
}));
|
|
1065
|
+
} catch (e) {
|
|
1066
|
+
return Promise.reject(e);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
/****************************************************************************
|
|
1070
|
+
* FORMULA
|
|
1071
|
+
*/
|
|
1072
|
+
/**
|
|
1073
|
+
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1074
|
+
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1075
|
+
*/
|
|
1076
|
+
getFormula(options) {
|
|
1077
|
+
try {
|
|
1078
|
+
const _this3 = this;
|
|
1079
|
+
const {
|
|
1080
|
+
filterOwner,
|
|
1081
|
+
spatialFilter,
|
|
1082
|
+
abortController,
|
|
1083
|
+
operationExp,
|
|
1084
|
+
...params
|
|
1085
|
+
} = options;
|
|
1086
|
+
const {
|
|
1087
|
+
column,
|
|
1088
|
+
operation
|
|
1089
|
+
} = params;
|
|
1090
|
+
return Promise.resolve(executeModel({
|
|
1091
|
+
model: 'formula',
|
|
1092
|
+
source: {
|
|
1093
|
+
..._this3.getModelSource(filterOwner),
|
|
1094
|
+
spatialFilter
|
|
1095
|
+
},
|
|
1096
|
+
params: {
|
|
1097
|
+
column: column ?? '*',
|
|
1098
|
+
operation,
|
|
1099
|
+
operationExp
|
|
1100
|
+
},
|
|
1101
|
+
opts: {
|
|
1102
|
+
abortController
|
|
1103
|
+
}
|
|
1104
|
+
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1105
|
+
} catch (e) {
|
|
1106
|
+
return Promise.reject(e);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/****************************************************************************
|
|
1110
|
+
* HISTOGRAM
|
|
1111
|
+
*/
|
|
1112
|
+
/**
|
|
1113
|
+
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1114
|
+
* over a numerical range. Suitable for histogram charts.
|
|
1115
|
+
*/
|
|
1116
|
+
getHistogram(options) {
|
|
1117
|
+
try {
|
|
1118
|
+
const _this4 = this;
|
|
1119
|
+
const {
|
|
1120
|
+
filterOwner,
|
|
1121
|
+
spatialFilter,
|
|
1122
|
+
abortController,
|
|
1123
|
+
...params
|
|
1124
|
+
} = options;
|
|
1125
|
+
const {
|
|
1126
|
+
column,
|
|
1127
|
+
operation,
|
|
1128
|
+
ticks
|
|
1129
|
+
} = params;
|
|
1130
|
+
return Promise.resolve(executeModel({
|
|
1131
|
+
model: 'histogram',
|
|
1132
|
+
source: {
|
|
1133
|
+
..._this4.getModelSource(filterOwner),
|
|
1134
|
+
spatialFilter
|
|
1135
|
+
},
|
|
1136
|
+
params: {
|
|
1137
|
+
column,
|
|
1138
|
+
operation,
|
|
1139
|
+
ticks
|
|
1140
|
+
},
|
|
1141
|
+
opts: {
|
|
1142
|
+
abortController
|
|
1143
|
+
}
|
|
1144
|
+
}).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
|
|
1145
|
+
if (data.length) {
|
|
1146
|
+
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
1147
|
+
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
1148
|
+
const result = Array(ticks.length + 1).fill(0);
|
|
1149
|
+
data.forEach(_ref2 => {
|
|
1150
|
+
let {
|
|
1151
|
+
tick,
|
|
1152
|
+
value
|
|
1153
|
+
} = _ref2;
|
|
1154
|
+
return result[tick] = value;
|
|
1155
|
+
});
|
|
1156
|
+
return result;
|
|
1157
|
+
}
|
|
1158
|
+
return [];
|
|
1159
|
+
});
|
|
1160
|
+
} catch (e) {
|
|
1161
|
+
return Promise.reject(e);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
/****************************************************************************
|
|
1165
|
+
* RANGE
|
|
1166
|
+
*/
|
|
1167
|
+
/**
|
|
1168
|
+
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1169
|
+
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1170
|
+
* or rendering a range slider UI for filtering.
|
|
1171
|
+
*/
|
|
1172
|
+
getRange(options) {
|
|
1173
|
+
try {
|
|
1174
|
+
const _this5 = this;
|
|
1175
|
+
const {
|
|
1176
|
+
filterOwner,
|
|
1177
|
+
spatialFilter,
|
|
1178
|
+
abortController,
|
|
1179
|
+
...params
|
|
1180
|
+
} = options;
|
|
1181
|
+
const {
|
|
1182
|
+
column
|
|
1183
|
+
} = params;
|
|
1184
|
+
return Promise.resolve(executeModel({
|
|
1185
|
+
model: 'range',
|
|
1186
|
+
source: {
|
|
1187
|
+
..._this5.getModelSource(filterOwner),
|
|
1188
|
+
spatialFilter
|
|
1189
|
+
},
|
|
1190
|
+
params: {
|
|
1191
|
+
column
|
|
1192
|
+
},
|
|
1193
|
+
opts: {
|
|
1194
|
+
abortController
|
|
1195
|
+
}
|
|
1196
|
+
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1197
|
+
} catch (e) {
|
|
1198
|
+
return Promise.reject(e);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/****************************************************************************
|
|
1202
|
+
* SCATTER
|
|
1203
|
+
*/
|
|
1204
|
+
/**
|
|
1205
|
+
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1206
|
+
* values. Suitable for rendering scatter plots.
|
|
1207
|
+
*/
|
|
1208
|
+
getScatter(options) {
|
|
1209
|
+
try {
|
|
1210
|
+
const _this6 = this;
|
|
1211
|
+
const {
|
|
1212
|
+
filterOwner,
|
|
1213
|
+
spatialFilter,
|
|
1214
|
+
abortController,
|
|
1215
|
+
...params
|
|
1216
|
+
} = options;
|
|
1217
|
+
const {
|
|
1218
|
+
xAxisColumn,
|
|
1219
|
+
xAxisJoinOperation,
|
|
1220
|
+
yAxisColumn,
|
|
1221
|
+
yAxisJoinOperation
|
|
1222
|
+
} = params;
|
|
1223
|
+
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1224
|
+
const HARD_LIMIT = 500;
|
|
1225
|
+
return Promise.resolve(executeModel({
|
|
1226
|
+
model: 'scatterplot',
|
|
1227
|
+
source: {
|
|
1228
|
+
..._this6.getModelSource(filterOwner),
|
|
1229
|
+
spatialFilter
|
|
1230
|
+
},
|
|
1231
|
+
params: {
|
|
1232
|
+
xAxisColumn,
|
|
1233
|
+
xAxisJoinOperation,
|
|
1234
|
+
yAxisColumn,
|
|
1235
|
+
yAxisJoinOperation,
|
|
1236
|
+
limit: HARD_LIMIT
|
|
1237
|
+
},
|
|
1238
|
+
opts: {
|
|
1239
|
+
abortController
|
|
1240
|
+
}
|
|
1241
|
+
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(_ref3 => {
|
|
1242
|
+
let {
|
|
1243
|
+
x,
|
|
1244
|
+
y
|
|
1245
|
+
} = _ref3;
|
|
1246
|
+
return [x, y];
|
|
1247
|
+
})));
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
return Promise.reject(e);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
/****************************************************************************
|
|
1253
|
+
* TABLE
|
|
1254
|
+
*/
|
|
1255
|
+
/**
|
|
1256
|
+
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1257
|
+
* sorting. Suitable for displaying tables and lists.
|
|
1258
|
+
*/
|
|
1259
|
+
getTable(options) {
|
|
1260
|
+
try {
|
|
1261
|
+
const _this7 = this;
|
|
1262
|
+
const {
|
|
1263
|
+
filterOwner,
|
|
1264
|
+
spatialFilter,
|
|
1265
|
+
abortController,
|
|
1266
|
+
...params
|
|
1267
|
+
} = options;
|
|
1268
|
+
const {
|
|
1269
|
+
columns,
|
|
1270
|
+
sortBy,
|
|
1271
|
+
sortDirection,
|
|
1272
|
+
offset = 0,
|
|
1273
|
+
limit = 10
|
|
1274
|
+
} = params;
|
|
1275
|
+
return Promise.resolve(executeModel({
|
|
1276
|
+
model: 'table',
|
|
1277
|
+
source: {
|
|
1278
|
+
..._this7.getModelSource(filterOwner),
|
|
1279
|
+
spatialFilter
|
|
1280
|
+
},
|
|
1281
|
+
params: {
|
|
1282
|
+
column: columns,
|
|
1283
|
+
sortBy,
|
|
1284
|
+
sortDirection,
|
|
1285
|
+
limit,
|
|
1286
|
+
offset
|
|
1287
|
+
},
|
|
1288
|
+
opts: {
|
|
1289
|
+
abortController
|
|
1290
|
+
}
|
|
1291
|
+
}).then(res => ({
|
|
1292
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1293
|
+
rows: res.rows ?? res.ROWS,
|
|
1294
|
+
totalCount: res.metadata?.total ?? res.METADATA?.TOTAL
|
|
1295
|
+
})));
|
|
1296
|
+
} catch (e) {
|
|
1297
|
+
return Promise.reject(e);
|
|
1455
1298
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
baseUrl: dataUrl,
|
|
1504
|
-
headers,
|
|
1505
|
-
errorContext,
|
|
1506
|
-
maxLengthURL,
|
|
1507
|
-
localCache
|
|
1508
|
-
})).then(function (json) {
|
|
1509
|
-
if (accessToken) {
|
|
1510
|
-
json.accessToken = accessToken;
|
|
1511
|
-
}
|
|
1512
|
-
_exit = 1;
|
|
1513
|
-
return json;
|
|
1514
|
-
});
|
|
1299
|
+
}
|
|
1300
|
+
/****************************************************************************
|
|
1301
|
+
* TIME SERIES
|
|
1302
|
+
*/
|
|
1303
|
+
/**
|
|
1304
|
+
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1305
|
+
* time intervals. Suitable for rendering time series charts.
|
|
1306
|
+
*/
|
|
1307
|
+
getTimeSeries(options) {
|
|
1308
|
+
try {
|
|
1309
|
+
const _this8 = this;
|
|
1310
|
+
const {
|
|
1311
|
+
filterOwner,
|
|
1312
|
+
abortController,
|
|
1313
|
+
spatialFilter,
|
|
1314
|
+
...params
|
|
1315
|
+
} = options;
|
|
1316
|
+
const {
|
|
1317
|
+
column,
|
|
1318
|
+
operationColumn,
|
|
1319
|
+
joinOperation,
|
|
1320
|
+
operation,
|
|
1321
|
+
stepSize,
|
|
1322
|
+
stepMultiplier,
|
|
1323
|
+
splitByCategory,
|
|
1324
|
+
splitByCategoryLimit,
|
|
1325
|
+
splitByCategoryValues
|
|
1326
|
+
} = params;
|
|
1327
|
+
return Promise.resolve(executeModel({
|
|
1328
|
+
model: 'timeseries',
|
|
1329
|
+
source: {
|
|
1330
|
+
..._this8.getModelSource(filterOwner),
|
|
1331
|
+
spatialFilter
|
|
1332
|
+
},
|
|
1333
|
+
params: {
|
|
1334
|
+
column,
|
|
1335
|
+
stepSize,
|
|
1336
|
+
stepMultiplier,
|
|
1337
|
+
operationColumn: operationColumn || column,
|
|
1338
|
+
joinOperation,
|
|
1339
|
+
operation,
|
|
1340
|
+
splitByCategory,
|
|
1341
|
+
splitByCategoryLimit,
|
|
1342
|
+
splitByCategoryValues
|
|
1343
|
+
},
|
|
1344
|
+
opts: {
|
|
1345
|
+
abortController
|
|
1515
1346
|
}
|
|
1516
|
-
}(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1347
|
+
}).then(res => ({
|
|
1348
|
+
rows: normalizeObjectKeys(res.rows),
|
|
1349
|
+
categories: res.metadata?.categories
|
|
1350
|
+
})));
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
return Promise.reject(e);
|
|
1353
|
+
}
|
|
1521
1354
|
}
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1355
|
+
}
|
|
1356
|
+
WidgetBaseSource.defaultProps = {
|
|
1357
|
+
apiVersion: exports.ApiVersion.V3,
|
|
1524
1358
|
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1525
1359
|
clientId: getClient(),
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1360
|
+
filters: {},
|
|
1361
|
+
filtersLogicalOperator: 'and',
|
|
1362
|
+
geoColumn: DEFAULT_GEO_COLUMN
|
|
1529
1363
|
};
|
|
1530
1364
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1365
|
+
/**
|
|
1366
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
1367
|
+
*
|
|
1368
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1369
|
+
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
1370
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1371
|
+
* for use by widget implementations.
|
|
1372
|
+
*
|
|
1373
|
+
* Example:
|
|
1374
|
+
*
|
|
1375
|
+
* ```javascript
|
|
1376
|
+
* import { vectorQuerySource } from '@carto/api-client';
|
|
1377
|
+
*
|
|
1378
|
+
* const data = vectorQuerySource({
|
|
1379
|
+
* accessToken: '••••',
|
|
1380
|
+
* connectionName: 'carto_dw',
|
|
1381
|
+
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
1382
|
+
* });
|
|
1383
|
+
*
|
|
1384
|
+
* const { widgetSource } = await data;
|
|
1385
|
+
* ```
|
|
1386
|
+
*/
|
|
1387
|
+
class WidgetQuerySource extends WidgetBaseSource {
|
|
1388
|
+
getModelSource(owner) {
|
|
1389
|
+
return {
|
|
1390
|
+
...super._getModelSource(owner),
|
|
1391
|
+
type: 'query',
|
|
1392
|
+
data: this.props.sqlQuery,
|
|
1393
|
+
queryParameters: this.props.queryParameters
|
|
1544
1394
|
};
|
|
1545
|
-
if (columns) {
|
|
1546
|
-
urlParameters.columns = columns.join(',');
|
|
1547
|
-
}
|
|
1548
|
-
if (filters) {
|
|
1549
|
-
urlParameters.filters = filters;
|
|
1550
|
-
}
|
|
1551
|
-
if (queryParameters) {
|
|
1552
|
-
urlParameters.queryParameters = queryParameters;
|
|
1553
|
-
}
|
|
1554
|
-
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
1555
|
-
} catch (e) {
|
|
1556
|
-
return Promise.reject(e);
|
|
1557
1395
|
}
|
|
1558
|
-
}
|
|
1396
|
+
}
|
|
1559
1397
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1398
|
+
/**
|
|
1399
|
+
* Source for Widget API requests on a data source defined as a table.
|
|
1400
|
+
*
|
|
1401
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1402
|
+
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
1403
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1404
|
+
* for use by widget implementations.
|
|
1405
|
+
*
|
|
1406
|
+
* Example:
|
|
1407
|
+
*
|
|
1408
|
+
* ```javascript
|
|
1409
|
+
* import { vectorTableSource } from '@carto/api-client';
|
|
1410
|
+
*
|
|
1411
|
+
* const data = vectorTableSource({
|
|
1412
|
+
* accessToken: '••••',
|
|
1413
|
+
* connectionName: 'carto_dw',
|
|
1414
|
+
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
1415
|
+
* });
|
|
1416
|
+
*
|
|
1417
|
+
* const { widgetSource } = await data;
|
|
1418
|
+
* ```
|
|
1419
|
+
*/
|
|
1420
|
+
class WidgetTableSource extends WidgetBaseSource {
|
|
1421
|
+
getModelSource(owner) {
|
|
1422
|
+
return {
|
|
1423
|
+
...super._getModelSource(owner),
|
|
1424
|
+
type: 'table',
|
|
1425
|
+
data: this.props.tableName
|
|
1572
1426
|
};
|
|
1573
|
-
if (columns) {
|
|
1574
|
-
urlParameters.columns = columns.join(',');
|
|
1575
|
-
}
|
|
1576
|
-
if (filters) {
|
|
1577
|
-
urlParameters.filters = filters;
|
|
1578
|
-
}
|
|
1579
|
-
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
1580
|
-
} catch (e) {
|
|
1581
|
-
return Promise.reject(e);
|
|
1582
1427
|
}
|
|
1583
|
-
}
|
|
1428
|
+
}
|
|
1584
1429
|
|
|
1585
1430
|
// deck.gl
|
|
1586
1431
|
const h3QuerySource = function (options) {
|
|
@@ -1610,12 +1455,7 @@ const h3QuerySource = function (options) {
|
|
|
1610
1455
|
}
|
|
1611
1456
|
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
1612
1457
|
...result,
|
|
1613
|
-
widgetSource: new WidgetQuerySource(
|
|
1614
|
-
...options,
|
|
1615
|
-
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
1616
|
-
spatialDataColumn,
|
|
1617
|
-
spatialDataType: 'h3'
|
|
1618
|
-
})
|
|
1458
|
+
widgetSource: new WidgetQuerySource(options)
|
|
1619
1459
|
})));
|
|
1620
1460
|
} catch (e) {
|
|
1621
1461
|
return Promise.reject(e);
|
|
@@ -1646,12 +1486,7 @@ const h3TableSource = function (options) {
|
|
|
1646
1486
|
}
|
|
1647
1487
|
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
1648
1488
|
...result,
|
|
1649
|
-
widgetSource: new WidgetTableSource(
|
|
1650
|
-
...options,
|
|
1651
|
-
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
1652
|
-
spatialDataColumn,
|
|
1653
|
-
spatialDataType: 'h3'
|
|
1654
|
-
})
|
|
1489
|
+
widgetSource: new WidgetTableSource(options)
|
|
1655
1490
|
})));
|
|
1656
1491
|
} catch (e) {
|
|
1657
1492
|
return Promise.reject(e);
|
|
@@ -1720,12 +1555,7 @@ const quadbinQuerySource = function (options) {
|
|
|
1720
1555
|
}
|
|
1721
1556
|
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
1722
1557
|
...result,
|
|
1723
|
-
widgetSource: new WidgetQuerySource(
|
|
1724
|
-
...options,
|
|
1725
|
-
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
1726
|
-
spatialDataColumn,
|
|
1727
|
-
spatialDataType: 'quadbin'
|
|
1728
|
-
})
|
|
1558
|
+
widgetSource: new WidgetQuerySource(options)
|
|
1729
1559
|
})));
|
|
1730
1560
|
} catch (e) {
|
|
1731
1561
|
return Promise.reject(e);
|
|
@@ -1756,12 +1586,7 @@ const quadbinTableSource = function (options) {
|
|
|
1756
1586
|
}
|
|
1757
1587
|
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
1758
1588
|
...result,
|
|
1759
|
-
widgetSource: new WidgetTableSource(
|
|
1760
|
-
...options,
|
|
1761
|
-
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
1762
|
-
spatialDataColumn,
|
|
1763
|
-
spatialDataType: 'quadbin'
|
|
1764
|
-
})
|
|
1589
|
+
widgetSource: new WidgetTableSource(options)
|
|
1765
1590
|
})));
|
|
1766
1591
|
} catch (e) {
|
|
1767
1592
|
return Promise.reject(e);
|
|
@@ -1792,7 +1617,8 @@ const vectorQuerySource = function (options) {
|
|
|
1792
1617
|
spatialDataColumn = 'geom',
|
|
1793
1618
|
sqlQuery,
|
|
1794
1619
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1795
|
-
queryParameters
|
|
1620
|
+
queryParameters,
|
|
1621
|
+
aggregationExp
|
|
1796
1622
|
} = options;
|
|
1797
1623
|
const urlParameters = {
|
|
1798
1624
|
spatialDataColumn,
|
|
@@ -1809,12 +1635,12 @@ const vectorQuerySource = function (options) {
|
|
|
1809
1635
|
if (queryParameters) {
|
|
1810
1636
|
urlParameters.queryParameters = queryParameters;
|
|
1811
1637
|
}
|
|
1638
|
+
if (aggregationExp) {
|
|
1639
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1640
|
+
}
|
|
1812
1641
|
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
1813
1642
|
...result,
|
|
1814
|
-
widgetSource: new WidgetQuerySource(
|
|
1815
|
-
...options,
|
|
1816
|
-
spatialDataType: 'geo'
|
|
1817
|
-
})
|
|
1643
|
+
widgetSource: new WidgetQuerySource(options)
|
|
1818
1644
|
})));
|
|
1819
1645
|
} catch (e) {
|
|
1820
1646
|
return Promise.reject(e);
|
|
@@ -1829,7 +1655,8 @@ const vectorTableSource = function (options) {
|
|
|
1829
1655
|
filters,
|
|
1830
1656
|
spatialDataColumn = 'geom',
|
|
1831
1657
|
tableName,
|
|
1832
|
-
tileResolution = DEFAULT_TILE_RESOLUTION
|
|
1658
|
+
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1659
|
+
aggregationExp
|
|
1833
1660
|
} = options;
|
|
1834
1661
|
const urlParameters = {
|
|
1835
1662
|
name: tableName,
|
|
@@ -1843,12 +1670,12 @@ const vectorTableSource = function (options) {
|
|
|
1843
1670
|
if (filters) {
|
|
1844
1671
|
urlParameters.filters = filters;
|
|
1845
1672
|
}
|
|
1673
|
+
if (aggregationExp) {
|
|
1674
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1675
|
+
}
|
|
1846
1676
|
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
1847
1677
|
...result,
|
|
1848
|
-
widgetSource: new WidgetTableSource(
|
|
1849
|
-
...options,
|
|
1850
|
-
spatialDataType: 'geo'
|
|
1851
|
-
})
|
|
1678
|
+
widgetSource: new WidgetTableSource(options)
|
|
1852
1679
|
})));
|
|
1853
1680
|
} catch (e) {
|
|
1854
1681
|
return Promise.reject(e);
|