@carto/api-client 0.4.2 → 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 +4 -0
- package/build/api-client.cjs +933 -925
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +873 -865
- package/build/api-client.modern.js.map +1 -1
- package/build/index.d.ts +1 -1
- 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 +16 -0
- 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/package.json +1 -1
- package/src/index.ts +1 -36
- 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 +3 -1
- package/src/sources/h3-table-source.ts +3 -1
- package/src/sources/h3-tileset-source.ts +4 -2
- package/src/sources/index.ts +54 -24
- package/src/sources/quadbin-query-source.ts +4 -1
- package/src/sources/quadbin-table-source.ts +4 -1
- package/src/sources/quadbin-tileset-source.ts +4 -2
- package/src/sources/raster-source.ts +4 -2
- package/src/sources/types.ts +18 -0
- package/src/sources/vector-query-source.ts +9 -1
- package/src/sources/vector-table-source.ts +10 -1
- package/src/sources/vector-tileset-source.ts +4 -2
|
@@ -393,926 +393,926 @@ const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
|
393
393
|
*/
|
|
394
394
|
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
395
395
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
396
|
+
// deck.gl
|
|
397
|
+
// SPDX-License-Identifier: MIT
|
|
398
|
+
// Copyright (c) vis.gl contributors
|
|
399
|
+
function joinPath(...args) {
|
|
400
|
+
return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
|
|
401
|
+
}
|
|
402
|
+
function buildV3Path(apiBaseUrl, version, endpoint, ...rest) {
|
|
403
|
+
return joinPath(apiBaseUrl, version, endpoint, ...rest);
|
|
404
|
+
}
|
|
405
|
+
/** @internal Required by fetchMap(). */
|
|
406
|
+
function buildPublicMapUrl({
|
|
407
|
+
apiBaseUrl,
|
|
408
|
+
cartoMapId
|
|
403
409
|
}) {
|
|
404
|
-
|
|
405
|
-
if (data.error === 'Column not found') {
|
|
406
|
-
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
407
|
-
}
|
|
408
|
-
if (typeof data.error === 'string' && (_data$error = data.error) != null && _data$error.includes('Missing columns')) {
|
|
409
|
-
throw new InvalidColumnError(data.error);
|
|
410
|
-
}
|
|
411
|
-
switch (response.status) {
|
|
412
|
-
case 401:
|
|
413
|
-
throw new Error('Unauthorized access. Invalid credentials');
|
|
414
|
-
case 403:
|
|
415
|
-
throw new Error('Forbidden access to the requested data');
|
|
416
|
-
default:
|
|
417
|
-
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]));
|
|
418
|
-
throw new Error(msg);
|
|
419
|
-
}
|
|
410
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
420
411
|
}
|
|
421
|
-
/** @
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
412
|
+
/** @internal Required by fetchMap(). */
|
|
413
|
+
function buildStatsUrl({
|
|
414
|
+
attribute,
|
|
415
|
+
apiBaseUrl,
|
|
416
|
+
connectionName,
|
|
417
|
+
source,
|
|
418
|
+
type
|
|
426
419
|
}) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const isPost = (opts == null ? void 0 : opts.method) === 'POST';
|
|
430
|
-
try {
|
|
431
|
-
var _opts$abortController;
|
|
432
|
-
response = await fetch(url.toString(), _extends({
|
|
433
|
-
headers: _extends({
|
|
434
|
-
Authorization: `Bearer ${accessToken}`
|
|
435
|
-
}, isPost && {
|
|
436
|
-
'Content-Type': 'application/json'
|
|
437
|
-
})
|
|
438
|
-
}, isPost && {
|
|
439
|
-
method: opts == null ? void 0 : opts.method,
|
|
440
|
-
body: opts == null ? void 0 : opts.body
|
|
441
|
-
}, {
|
|
442
|
-
signal: opts == null || (_opts$abortController = opts.abortController) == null ? void 0 : _opts$abortController.signal
|
|
443
|
-
}, opts == null ? void 0 : opts.otherOptions));
|
|
444
|
-
data = await response.json();
|
|
445
|
-
} catch (error) {
|
|
446
|
-
if (error.name === 'AbortError') throw error;
|
|
447
|
-
throw new Error(`Failed request: ${error}`);
|
|
448
|
-
}
|
|
449
|
-
if (!response.ok) {
|
|
450
|
-
dealWithApiError({
|
|
451
|
-
response,
|
|
452
|
-
data
|
|
453
|
-
});
|
|
420
|
+
if (type === 'query') {
|
|
421
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
454
422
|
}
|
|
455
|
-
|
|
423
|
+
// type === 'table'
|
|
424
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
425
|
+
}
|
|
426
|
+
function buildSourceUrl({
|
|
427
|
+
apiBaseUrl,
|
|
428
|
+
connectionName,
|
|
429
|
+
endpoint
|
|
430
|
+
}) {
|
|
431
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
432
|
+
}
|
|
433
|
+
function buildQueryUrl({
|
|
434
|
+
apiBaseUrl,
|
|
435
|
+
connectionName
|
|
436
|
+
}) {
|
|
437
|
+
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
456
438
|
}
|
|
457
439
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
V3
|
|
462
|
-
} = ApiVersion;
|
|
463
|
-
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
440
|
+
// deck.gl
|
|
441
|
+
// SPDX-License-Identifier: MIT
|
|
442
|
+
// Copyright (c) vis.gl contributors
|
|
464
443
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
444
|
+
*
|
|
445
|
+
* Custom error for reported errors in CARTO Maps API.
|
|
446
|
+
* Provides useful debugging information in console and context for applications.
|
|
447
|
+
*
|
|
467
448
|
*/
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
491
|
-
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
492
|
-
const {
|
|
493
|
-
data,
|
|
494
|
-
filters,
|
|
495
|
-
filtersLogicalOperator = 'and',
|
|
496
|
-
geoColumn = DEFAULT_GEO_COLUMN
|
|
497
|
-
} = source;
|
|
498
|
-
const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
|
|
499
|
-
const queryParams = {
|
|
500
|
-
type,
|
|
501
|
-
client: clientId,
|
|
502
|
-
source: data,
|
|
503
|
-
params: JSON.stringify(params),
|
|
504
|
-
queryParameters,
|
|
505
|
-
filters: JSON.stringify(filters),
|
|
506
|
-
filtersLogicalOperator
|
|
507
|
-
};
|
|
508
|
-
// Picking Model API requires 'spatialDataColumn'.
|
|
509
|
-
if (model === 'pick') {
|
|
510
|
-
queryParams.spatialDataColumn = geoColumn;
|
|
511
|
-
}
|
|
512
|
-
// API supports multiple filters, we apply it only to geoColumn
|
|
513
|
-
const spatialFilters = source.spatialFilter ? {
|
|
514
|
-
[geoColumn]: source.spatialFilter
|
|
515
|
-
} : undefined;
|
|
516
|
-
if (spatialFilters) {
|
|
517
|
-
queryParams.spatialFilters = JSON.stringify(spatialFilters);
|
|
518
|
-
}
|
|
519
|
-
const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
|
|
520
|
-
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
521
|
-
if (isGet) {
|
|
522
|
-
url = urlWithSearchParams;
|
|
523
|
-
} else {
|
|
524
|
-
// undo the JSON.stringify, @TODO find a better pattern
|
|
525
|
-
queryParams.params = params;
|
|
526
|
-
queryParams.filters = filters;
|
|
527
|
-
queryParams.queryParameters = source.queryParameters;
|
|
528
|
-
if (spatialFilters) {
|
|
529
|
-
queryParams.spatialFilters = spatialFilters;
|
|
449
|
+
class CartoAPIError extends Error {
|
|
450
|
+
constructor(error, errorContext, response, responseJson) {
|
|
451
|
+
let responseString = 'Failed to connect';
|
|
452
|
+
if (response) {
|
|
453
|
+
responseString = 'Server returned: ';
|
|
454
|
+
if (response.status === 400) {
|
|
455
|
+
responseString += 'Bad request';
|
|
456
|
+
} else if (response.status === 401 || response.status === 403) {
|
|
457
|
+
responseString += 'Unauthorized access';
|
|
458
|
+
} else if (response.status === 404) {
|
|
459
|
+
responseString += 'Not found';
|
|
460
|
+
} else {
|
|
461
|
+
responseString += 'Error';
|
|
462
|
+
}
|
|
463
|
+
responseString += ` (${response.status}):`;
|
|
464
|
+
}
|
|
465
|
+
responseString += ` ${error.message || error}`;
|
|
466
|
+
let message = `${errorContext.requestType} API request failed`;
|
|
467
|
+
message += `\n${responseString}`;
|
|
468
|
+
for (const key of Object.keys(errorContext)) {
|
|
469
|
+
if (key === 'requestType') continue;
|
|
470
|
+
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
530
471
|
}
|
|
472
|
+
message += '\n';
|
|
473
|
+
super(message);
|
|
474
|
+
/** Source error from server */
|
|
475
|
+
this.error = void 0;
|
|
476
|
+
/** Context (API call & parameters) in which error occured */
|
|
477
|
+
this.errorContext = void 0;
|
|
478
|
+
/** Response from server */
|
|
479
|
+
this.response = void 0;
|
|
480
|
+
/** JSON Response from server */
|
|
481
|
+
this.responseJson = void 0;
|
|
482
|
+
this.name = 'CartoAPIError';
|
|
483
|
+
this.response = response;
|
|
484
|
+
this.responseJson = responseJson;
|
|
485
|
+
this.error = error;
|
|
486
|
+
this.errorContext = errorContext;
|
|
531
487
|
}
|
|
532
|
-
return makeCall({
|
|
533
|
-
url,
|
|
534
|
-
accessToken: source.accessToken,
|
|
535
|
-
opts: _extends({}, opts, {
|
|
536
|
-
method: isGet ? 'GET' : 'POST'
|
|
537
|
-
}, !isGet && {
|
|
538
|
-
body: JSON.stringify(queryParams)
|
|
539
|
-
})
|
|
540
|
-
});
|
|
541
488
|
}
|
|
542
|
-
|
|
543
|
-
const _excluded$1 = ["filterOwner", "spatialFilter", "abortController"],
|
|
544
|
-
_excluded2 = ["filterOwner", "spatialFilter", "abortController"],
|
|
545
|
-
_excluded3 = ["filterOwner", "spatialFilter", "abortController", "operationExp"],
|
|
546
|
-
_excluded4 = ["filterOwner", "spatialFilter", "abortController"],
|
|
547
|
-
_excluded5 = ["filterOwner", "spatialFilter", "abortController"],
|
|
548
|
-
_excluded6 = ["filterOwner", "spatialFilter", "abortController"],
|
|
549
|
-
_excluded7 = ["filterOwner", "spatialFilter", "abortController"],
|
|
550
|
-
_excluded8 = ["filterOwner", "abortController", "spatialFilter"];
|
|
551
489
|
/**
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
490
|
+
* Converts camelCase to Camel Case
|
|
555
491
|
*/
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
operationColumn
|
|
592
|
-
} = params;
|
|
593
|
-
return executeModel({
|
|
594
|
-
model: 'category',
|
|
595
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
596
|
-
spatialFilter
|
|
597
|
-
}),
|
|
598
|
-
params: {
|
|
599
|
-
column,
|
|
600
|
-
operation,
|
|
601
|
-
operationColumn: operationColumn || column
|
|
602
|
-
},
|
|
603
|
-
opts: {
|
|
604
|
-
abortController
|
|
605
|
-
}
|
|
606
|
-
}).then(res => normalizeObjectKeys(res.rows));
|
|
492
|
+
function formatErrorKey(key) {
|
|
493
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const DEFAULT_HEADERS = {
|
|
497
|
+
Accept: 'application/json',
|
|
498
|
+
'Content-Type': 'application/json'
|
|
499
|
+
};
|
|
500
|
+
const DEFAULT_REQUEST_CACHE = new Map();
|
|
501
|
+
async function requestWithParameters({
|
|
502
|
+
baseUrl,
|
|
503
|
+
parameters = {},
|
|
504
|
+
headers: customHeaders = {},
|
|
505
|
+
errorContext,
|
|
506
|
+
maxLengthURL = DEFAULT_MAX_LENGTH_URL,
|
|
507
|
+
localCache
|
|
508
|
+
}) {
|
|
509
|
+
// Parameters added to all requests issued with `requestWithParameters()`.
|
|
510
|
+
// These parameters override parameters already in the base URL, but not
|
|
511
|
+
// user-provided parameters.
|
|
512
|
+
parameters = _extends({
|
|
513
|
+
v: V3_MINOR_VERSION,
|
|
514
|
+
client: getClient()
|
|
515
|
+
}, typeof deck !== 'undefined' && deck.VERSION && {
|
|
516
|
+
deckglVersion: deck.VERSION
|
|
517
|
+
}, parameters);
|
|
518
|
+
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
519
|
+
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
520
|
+
const {
|
|
521
|
+
cache: REQUEST_CACHE,
|
|
522
|
+
canReadCache,
|
|
523
|
+
canStoreInCache
|
|
524
|
+
} = getCacheSettings(localCache);
|
|
525
|
+
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
526
|
+
return REQUEST_CACHE.get(key);
|
|
607
527
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
spatialFilter
|
|
638
|
-
}),
|
|
639
|
-
params: {
|
|
640
|
-
columns,
|
|
641
|
-
dataType,
|
|
642
|
-
featureIds,
|
|
643
|
-
z,
|
|
644
|
-
limit: limit || 1000,
|
|
645
|
-
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
646
|
-
},
|
|
647
|
-
opts: {
|
|
648
|
-
abortController
|
|
649
|
-
}
|
|
650
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
651
|
-
}).then(({
|
|
652
|
-
rows
|
|
653
|
-
}) => ({
|
|
654
|
-
rows
|
|
655
|
-
}));
|
|
528
|
+
const url = createURLWithParameters(baseUrl, parameters);
|
|
529
|
+
const headers = _extends({}, DEFAULT_HEADERS, customHeaders);
|
|
530
|
+
/* global fetch */
|
|
531
|
+
const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
|
|
532
|
+
method: 'POST',
|
|
533
|
+
body: JSON.stringify(parameters),
|
|
534
|
+
headers
|
|
535
|
+
}) : fetch(url, {
|
|
536
|
+
headers
|
|
537
|
+
});
|
|
538
|
+
let response;
|
|
539
|
+
let responseJson;
|
|
540
|
+
const jsonPromise = fetchPromise.then(_response => {
|
|
541
|
+
response = _response;
|
|
542
|
+
return response.json();
|
|
543
|
+
}).then(json => {
|
|
544
|
+
responseJson = json;
|
|
545
|
+
if (!response || !response.ok) {
|
|
546
|
+
throw new Error(json.error);
|
|
547
|
+
}
|
|
548
|
+
return json;
|
|
549
|
+
}).catch(error => {
|
|
550
|
+
if (canStoreInCache) {
|
|
551
|
+
REQUEST_CACHE.delete(key);
|
|
552
|
+
}
|
|
553
|
+
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
554
|
+
});
|
|
555
|
+
if (canStoreInCache) {
|
|
556
|
+
REQUEST_CACHE.set(key, jsonPromise);
|
|
656
557
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
558
|
+
return jsonPromise;
|
|
559
|
+
}
|
|
560
|
+
function getCacheSettings(localCache) {
|
|
561
|
+
var _localCache$cacheCont, _localCache$cacheCont2;
|
|
562
|
+
const canReadCache = localCache != null && (_localCache$cacheCont = localCache.cacheControl) != null && _localCache$cacheCont.includes('no-cache') ? false : true;
|
|
563
|
+
const canStoreInCache = localCache != null && (_localCache$cacheCont2 = localCache.cacheControl) != null && _localCache$cacheCont2.includes('no-store') ? false : true;
|
|
564
|
+
const cache = (localCache == null ? void 0 : localCache.cache) || DEFAULT_REQUEST_CACHE;
|
|
565
|
+
return {
|
|
566
|
+
cache,
|
|
567
|
+
canReadCache,
|
|
568
|
+
canStoreInCache
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function createCacheKey(baseUrl, parameters, headers) {
|
|
572
|
+
const parameterEntries = Object.entries(parameters).sort(([a], [b]) => a > b ? 1 : -1);
|
|
573
|
+
const headerEntries = Object.entries(headers).sort(([a], [b]) => a > b ? 1 : -1);
|
|
574
|
+
return JSON.stringify({
|
|
575
|
+
baseUrl,
|
|
576
|
+
parameters: parameterEntries,
|
|
577
|
+
headers: headerEntries
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
582
|
+
* unless there is a conflict, in which case the new parameters override
|
|
583
|
+
* those already in the URL.
|
|
584
|
+
*/
|
|
585
|
+
function createURLWithParameters(baseUrlString, parameters) {
|
|
586
|
+
const baseUrl = new URL(baseUrlString);
|
|
587
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
588
|
+
if (isPureObject(value) || Array.isArray(value)) {
|
|
589
|
+
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
590
|
+
} else {
|
|
591
|
+
baseUrl.searchParams.set(key, value.toString());
|
|
592
|
+
}
|
|
690
593
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
spatialFilter,
|
|
702
|
-
abortController
|
|
703
|
-
} = options,
|
|
704
|
-
params = _objectWithoutPropertiesLoose(options, _excluded4);
|
|
705
|
-
const {
|
|
706
|
-
column,
|
|
707
|
-
operation,
|
|
708
|
-
ticks
|
|
709
|
-
} = params;
|
|
710
|
-
const data = await executeModel({
|
|
711
|
-
model: 'histogram',
|
|
712
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
713
|
-
spatialFilter
|
|
714
|
-
}),
|
|
715
|
-
params: {
|
|
716
|
-
column,
|
|
717
|
-
operation,
|
|
718
|
-
ticks
|
|
719
|
-
},
|
|
720
|
-
opts: {
|
|
721
|
-
abortController
|
|
722
|
-
}
|
|
723
|
-
}).then(res => normalizeObjectKeys(res.rows));
|
|
724
|
-
if (data.length) {
|
|
725
|
-
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
726
|
-
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
727
|
-
const result = Array(ticks.length + 1).fill(0);
|
|
728
|
-
data.forEach(({
|
|
729
|
-
tick,
|
|
730
|
-
value
|
|
731
|
-
}) => result[tick] = value);
|
|
732
|
-
return result;
|
|
594
|
+
return baseUrl.toString();
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Deletes query string parameters from a URL.
|
|
598
|
+
*/
|
|
599
|
+
function excludeURLParameters(baseUrlString, parameters) {
|
|
600
|
+
const baseUrl = new URL(baseUrlString);
|
|
601
|
+
for (const param of parameters) {
|
|
602
|
+
if (baseUrl.searchParams.has(param)) {
|
|
603
|
+
baseUrl.searchParams.delete(param);
|
|
733
604
|
}
|
|
734
|
-
return [];
|
|
735
605
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
abortController
|
|
764
|
-
}
|
|
765
|
-
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
766
|
-
}
|
|
767
|
-
/****************************************************************************
|
|
768
|
-
* SCATTER
|
|
769
|
-
*/
|
|
770
|
-
/**
|
|
771
|
-
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
772
|
-
* values. Suitable for rendering scatter plots.
|
|
773
|
-
*/
|
|
774
|
-
async getScatter(options) {
|
|
775
|
-
const {
|
|
776
|
-
filterOwner,
|
|
777
|
-
spatialFilter,
|
|
778
|
-
abortController
|
|
779
|
-
} = options,
|
|
780
|
-
params = _objectWithoutPropertiesLoose(options, _excluded6);
|
|
781
|
-
const {
|
|
782
|
-
xAxisColumn,
|
|
783
|
-
xAxisJoinOperation,
|
|
784
|
-
yAxisColumn,
|
|
785
|
-
yAxisJoinOperation
|
|
786
|
-
} = params;
|
|
787
|
-
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
788
|
-
const HARD_LIMIT = 500;
|
|
789
|
-
return executeModel({
|
|
790
|
-
model: 'scatterplot',
|
|
791
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
792
|
-
spatialFilter
|
|
793
|
-
}),
|
|
794
|
-
params: {
|
|
795
|
-
xAxisColumn,
|
|
796
|
-
xAxisJoinOperation,
|
|
797
|
-
yAxisColumn,
|
|
798
|
-
yAxisJoinOperation,
|
|
799
|
-
limit: HARD_LIMIT
|
|
800
|
-
},
|
|
801
|
-
opts: {
|
|
802
|
-
abortController
|
|
803
|
-
}
|
|
804
|
-
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(({
|
|
805
|
-
x,
|
|
806
|
-
y
|
|
807
|
-
}) => [x, y]));
|
|
606
|
+
return baseUrl.toString();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const _excluded$1 = ["accessToken", "connectionName", "cache"];
|
|
610
|
+
const SOURCE_DEFAULTS = {
|
|
611
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
612
|
+
clientId: getClient(),
|
|
613
|
+
format: 'tilejson',
|
|
614
|
+
headers: {},
|
|
615
|
+
maxLengthURL: DEFAULT_MAX_LENGTH_URL
|
|
616
|
+
};
|
|
617
|
+
async function baseSource(endpoint, options, urlParameters) {
|
|
618
|
+
const {
|
|
619
|
+
accessToken,
|
|
620
|
+
connectionName,
|
|
621
|
+
cache
|
|
622
|
+
} = options,
|
|
623
|
+
optionalOptions = _objectWithoutPropertiesLoose(options, _excluded$1);
|
|
624
|
+
const mergedOptions = _extends({}, SOURCE_DEFAULTS, {
|
|
625
|
+
accessToken,
|
|
626
|
+
connectionName,
|
|
627
|
+
endpoint
|
|
628
|
+
});
|
|
629
|
+
for (const key in optionalOptions) {
|
|
630
|
+
if (optionalOptions[key]) {
|
|
631
|
+
mergedOptions[key] = optionalOptions[key];
|
|
632
|
+
}
|
|
808
633
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
limit,
|
|
840
|
-
offset
|
|
841
|
-
},
|
|
842
|
-
opts: {
|
|
843
|
-
abortController
|
|
844
|
-
}
|
|
845
|
-
}).then(res => {
|
|
846
|
-
var _res$rows, _res$metadata$total, _res$metadata, _res$METADATA;
|
|
847
|
-
return {
|
|
848
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
849
|
-
rows: (_res$rows = res.rows) != null ? _res$rows : res.ROWS,
|
|
850
|
-
totalCount: (_res$metadata$total = (_res$metadata = res.metadata) == null ? void 0 : _res$metadata.total) != null ? _res$metadata$total : (_res$METADATA = res.METADATA) == null ? void 0 : _res$METADATA.TOTAL
|
|
851
|
-
};
|
|
852
|
-
});
|
|
634
|
+
const baseUrl = buildSourceUrl(mergedOptions);
|
|
635
|
+
const {
|
|
636
|
+
clientId,
|
|
637
|
+
maxLengthURL,
|
|
638
|
+
format,
|
|
639
|
+
localCache
|
|
640
|
+
} = mergedOptions;
|
|
641
|
+
const headers = _extends({
|
|
642
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
643
|
+
}, options.headers);
|
|
644
|
+
const parameters = _extends({
|
|
645
|
+
client: clientId
|
|
646
|
+
}, urlParameters);
|
|
647
|
+
const errorContext = {
|
|
648
|
+
requestType: 'Map instantiation',
|
|
649
|
+
connection: options.connectionName,
|
|
650
|
+
type: endpoint,
|
|
651
|
+
source: JSON.stringify(parameters, undefined, 2)
|
|
652
|
+
};
|
|
653
|
+
const mapInstantiation = await requestWithParameters({
|
|
654
|
+
baseUrl,
|
|
655
|
+
parameters,
|
|
656
|
+
headers,
|
|
657
|
+
errorContext,
|
|
658
|
+
maxLengthURL,
|
|
659
|
+
localCache
|
|
660
|
+
});
|
|
661
|
+
const dataUrl = mapInstantiation[format].url[0];
|
|
662
|
+
if (cache) {
|
|
663
|
+
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
853
664
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const {
|
|
863
|
-
filterOwner,
|
|
864
|
-
abortController,
|
|
865
|
-
spatialFilter
|
|
866
|
-
} = options,
|
|
867
|
-
params = _objectWithoutPropertiesLoose(options, _excluded8);
|
|
868
|
-
const {
|
|
869
|
-
column,
|
|
870
|
-
operationColumn,
|
|
871
|
-
joinOperation,
|
|
872
|
-
operation,
|
|
873
|
-
stepSize,
|
|
874
|
-
stepMultiplier,
|
|
875
|
-
splitByCategory,
|
|
876
|
-
splitByCategoryLimit,
|
|
877
|
-
splitByCategoryValues
|
|
878
|
-
} = params;
|
|
879
|
-
return executeModel({
|
|
880
|
-
model: 'timeseries',
|
|
881
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
882
|
-
spatialFilter
|
|
883
|
-
}),
|
|
884
|
-
params: {
|
|
885
|
-
column,
|
|
886
|
-
stepSize,
|
|
887
|
-
stepMultiplier,
|
|
888
|
-
operationColumn: operationColumn || column,
|
|
889
|
-
joinOperation,
|
|
890
|
-
operation,
|
|
891
|
-
splitByCategory,
|
|
892
|
-
splitByCategoryLimit,
|
|
893
|
-
splitByCategoryValues
|
|
894
|
-
},
|
|
895
|
-
opts: {
|
|
896
|
-
abortController
|
|
897
|
-
}
|
|
898
|
-
}).then(res => {
|
|
899
|
-
var _res$metadata2;
|
|
900
|
-
return {
|
|
901
|
-
rows: normalizeObjectKeys(res.rows),
|
|
902
|
-
categories: (_res$metadata2 = res.metadata) == null ? void 0 : _res$metadata2.categories
|
|
903
|
-
};
|
|
665
|
+
errorContext.requestType = 'Map data';
|
|
666
|
+
if (format === 'tilejson') {
|
|
667
|
+
const json = await requestWithParameters({
|
|
668
|
+
baseUrl: dataUrl,
|
|
669
|
+
headers,
|
|
670
|
+
errorContext,
|
|
671
|
+
maxLengthURL,
|
|
672
|
+
localCache
|
|
904
673
|
});
|
|
674
|
+
if (accessToken) {
|
|
675
|
+
json.accessToken = accessToken;
|
|
676
|
+
}
|
|
677
|
+
return json;
|
|
905
678
|
}
|
|
679
|
+
return await requestWithParameters({
|
|
680
|
+
baseUrl: dataUrl,
|
|
681
|
+
headers,
|
|
682
|
+
errorContext,
|
|
683
|
+
maxLengthURL,
|
|
684
|
+
localCache
|
|
685
|
+
});
|
|
906
686
|
}
|
|
907
|
-
WidgetBaseSource.defaultProps = {
|
|
908
|
-
apiVersion: ApiVersion.V3,
|
|
909
|
-
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
910
|
-
clientId: getClient(),
|
|
911
|
-
filters: {},
|
|
912
|
-
filtersLogicalOperator: 'and',
|
|
913
|
-
geoColumn: DEFAULT_GEO_COLUMN
|
|
914
|
-
};
|
|
915
687
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
* connectionName: 'carto_dw',
|
|
932
|
-
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
933
|
-
* });
|
|
934
|
-
*
|
|
935
|
-
* const { widgetSource } = await data;
|
|
936
|
-
* ```
|
|
937
|
-
*/
|
|
938
|
-
class WidgetQuerySource extends WidgetBaseSource {
|
|
939
|
-
getModelSource(owner) {
|
|
940
|
-
return _extends({}, super._getModelSource(owner), {
|
|
941
|
-
type: 'query',
|
|
942
|
-
data: this.props.sqlQuery,
|
|
943
|
-
queryParameters: this.props.queryParameters
|
|
944
|
-
});
|
|
688
|
+
// deck.gl
|
|
689
|
+
const boundaryQuerySource = async function boundaryQuerySource(options) {
|
|
690
|
+
const {
|
|
691
|
+
columns,
|
|
692
|
+
filters,
|
|
693
|
+
tilesetTableName,
|
|
694
|
+
propertiesSqlQuery,
|
|
695
|
+
queryParameters
|
|
696
|
+
} = options;
|
|
697
|
+
const urlParameters = {
|
|
698
|
+
tilesetTableName,
|
|
699
|
+
propertiesSqlQuery
|
|
700
|
+
};
|
|
701
|
+
if (columns) {
|
|
702
|
+
urlParameters.columns = columns.join(',');
|
|
945
703
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
/**
|
|
949
|
-
* Source for Widget API requests on a data source defined as a table.
|
|
950
|
-
*
|
|
951
|
-
* Generally not intended to be constructed directly. Instead, call
|
|
952
|
-
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
953
|
-
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
954
|
-
* for use by widget implementations.
|
|
955
|
-
*
|
|
956
|
-
* Example:
|
|
957
|
-
*
|
|
958
|
-
* ```javascript
|
|
959
|
-
* import { vectorTableSource } from '@carto/api-client';
|
|
960
|
-
*
|
|
961
|
-
* const data = vectorTableSource({
|
|
962
|
-
* accessToken: '••••',
|
|
963
|
-
* connectionName: 'carto_dw',
|
|
964
|
-
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
965
|
-
* });
|
|
966
|
-
*
|
|
967
|
-
* const { widgetSource } = await data;
|
|
968
|
-
* ```
|
|
969
|
-
*/
|
|
970
|
-
class WidgetTableSource extends WidgetBaseSource {
|
|
971
|
-
getModelSource(owner) {
|
|
972
|
-
return _extends({}, super._getModelSource(owner), {
|
|
973
|
-
type: 'table',
|
|
974
|
-
data: this.props.tableName
|
|
975
|
-
});
|
|
704
|
+
if (filters) {
|
|
705
|
+
urlParameters.filters = filters;
|
|
976
706
|
}
|
|
977
|
-
|
|
707
|
+
if (queryParameters) {
|
|
708
|
+
urlParameters.queryParameters = queryParameters;
|
|
709
|
+
}
|
|
710
|
+
return baseSource('boundary', options, urlParameters);
|
|
711
|
+
};
|
|
978
712
|
|
|
979
713
|
// deck.gl
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (response.status === 400) {
|
|
994
|
-
responseString += 'Bad request';
|
|
995
|
-
} else if (response.status === 401 || response.status === 403) {
|
|
996
|
-
responseString += 'Unauthorized access';
|
|
997
|
-
} else if (response.status === 404) {
|
|
998
|
-
responseString += 'Not found';
|
|
999
|
-
} else {
|
|
1000
|
-
responseString += 'Error';
|
|
1001
|
-
}
|
|
1002
|
-
responseString += ` (${response.status}):`;
|
|
1003
|
-
}
|
|
1004
|
-
responseString += ` ${error.message || error}`;
|
|
1005
|
-
let message = `${errorContext.requestType} API request failed`;
|
|
1006
|
-
message += `\n${responseString}`;
|
|
1007
|
-
for (const key of Object.keys(errorContext)) {
|
|
1008
|
-
if (key === 'requestType') continue;
|
|
1009
|
-
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
1010
|
-
}
|
|
1011
|
-
message += '\n';
|
|
1012
|
-
super(message);
|
|
1013
|
-
/** Source error from server */
|
|
1014
|
-
this.error = void 0;
|
|
1015
|
-
/** Context (API call & parameters) in which error occured */
|
|
1016
|
-
this.errorContext = void 0;
|
|
1017
|
-
/** Response from server */
|
|
1018
|
-
this.response = void 0;
|
|
1019
|
-
/** JSON Response from server */
|
|
1020
|
-
this.responseJson = void 0;
|
|
1021
|
-
this.name = 'CartoAPIError';
|
|
1022
|
-
this.response = response;
|
|
1023
|
-
this.responseJson = responseJson;
|
|
1024
|
-
this.error = error;
|
|
1025
|
-
this.errorContext = errorContext;
|
|
714
|
+
const boundaryTableSource = async function boundaryTableSource(options) {
|
|
715
|
+
const {
|
|
716
|
+
filters,
|
|
717
|
+
tilesetTableName,
|
|
718
|
+
columns,
|
|
719
|
+
propertiesTableName
|
|
720
|
+
} = options;
|
|
721
|
+
const urlParameters = {
|
|
722
|
+
tilesetTableName,
|
|
723
|
+
propertiesTableName
|
|
724
|
+
};
|
|
725
|
+
if (columns) {
|
|
726
|
+
urlParameters.columns = columns.join(',');
|
|
1026
727
|
}
|
|
1027
|
-
|
|
728
|
+
if (filters) {
|
|
729
|
+
urlParameters.filters = filters;
|
|
730
|
+
}
|
|
731
|
+
return baseSource('boundary', options, urlParameters);
|
|
732
|
+
};
|
|
733
|
+
|
|
1028
734
|
/**
|
|
1029
|
-
*
|
|
735
|
+
* Return more descriptive error from API
|
|
736
|
+
* @internalRemarks Source: @carto/react-api
|
|
1030
737
|
*/
|
|
1031
|
-
function
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
// deck.gl
|
|
1036
|
-
// SPDX-License-Identifier: MIT
|
|
1037
|
-
// Copyright (c) vis.gl contributors
|
|
1038
|
-
function joinPath(...args) {
|
|
1039
|
-
return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
|
|
1040
|
-
}
|
|
1041
|
-
function buildV3Path(apiBaseUrl, version, endpoint, ...rest) {
|
|
1042
|
-
return joinPath(apiBaseUrl, version, endpoint, ...rest);
|
|
1043
|
-
}
|
|
1044
|
-
/** @internal Required by fetchMap(). */
|
|
1045
|
-
function buildPublicMapUrl({
|
|
1046
|
-
apiBaseUrl,
|
|
1047
|
-
cartoMapId
|
|
1048
|
-
}) {
|
|
1049
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
1050
|
-
}
|
|
1051
|
-
/** @internal Required by fetchMap(). */
|
|
1052
|
-
function buildStatsUrl({
|
|
1053
|
-
attribute,
|
|
1054
|
-
apiBaseUrl,
|
|
1055
|
-
connectionName,
|
|
1056
|
-
source,
|
|
1057
|
-
type
|
|
738
|
+
function dealWithApiError({
|
|
739
|
+
response,
|
|
740
|
+
data
|
|
1058
741
|
}) {
|
|
1059
|
-
|
|
1060
|
-
|
|
742
|
+
var _data$error, _data$error2;
|
|
743
|
+
if (data.error === 'Column not found') {
|
|
744
|
+
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
745
|
+
}
|
|
746
|
+
if (typeof data.error === 'string' && (_data$error = data.error) != null && _data$error.includes('Missing columns')) {
|
|
747
|
+
throw new InvalidColumnError(data.error);
|
|
748
|
+
}
|
|
749
|
+
switch (response.status) {
|
|
750
|
+
case 401:
|
|
751
|
+
throw new Error('Unauthorized access. Invalid credentials');
|
|
752
|
+
case 403:
|
|
753
|
+
throw new Error('Forbidden access to the requested data');
|
|
754
|
+
default:
|
|
755
|
+
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]));
|
|
756
|
+
throw new Error(msg);
|
|
1061
757
|
}
|
|
1062
|
-
// type === 'table'
|
|
1063
|
-
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
1064
|
-
}
|
|
1065
|
-
function buildSourceUrl({
|
|
1066
|
-
apiBaseUrl,
|
|
1067
|
-
connectionName,
|
|
1068
|
-
endpoint
|
|
1069
|
-
}) {
|
|
1070
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
1071
758
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
759
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
760
|
+
async function makeCall({
|
|
761
|
+
url,
|
|
762
|
+
accessToken,
|
|
763
|
+
opts
|
|
1075
764
|
}) {
|
|
1076
|
-
|
|
765
|
+
let response;
|
|
766
|
+
let data;
|
|
767
|
+
const isPost = (opts == null ? void 0 : opts.method) === 'POST';
|
|
768
|
+
try {
|
|
769
|
+
var _opts$abortController;
|
|
770
|
+
response = await fetch(url.toString(), _extends({
|
|
771
|
+
headers: _extends({
|
|
772
|
+
Authorization: `Bearer ${accessToken}`
|
|
773
|
+
}, isPost && {
|
|
774
|
+
'Content-Type': 'application/json'
|
|
775
|
+
})
|
|
776
|
+
}, isPost && {
|
|
777
|
+
method: opts == null ? void 0 : opts.method,
|
|
778
|
+
body: opts == null ? void 0 : opts.body
|
|
779
|
+
}, {
|
|
780
|
+
signal: opts == null || (_opts$abortController = opts.abortController) == null ? void 0 : _opts$abortController.signal
|
|
781
|
+
}, opts == null ? void 0 : opts.otherOptions));
|
|
782
|
+
data = await response.json();
|
|
783
|
+
} catch (error) {
|
|
784
|
+
if (error.name === 'AbortError') throw error;
|
|
785
|
+
throw new Error(`Failed request: ${error}`);
|
|
786
|
+
}
|
|
787
|
+
if (!response.ok) {
|
|
788
|
+
dealWithApiError({
|
|
789
|
+
response,
|
|
790
|
+
data
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return data;
|
|
1077
794
|
}
|
|
1078
795
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
// user-provided parameters.
|
|
1095
|
-
parameters = _extends({
|
|
1096
|
-
v: V3_MINOR_VERSION,
|
|
1097
|
-
client: getClient()
|
|
1098
|
-
}, typeof deck !== 'undefined' && deck.VERSION && {
|
|
1099
|
-
deckglVersion: deck.VERSION
|
|
1100
|
-
}, parameters);
|
|
1101
|
-
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
1102
|
-
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
796
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
797
|
+
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
798
|
+
const {
|
|
799
|
+
V3
|
|
800
|
+
} = ApiVersion;
|
|
801
|
+
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
802
|
+
/**
|
|
803
|
+
* Execute a SQL model request.
|
|
804
|
+
* @internalRemarks Source: @carto/react-api
|
|
805
|
+
*/
|
|
806
|
+
function executeModel(props) {
|
|
807
|
+
assert(props.source, 'executeModel: missing source');
|
|
808
|
+
assert(props.model, 'executeModel: missing model');
|
|
809
|
+
assert(props.params, 'executeModel: missing params');
|
|
810
|
+
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
1103
811
|
const {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
812
|
+
model,
|
|
813
|
+
source,
|
|
814
|
+
params,
|
|
815
|
+
opts
|
|
816
|
+
} = props;
|
|
817
|
+
const {
|
|
818
|
+
type,
|
|
819
|
+
apiVersion,
|
|
820
|
+
apiBaseUrl,
|
|
821
|
+
accessToken,
|
|
822
|
+
connectionName,
|
|
823
|
+
clientId
|
|
824
|
+
} = source;
|
|
825
|
+
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
826
|
+
assert(accessToken, 'executeModel: missing accessToken');
|
|
827
|
+
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
828
|
+
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
829
|
+
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
830
|
+
const {
|
|
831
|
+
data,
|
|
832
|
+
filters,
|
|
833
|
+
filtersLogicalOperator = 'and',
|
|
834
|
+
geoColumn = DEFAULT_GEO_COLUMN
|
|
835
|
+
} = source;
|
|
836
|
+
const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
|
|
837
|
+
const queryParams = {
|
|
838
|
+
type,
|
|
839
|
+
client: clientId,
|
|
840
|
+
source: data,
|
|
841
|
+
params: JSON.stringify(params),
|
|
842
|
+
queryParameters,
|
|
843
|
+
filters: JSON.stringify(filters),
|
|
844
|
+
filtersLogicalOperator
|
|
845
|
+
};
|
|
846
|
+
// Picking Model API requires 'spatialDataColumn'.
|
|
847
|
+
if (model === 'pick') {
|
|
848
|
+
queryParams.spatialDataColumn = geoColumn;
|
|
1110
849
|
}
|
|
1111
|
-
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
throw new Error(json.error);
|
|
1130
|
-
}
|
|
1131
|
-
return json;
|
|
1132
|
-
}).catch(error => {
|
|
1133
|
-
if (canStoreInCache) {
|
|
1134
|
-
REQUEST_CACHE.delete(key);
|
|
850
|
+
// API supports multiple filters, we apply it only to geoColumn
|
|
851
|
+
const spatialFilters = source.spatialFilter ? {
|
|
852
|
+
[geoColumn]: source.spatialFilter
|
|
853
|
+
} : undefined;
|
|
854
|
+
if (spatialFilters) {
|
|
855
|
+
queryParams.spatialFilters = JSON.stringify(spatialFilters);
|
|
856
|
+
}
|
|
857
|
+
const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
|
|
858
|
+
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
859
|
+
if (isGet) {
|
|
860
|
+
url = urlWithSearchParams;
|
|
861
|
+
} else {
|
|
862
|
+
// undo the JSON.stringify, @TODO find a better pattern
|
|
863
|
+
queryParams.params = params;
|
|
864
|
+
queryParams.filters = filters;
|
|
865
|
+
queryParams.queryParameters = source.queryParameters;
|
|
866
|
+
if (spatialFilters) {
|
|
867
|
+
queryParams.spatialFilters = spatialFilters;
|
|
1135
868
|
}
|
|
1136
|
-
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
1137
|
-
});
|
|
1138
|
-
if (canStoreInCache) {
|
|
1139
|
-
REQUEST_CACHE.set(key, jsonPromise);
|
|
1140
869
|
}
|
|
1141
|
-
return
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
cache,
|
|
1150
|
-
canReadCache,
|
|
1151
|
-
canStoreInCache
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
|
-
function createCacheKey(baseUrl, parameters, headers) {
|
|
1155
|
-
const parameterEntries = Object.entries(parameters).sort(([a], [b]) => a > b ? 1 : -1);
|
|
1156
|
-
const headerEntries = Object.entries(headers).sort(([a], [b]) => a > b ? 1 : -1);
|
|
1157
|
-
return JSON.stringify({
|
|
1158
|
-
baseUrl,
|
|
1159
|
-
parameters: parameterEntries,
|
|
1160
|
-
headers: headerEntries
|
|
870
|
+
return makeCall({
|
|
871
|
+
url,
|
|
872
|
+
accessToken: source.accessToken,
|
|
873
|
+
opts: _extends({}, opts, {
|
|
874
|
+
method: isGet ? 'GET' : 'POST'
|
|
875
|
+
}, !isGet && {
|
|
876
|
+
body: JSON.stringify(queryParams)
|
|
877
|
+
})
|
|
1161
878
|
});
|
|
1162
879
|
}
|
|
880
|
+
|
|
881
|
+
const _excluded = ["filterOwner", "spatialFilter", "abortController"],
|
|
882
|
+
_excluded2 = ["filterOwner", "spatialFilter", "abortController"],
|
|
883
|
+
_excluded3 = ["filterOwner", "spatialFilter", "abortController", "operationExp"],
|
|
884
|
+
_excluded4 = ["filterOwner", "spatialFilter", "abortController"],
|
|
885
|
+
_excluded5 = ["filterOwner", "spatialFilter", "abortController"],
|
|
886
|
+
_excluded6 = ["filterOwner", "spatialFilter", "abortController"],
|
|
887
|
+
_excluded7 = ["filterOwner", "spatialFilter", "abortController"],
|
|
888
|
+
_excluded8 = ["filterOwner", "abortController", "spatialFilter"];
|
|
1163
889
|
/**
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
890
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
891
|
+
*
|
|
892
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
1167
893
|
*/
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
894
|
+
class WidgetBaseSource {
|
|
895
|
+
constructor(props) {
|
|
896
|
+
this.props = void 0;
|
|
897
|
+
this.props = _extends({}, WidgetBaseSource.defaultProps, props);
|
|
898
|
+
}
|
|
899
|
+
_getModelSource(owner) {
|
|
900
|
+
const props = this.props;
|
|
901
|
+
return {
|
|
902
|
+
apiVersion: props.apiVersion,
|
|
903
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
904
|
+
clientId: props.clientId,
|
|
905
|
+
accessToken: props.accessToken,
|
|
906
|
+
connectionName: props.connectionName,
|
|
907
|
+
filters: getApplicableFilters(owner, props.filters),
|
|
908
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
909
|
+
geoColumn: props.geoColumn
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
/****************************************************************************
|
|
913
|
+
* CATEGORIES
|
|
914
|
+
*/
|
|
915
|
+
/**
|
|
916
|
+
* Returns a list of labeled datapoints for categorical data. Suitable for
|
|
917
|
+
* charts including grouped bar charts, pie charts, and tree charts.
|
|
918
|
+
*/
|
|
919
|
+
async getCategories(options) {
|
|
920
|
+
const {
|
|
921
|
+
filterOwner,
|
|
922
|
+
spatialFilter,
|
|
923
|
+
abortController
|
|
924
|
+
} = options,
|
|
925
|
+
params = _objectWithoutPropertiesLoose(options, _excluded);
|
|
926
|
+
const {
|
|
927
|
+
column,
|
|
928
|
+
operation,
|
|
929
|
+
operationColumn
|
|
930
|
+
} = params;
|
|
931
|
+
return executeModel({
|
|
932
|
+
model: 'category',
|
|
933
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
934
|
+
spatialFilter
|
|
935
|
+
}),
|
|
936
|
+
params: {
|
|
937
|
+
column,
|
|
938
|
+
operation,
|
|
939
|
+
operationColumn: operationColumn || column
|
|
940
|
+
},
|
|
941
|
+
opts: {
|
|
942
|
+
abortController
|
|
943
|
+
}
|
|
944
|
+
}).then(res => normalizeObjectKeys(res.rows));
|
|
945
|
+
}
|
|
946
|
+
/****************************************************************************
|
|
947
|
+
* FEATURES
|
|
948
|
+
*/
|
|
949
|
+
/**
|
|
950
|
+
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
951
|
+
* matching features. In datasets containing features with duplicate geometries,
|
|
952
|
+
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
953
|
+
* results may be returned than IDs in the request.
|
|
954
|
+
* @internal
|
|
955
|
+
* @experimental
|
|
956
|
+
*/
|
|
957
|
+
async getFeatures(options) {
|
|
958
|
+
const {
|
|
959
|
+
filterOwner,
|
|
960
|
+
spatialFilter,
|
|
961
|
+
abortController
|
|
962
|
+
} = options,
|
|
963
|
+
params = _objectWithoutPropertiesLoose(options, _excluded2);
|
|
964
|
+
const {
|
|
965
|
+
columns,
|
|
966
|
+
dataType,
|
|
967
|
+
featureIds,
|
|
968
|
+
z,
|
|
969
|
+
limit,
|
|
970
|
+
tileResolution
|
|
971
|
+
} = params;
|
|
972
|
+
return executeModel({
|
|
973
|
+
model: 'pick',
|
|
974
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
975
|
+
spatialFilter
|
|
976
|
+
}),
|
|
977
|
+
params: {
|
|
978
|
+
columns,
|
|
979
|
+
dataType,
|
|
980
|
+
featureIds,
|
|
981
|
+
z,
|
|
982
|
+
limit: limit || 1000,
|
|
983
|
+
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
984
|
+
},
|
|
985
|
+
opts: {
|
|
986
|
+
abortController
|
|
987
|
+
}
|
|
988
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
989
|
+
}).then(({
|
|
990
|
+
rows
|
|
991
|
+
}) => ({
|
|
992
|
+
rows
|
|
993
|
+
}));
|
|
994
|
+
}
|
|
995
|
+
/****************************************************************************
|
|
996
|
+
* FORMULA
|
|
997
|
+
*/
|
|
998
|
+
/**
|
|
999
|
+
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1000
|
+
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1001
|
+
*/
|
|
1002
|
+
async getFormula(options) {
|
|
1003
|
+
const {
|
|
1004
|
+
filterOwner,
|
|
1005
|
+
spatialFilter,
|
|
1006
|
+
abortController,
|
|
1007
|
+
operationExp
|
|
1008
|
+
} = options,
|
|
1009
|
+
params = _objectWithoutPropertiesLoose(options, _excluded3);
|
|
1010
|
+
const {
|
|
1011
|
+
column,
|
|
1012
|
+
operation
|
|
1013
|
+
} = params;
|
|
1014
|
+
return executeModel({
|
|
1015
|
+
model: 'formula',
|
|
1016
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1017
|
+
spatialFilter
|
|
1018
|
+
}),
|
|
1019
|
+
params: {
|
|
1020
|
+
column: column != null ? column : '*',
|
|
1021
|
+
operation,
|
|
1022
|
+
operationExp
|
|
1023
|
+
},
|
|
1024
|
+
opts: {
|
|
1025
|
+
abortController
|
|
1026
|
+
}
|
|
1027
|
+
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1028
|
+
}
|
|
1029
|
+
/****************************************************************************
|
|
1030
|
+
* HISTOGRAM
|
|
1031
|
+
*/
|
|
1032
|
+
/**
|
|
1033
|
+
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1034
|
+
* over a numerical range. Suitable for histogram charts.
|
|
1035
|
+
*/
|
|
1036
|
+
async getHistogram(options) {
|
|
1037
|
+
const {
|
|
1038
|
+
filterOwner,
|
|
1039
|
+
spatialFilter,
|
|
1040
|
+
abortController
|
|
1041
|
+
} = options,
|
|
1042
|
+
params = _objectWithoutPropertiesLoose(options, _excluded4);
|
|
1043
|
+
const {
|
|
1044
|
+
column,
|
|
1045
|
+
operation,
|
|
1046
|
+
ticks
|
|
1047
|
+
} = params;
|
|
1048
|
+
const data = await executeModel({
|
|
1049
|
+
model: 'histogram',
|
|
1050
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1051
|
+
spatialFilter
|
|
1052
|
+
}),
|
|
1053
|
+
params: {
|
|
1054
|
+
column,
|
|
1055
|
+
operation,
|
|
1056
|
+
ticks
|
|
1057
|
+
},
|
|
1058
|
+
opts: {
|
|
1059
|
+
abortController
|
|
1060
|
+
}
|
|
1061
|
+
}).then(res => normalizeObjectKeys(res.rows));
|
|
1062
|
+
if (data.length) {
|
|
1063
|
+
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
1064
|
+
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
1065
|
+
const result = Array(ticks.length + 1).fill(0);
|
|
1066
|
+
data.forEach(({
|
|
1067
|
+
tick,
|
|
1068
|
+
value
|
|
1069
|
+
}) => result[tick] = value);
|
|
1070
|
+
return result;
|
|
1175
1071
|
}
|
|
1072
|
+
return [];
|
|
1073
|
+
}
|
|
1074
|
+
/****************************************************************************
|
|
1075
|
+
* RANGE
|
|
1076
|
+
*/
|
|
1077
|
+
/**
|
|
1078
|
+
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1079
|
+
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1080
|
+
* or rendering a range slider UI for filtering.
|
|
1081
|
+
*/
|
|
1082
|
+
async getRange(options) {
|
|
1083
|
+
const {
|
|
1084
|
+
filterOwner,
|
|
1085
|
+
spatialFilter,
|
|
1086
|
+
abortController
|
|
1087
|
+
} = options,
|
|
1088
|
+
params = _objectWithoutPropertiesLoose(options, _excluded5);
|
|
1089
|
+
const {
|
|
1090
|
+
column
|
|
1091
|
+
} = params;
|
|
1092
|
+
return executeModel({
|
|
1093
|
+
model: 'range',
|
|
1094
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1095
|
+
spatialFilter
|
|
1096
|
+
}),
|
|
1097
|
+
params: {
|
|
1098
|
+
column
|
|
1099
|
+
},
|
|
1100
|
+
opts: {
|
|
1101
|
+
abortController
|
|
1102
|
+
}
|
|
1103
|
+
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1104
|
+
}
|
|
1105
|
+
/****************************************************************************
|
|
1106
|
+
* SCATTER
|
|
1107
|
+
*/
|
|
1108
|
+
/**
|
|
1109
|
+
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1110
|
+
* values. Suitable for rendering scatter plots.
|
|
1111
|
+
*/
|
|
1112
|
+
async getScatter(options) {
|
|
1113
|
+
const {
|
|
1114
|
+
filterOwner,
|
|
1115
|
+
spatialFilter,
|
|
1116
|
+
abortController
|
|
1117
|
+
} = options,
|
|
1118
|
+
params = _objectWithoutPropertiesLoose(options, _excluded6);
|
|
1119
|
+
const {
|
|
1120
|
+
xAxisColumn,
|
|
1121
|
+
xAxisJoinOperation,
|
|
1122
|
+
yAxisColumn,
|
|
1123
|
+
yAxisJoinOperation
|
|
1124
|
+
} = params;
|
|
1125
|
+
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1126
|
+
const HARD_LIMIT = 500;
|
|
1127
|
+
return executeModel({
|
|
1128
|
+
model: 'scatterplot',
|
|
1129
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1130
|
+
spatialFilter
|
|
1131
|
+
}),
|
|
1132
|
+
params: {
|
|
1133
|
+
xAxisColumn,
|
|
1134
|
+
xAxisJoinOperation,
|
|
1135
|
+
yAxisColumn,
|
|
1136
|
+
yAxisJoinOperation,
|
|
1137
|
+
limit: HARD_LIMIT
|
|
1138
|
+
},
|
|
1139
|
+
opts: {
|
|
1140
|
+
abortController
|
|
1141
|
+
}
|
|
1142
|
+
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(({
|
|
1143
|
+
x,
|
|
1144
|
+
y
|
|
1145
|
+
}) => [x, y]));
|
|
1146
|
+
}
|
|
1147
|
+
/****************************************************************************
|
|
1148
|
+
* TABLE
|
|
1149
|
+
*/
|
|
1150
|
+
/**
|
|
1151
|
+
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1152
|
+
* sorting. Suitable for displaying tables and lists.
|
|
1153
|
+
*/
|
|
1154
|
+
async getTable(options) {
|
|
1155
|
+
const {
|
|
1156
|
+
filterOwner,
|
|
1157
|
+
spatialFilter,
|
|
1158
|
+
abortController
|
|
1159
|
+
} = options,
|
|
1160
|
+
params = _objectWithoutPropertiesLoose(options, _excluded7);
|
|
1161
|
+
const {
|
|
1162
|
+
columns,
|
|
1163
|
+
sortBy,
|
|
1164
|
+
sortDirection,
|
|
1165
|
+
offset = 0,
|
|
1166
|
+
limit = 10
|
|
1167
|
+
} = params;
|
|
1168
|
+
return executeModel({
|
|
1169
|
+
model: 'table',
|
|
1170
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1171
|
+
spatialFilter
|
|
1172
|
+
}),
|
|
1173
|
+
params: {
|
|
1174
|
+
column: columns,
|
|
1175
|
+
sortBy,
|
|
1176
|
+
sortDirection,
|
|
1177
|
+
limit,
|
|
1178
|
+
offset
|
|
1179
|
+
},
|
|
1180
|
+
opts: {
|
|
1181
|
+
abortController
|
|
1182
|
+
}
|
|
1183
|
+
}).then(res => {
|
|
1184
|
+
var _res$rows, _res$metadata$total, _res$metadata, _res$METADATA;
|
|
1185
|
+
return {
|
|
1186
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1187
|
+
rows: (_res$rows = res.rows) != null ? _res$rows : res.ROWS,
|
|
1188
|
+
totalCount: (_res$metadata$total = (_res$metadata = res.metadata) == null ? void 0 : _res$metadata.total) != null ? _res$metadata$total : (_res$METADATA = res.METADATA) == null ? void 0 : _res$METADATA.TOTAL
|
|
1189
|
+
};
|
|
1190
|
+
});
|
|
1176
1191
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1192
|
+
/****************************************************************************
|
|
1193
|
+
* TIME SERIES
|
|
1194
|
+
*/
|
|
1195
|
+
/**
|
|
1196
|
+
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1197
|
+
* time intervals. Suitable for rendering time series charts.
|
|
1198
|
+
*/
|
|
1199
|
+
async getTimeSeries(options) {
|
|
1200
|
+
const {
|
|
1201
|
+
filterOwner,
|
|
1202
|
+
abortController,
|
|
1203
|
+
spatialFilter
|
|
1204
|
+
} = options,
|
|
1205
|
+
params = _objectWithoutPropertiesLoose(options, _excluded8);
|
|
1206
|
+
const {
|
|
1207
|
+
column,
|
|
1208
|
+
operationColumn,
|
|
1209
|
+
joinOperation,
|
|
1210
|
+
operation,
|
|
1211
|
+
stepSize,
|
|
1212
|
+
stepMultiplier,
|
|
1213
|
+
splitByCategory,
|
|
1214
|
+
splitByCategoryLimit,
|
|
1215
|
+
splitByCategoryValues
|
|
1216
|
+
} = params;
|
|
1217
|
+
return executeModel({
|
|
1218
|
+
model: 'timeseries',
|
|
1219
|
+
source: _extends({}, this.getModelSource(filterOwner), {
|
|
1220
|
+
spatialFilter
|
|
1221
|
+
}),
|
|
1222
|
+
params: {
|
|
1223
|
+
column,
|
|
1224
|
+
stepSize,
|
|
1225
|
+
stepMultiplier,
|
|
1226
|
+
operationColumn: operationColumn || column,
|
|
1227
|
+
joinOperation,
|
|
1228
|
+
operation,
|
|
1229
|
+
splitByCategory,
|
|
1230
|
+
splitByCategoryLimit,
|
|
1231
|
+
splitByCategoryValues
|
|
1232
|
+
},
|
|
1233
|
+
opts: {
|
|
1234
|
+
abortController
|
|
1235
|
+
}
|
|
1236
|
+
}).then(res => {
|
|
1237
|
+
var _res$metadata2;
|
|
1238
|
+
return {
|
|
1239
|
+
rows: normalizeObjectKeys(res.rows),
|
|
1240
|
+
categories: (_res$metadata2 = res.metadata) == null ? void 0 : _res$metadata2.categories
|
|
1241
|
+
};
|
|
1242
|
+
});
|
|
1188
1243
|
}
|
|
1189
|
-
return baseUrl.toString();
|
|
1190
1244
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const SOURCE_DEFAULTS = {
|
|
1245
|
+
WidgetBaseSource.defaultProps = {
|
|
1246
|
+
apiVersion: ApiVersion.V3,
|
|
1194
1247
|
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1195
1248
|
clientId: getClient(),
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1249
|
+
filters: {},
|
|
1250
|
+
filtersLogicalOperator: 'and',
|
|
1251
|
+
geoColumn: DEFAULT_GEO_COLUMN
|
|
1199
1252
|
};
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}, urlParameters);
|
|
1230
|
-
const errorContext = {
|
|
1231
|
-
requestType: 'Map instantiation',
|
|
1232
|
-
connection: options.connectionName,
|
|
1233
|
-
type: endpoint,
|
|
1234
|
-
source: JSON.stringify(parameters, undefined, 2)
|
|
1235
|
-
};
|
|
1236
|
-
const mapInstantiation = await requestWithParameters({
|
|
1237
|
-
baseUrl,
|
|
1238
|
-
parameters,
|
|
1239
|
-
headers,
|
|
1240
|
-
errorContext,
|
|
1241
|
-
maxLengthURL,
|
|
1242
|
-
localCache
|
|
1243
|
-
});
|
|
1244
|
-
const dataUrl = mapInstantiation[format].url[0];
|
|
1245
|
-
if (cache) {
|
|
1246
|
-
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
1247
|
-
}
|
|
1248
|
-
errorContext.requestType = 'Map data';
|
|
1249
|
-
if (format === 'tilejson') {
|
|
1250
|
-
const json = await requestWithParameters({
|
|
1251
|
-
baseUrl: dataUrl,
|
|
1252
|
-
headers,
|
|
1253
|
-
errorContext,
|
|
1254
|
-
maxLengthURL,
|
|
1255
|
-
localCache
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
1256
|
+
*
|
|
1257
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1258
|
+
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
1259
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1260
|
+
* for use by widget implementations.
|
|
1261
|
+
*
|
|
1262
|
+
* Example:
|
|
1263
|
+
*
|
|
1264
|
+
* ```javascript
|
|
1265
|
+
* import { vectorQuerySource } from '@carto/api-client';
|
|
1266
|
+
*
|
|
1267
|
+
* const data = vectorQuerySource({
|
|
1268
|
+
* accessToken: '••••',
|
|
1269
|
+
* connectionName: 'carto_dw',
|
|
1270
|
+
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
1271
|
+
* });
|
|
1272
|
+
*
|
|
1273
|
+
* const { widgetSource } = await data;
|
|
1274
|
+
* ```
|
|
1275
|
+
*/
|
|
1276
|
+
class WidgetQuerySource extends WidgetBaseSource {
|
|
1277
|
+
getModelSource(owner) {
|
|
1278
|
+
return _extends({}, super._getModelSource(owner), {
|
|
1279
|
+
type: 'query',
|
|
1280
|
+
data: this.props.sqlQuery,
|
|
1281
|
+
queryParameters: this.props.queryParameters
|
|
1256
1282
|
});
|
|
1257
|
-
if (accessToken) {
|
|
1258
|
-
json.accessToken = accessToken;
|
|
1259
|
-
}
|
|
1260
|
-
return json;
|
|
1261
1283
|
}
|
|
1262
|
-
return await requestWithParameters({
|
|
1263
|
-
baseUrl: dataUrl,
|
|
1264
|
-
headers,
|
|
1265
|
-
errorContext,
|
|
1266
|
-
maxLengthURL,
|
|
1267
|
-
localCache
|
|
1268
|
-
});
|
|
1269
1284
|
}
|
|
1270
1285
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
filters,
|
|
1300
|
-
tilesetTableName,
|
|
1301
|
-
columns,
|
|
1302
|
-
propertiesTableName
|
|
1303
|
-
} = options;
|
|
1304
|
-
const urlParameters = {
|
|
1305
|
-
tilesetTableName,
|
|
1306
|
-
propertiesTableName
|
|
1307
|
-
};
|
|
1308
|
-
if (columns) {
|
|
1309
|
-
urlParameters.columns = columns.join(',');
|
|
1310
|
-
}
|
|
1311
|
-
if (filters) {
|
|
1312
|
-
urlParameters.filters = filters;
|
|
1286
|
+
/**
|
|
1287
|
+
* Source for Widget API requests on a data source defined as a table.
|
|
1288
|
+
*
|
|
1289
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1290
|
+
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
1291
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1292
|
+
* for use by widget implementations.
|
|
1293
|
+
*
|
|
1294
|
+
* Example:
|
|
1295
|
+
*
|
|
1296
|
+
* ```javascript
|
|
1297
|
+
* import { vectorTableSource } from '@carto/api-client';
|
|
1298
|
+
*
|
|
1299
|
+
* const data = vectorTableSource({
|
|
1300
|
+
* accessToken: '••••',
|
|
1301
|
+
* connectionName: 'carto_dw',
|
|
1302
|
+
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
1303
|
+
* });
|
|
1304
|
+
*
|
|
1305
|
+
* const { widgetSource } = await data;
|
|
1306
|
+
* ```
|
|
1307
|
+
*/
|
|
1308
|
+
class WidgetTableSource extends WidgetBaseSource {
|
|
1309
|
+
getModelSource(owner) {
|
|
1310
|
+
return _extends({}, super._getModelSource(owner), {
|
|
1311
|
+
type: 'table',
|
|
1312
|
+
data: this.props.tableName
|
|
1313
|
+
});
|
|
1313
1314
|
}
|
|
1314
|
-
|
|
1315
|
-
};
|
|
1315
|
+
}
|
|
1316
1316
|
|
|
1317
1317
|
const h3QuerySource = async function h3QuerySource(options) {
|
|
1318
1318
|
const {
|
|
@@ -1466,7 +1466,8 @@ const vectorQuerySource = async function vectorQuerySource(options) {
|
|
|
1466
1466
|
spatialDataColumn = 'geom',
|
|
1467
1467
|
sqlQuery,
|
|
1468
1468
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1469
|
-
queryParameters
|
|
1469
|
+
queryParameters,
|
|
1470
|
+
aggregationExp
|
|
1470
1471
|
} = options;
|
|
1471
1472
|
const urlParameters = {
|
|
1472
1473
|
spatialDataColumn,
|
|
@@ -1483,6 +1484,9 @@ const vectorQuerySource = async function vectorQuerySource(options) {
|
|
|
1483
1484
|
if (queryParameters) {
|
|
1484
1485
|
urlParameters.queryParameters = queryParameters;
|
|
1485
1486
|
}
|
|
1487
|
+
if (aggregationExp) {
|
|
1488
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1489
|
+
}
|
|
1486
1490
|
return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
|
|
1487
1491
|
widgetSource: new WidgetQuerySource(options)
|
|
1488
1492
|
}));
|
|
@@ -1494,7 +1498,8 @@ const vectorTableSource = async function vectorTableSource(options) {
|
|
|
1494
1498
|
filters,
|
|
1495
1499
|
spatialDataColumn = 'geom',
|
|
1496
1500
|
tableName,
|
|
1497
|
-
tileResolution = DEFAULT_TILE_RESOLUTION
|
|
1501
|
+
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1502
|
+
aggregationExp
|
|
1498
1503
|
} = options;
|
|
1499
1504
|
const urlParameters = {
|
|
1500
1505
|
name: tableName,
|
|
@@ -1508,6 +1513,9 @@ const vectorTableSource = async function vectorTableSource(options) {
|
|
|
1508
1513
|
if (filters) {
|
|
1509
1514
|
urlParameters.filters = filters;
|
|
1510
1515
|
}
|
|
1516
|
+
if (aggregationExp) {
|
|
1517
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1518
|
+
}
|
|
1511
1519
|
return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
|
|
1512
1520
|
widgetSource: new WidgetTableSource(options)
|
|
1513
1521
|
}));
|