@carto/api-client 0.0.1-1 → 0.0.30
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/README.md +113 -0
- package/build/api-client.cjs +344 -206
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +337 -203
- package/build/api-client.modern.js.map +1 -1
- package/build/client.d.ts +12 -2
- package/build/constants-internal.d.ts +23 -5
- package/build/constants.d.ts +19 -15
- package/build/index.d.ts +0 -1
- package/build/models/common.d.ts +2 -5
- package/build/models/model.d.ts +18 -4
- package/build/sources/types.d.ts +30 -14
- package/build/sources/widget-base-source.d.ts +77 -22
- package/build/sources/widget-query-source.d.ts +25 -3
- package/build/sources/widget-table-source.d.ts +25 -3
- package/build/sources/wrappers.d.ts +21 -14
- package/build/types.d.ts +14 -28
- package/package.json +3 -5
- package/src/client.ts +14 -6
- package/src/constants-internal.ts +26 -5
- package/src/constants.ts +19 -18
- package/src/index.ts +0 -1
- package/src/models/common.ts +8 -18
- package/src/models/model.ts +44 -27
- package/src/sources/types.ts +32 -16
- package/src/sources/widget-base-source.ts +173 -113
- package/src/sources/widget-query-source.ts +30 -7
- package/src/sources/widget-table-source.ts +29 -7
- package/src/sources/wrappers.ts +38 -21
- package/src/types.ts +24 -32
package/src/models/model.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import {getClient} from '../client';
|
|
2
|
-
import {ApiVersion, MapType} from '../constants';
|
|
1
|
+
import {getClient} from '../client.js';
|
|
3
2
|
import {
|
|
3
|
+
ApiVersion,
|
|
4
4
|
DEFAULT_GEO_COLUMN,
|
|
5
|
-
|
|
6
|
-
} from '../constants-internal';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
MapType,
|
|
6
|
+
} from '../constants-internal.js';
|
|
7
|
+
import {
|
|
8
|
+
Filter,
|
|
9
|
+
FilterLogicalOperator,
|
|
10
|
+
QueryParameters,
|
|
11
|
+
SpatialFilter,
|
|
12
|
+
} from '../types.js';
|
|
13
|
+
import {$TODO} from '../types-internal.js';
|
|
14
|
+
import {assert} from '../utils.js';
|
|
15
|
+
import {ModelRequestOptions, makeCall} from './common.js';
|
|
11
16
|
|
|
12
17
|
/** @internalRemarks Source: @carto/react-api */
|
|
13
18
|
const AVAILABLE_MODELS = [
|
|
@@ -22,15 +27,32 @@ const AVAILABLE_MODELS = [
|
|
|
22
27
|
|
|
23
28
|
export type Model = (typeof AVAILABLE_MODELS)[number];
|
|
24
29
|
|
|
30
|
+
export interface ModelSource {
|
|
31
|
+
type: MapType;
|
|
32
|
+
apiVersion: ApiVersion;
|
|
33
|
+
apiBaseUrl: string;
|
|
34
|
+
accessToken: string;
|
|
35
|
+
clientId: string;
|
|
36
|
+
connectionName: string;
|
|
37
|
+
data: string;
|
|
38
|
+
filters?: Record<string, Filter>;
|
|
39
|
+
filtersLogicalOperator?: FilterLogicalOperator;
|
|
40
|
+
geoColumn?: string;
|
|
41
|
+
spatialFilter?: SpatialFilter;
|
|
42
|
+
queryParameters?: QueryParameters;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const {V3} = ApiVersion;
|
|
46
|
+
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
47
|
+
|
|
25
48
|
/**
|
|
26
49
|
* Execute a SQL model request.
|
|
27
50
|
* @internalRemarks Source: @carto/react-api
|
|
28
51
|
*/
|
|
29
52
|
export function executeModel(props: {
|
|
30
53
|
model: Model;
|
|
31
|
-
source:
|
|
54
|
+
source: ModelSource;
|
|
32
55
|
params: Record<string, unknown>;
|
|
33
|
-
spatialFilter?: SpatialFilter;
|
|
34
56
|
opts?: Partial<ModelRequestOptions>;
|
|
35
57
|
}) {
|
|
36
58
|
assert(props.source, 'executeModel: missing source');
|
|
@@ -38,28 +60,23 @@ export function executeModel(props: {
|
|
|
38
60
|
assert(props.params, 'executeModel: missing params');
|
|
39
61
|
|
|
40
62
|
assert(
|
|
41
|
-
AVAILABLE_MODELS.
|
|
63
|
+
AVAILABLE_MODELS.includes(props.model),
|
|
42
64
|
`executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(
|
|
43
65
|
', '
|
|
44
66
|
)}`
|
|
45
67
|
);
|
|
46
68
|
|
|
47
|
-
const {
|
|
69
|
+
const {model, source, params, opts} = props;
|
|
70
|
+
const {type, apiVersion, apiBaseUrl, accessToken, connectionName} = source;
|
|
48
71
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
assert(
|
|
52
|
-
|
|
53
|
-
'SQL Model API is a feature only available in CARTO 3.'
|
|
54
|
-
);
|
|
55
|
-
assert(
|
|
56
|
-
source.type !== MapType.TILESET,
|
|
57
|
-
'executeModel: Tileset not supported'
|
|
58
|
-
);
|
|
72
|
+
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
73
|
+
assert(accessToken, 'executeModel: missing accessToken');
|
|
74
|
+
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
75
|
+
assert(type !== MapType.TILESET, 'executeModel: Tilesets not supported');
|
|
59
76
|
|
|
60
|
-
let url = `${
|
|
77
|
+
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
61
78
|
|
|
62
|
-
const {filters, filtersLogicalOperator = 'and', data
|
|
79
|
+
const {filters, filtersLogicalOperator = 'and', data} = source;
|
|
63
80
|
const queryParameters = source.queryParameters
|
|
64
81
|
? JSON.stringify(source.queryParameters)
|
|
65
82
|
: '';
|
|
@@ -75,10 +92,10 @@ export function executeModel(props: {
|
|
|
75
92
|
};
|
|
76
93
|
|
|
77
94
|
// API supports multiple filters, we apply it only to geoColumn
|
|
78
|
-
const spatialFilters = spatialFilter
|
|
95
|
+
const spatialFilters = source.spatialFilter
|
|
79
96
|
? {
|
|
80
97
|
[source.geoColumn ? source.geoColumn : DEFAULT_GEO_COLUMN]:
|
|
81
|
-
spatialFilter,
|
|
98
|
+
source.spatialFilter,
|
|
82
99
|
}
|
|
83
100
|
: undefined;
|
|
84
101
|
|
|
@@ -102,7 +119,7 @@ export function executeModel(props: {
|
|
|
102
119
|
}
|
|
103
120
|
return makeCall({
|
|
104
121
|
url,
|
|
105
|
-
|
|
122
|
+
accessToken: source.accessToken,
|
|
106
123
|
opts: {
|
|
107
124
|
...opts,
|
|
108
125
|
method: isGet ? 'GET' : 'POST',
|
package/src/sources/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {GroupDateType} from '../constants';
|
|
1
2
|
import {
|
|
2
3
|
AggregationType,
|
|
3
4
|
SortColumnType,
|
|
@@ -9,28 +10,48 @@ import {
|
|
|
9
10
|
* WIDGET API REQUESTS
|
|
10
11
|
*/
|
|
11
12
|
|
|
13
|
+
/** Common options for {@link WidgetBaseSource} requests. */
|
|
12
14
|
interface BaseRequestOptions {
|
|
13
15
|
spatialFilter?: SpatialFilter;
|
|
14
16
|
abortController?: AbortController;
|
|
15
17
|
filterOwner?: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
/** Options for {@link WidgetBaseSource#getCategories}. */
|
|
21
|
+
export interface CategoryRequestOptions extends BaseRequestOptions {
|
|
22
|
+
column: string;
|
|
23
|
+
operation?: AggregationType;
|
|
24
|
+
operationColumn?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Options for {@link WidgetBaseSource#getFormula}. */
|
|
18
28
|
export interface FormulaRequestOptions extends BaseRequestOptions {
|
|
19
29
|
column: string;
|
|
20
30
|
operation?: AggregationType;
|
|
21
31
|
operationExp?: string;
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
/** Options for {@link WidgetBaseSource#getHistogram}. */
|
|
35
|
+
export interface HistogramRequestOptions extends BaseRequestOptions {
|
|
25
36
|
column: string;
|
|
37
|
+
ticks: number[];
|
|
26
38
|
operation?: AggregationType;
|
|
27
|
-
operationColumn?: string;
|
|
28
39
|
}
|
|
29
40
|
|
|
41
|
+
/** Options for {@link WidgetBaseSource#getRange}. */
|
|
30
42
|
export interface RangeRequestOptions extends BaseRequestOptions {
|
|
31
43
|
column: string;
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
/** Options for {@link WidgetBaseSource#getScatter}. */
|
|
47
|
+
export interface ScatterRequestOptions extends BaseRequestOptions {
|
|
48
|
+
xAxisColumn: string;
|
|
49
|
+
xAxisJoinOperation?: AggregationType;
|
|
50
|
+
yAxisColumn: string;
|
|
51
|
+
yAxisJoinOperation?: AggregationType;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Options for {@link WidgetBaseSource#getTable}. */
|
|
34
55
|
export interface TableRequestOptions extends BaseRequestOptions {
|
|
35
56
|
columns: string[];
|
|
36
57
|
sortBy?: string;
|
|
@@ -40,16 +61,10 @@ export interface TableRequestOptions extends BaseRequestOptions {
|
|
|
40
61
|
rowsPerPage?: number;
|
|
41
62
|
}
|
|
42
63
|
|
|
43
|
-
|
|
44
|
-
xAxisColumn: string;
|
|
45
|
-
xAxisJoinOperation?: AggregationType;
|
|
46
|
-
yAxisColumn: string;
|
|
47
|
-
yAxisJoinOperation?: AggregationType;
|
|
48
|
-
}
|
|
49
|
-
|
|
64
|
+
/** Options for {@link WidgetBaseSource#getTimeSeries}. */
|
|
50
65
|
export interface TimeSeriesRequestOptions extends BaseRequestOptions {
|
|
51
66
|
column: string;
|
|
52
|
-
stepSize?:
|
|
67
|
+
stepSize?: GroupDateType;
|
|
53
68
|
stepMultiplier?: number;
|
|
54
69
|
operation?: AggregationType;
|
|
55
70
|
operationColumn?: string;
|
|
@@ -59,32 +74,33 @@ export interface TimeSeriesRequestOptions extends BaseRequestOptions {
|
|
|
59
74
|
splitByCategoryValues?: string[];
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
export interface HistogramRequestOptions extends BaseRequestOptions {
|
|
63
|
-
column: string;
|
|
64
|
-
ticks: number[];
|
|
65
|
-
operation?: AggregationType;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
77
|
/******************************************************************************
|
|
69
78
|
* WIDGET API RESPONSES
|
|
70
79
|
*/
|
|
71
80
|
|
|
81
|
+
/** Response from {@link WidgetBaseSource#getFormula}. */
|
|
72
82
|
export type FormulaResponse = {value: number};
|
|
73
83
|
|
|
84
|
+
/** Response from {@link WidgetBaseSource#getCategories}. */
|
|
74
85
|
export type CategoryResponse = {name: string; value: number}[];
|
|
75
86
|
|
|
87
|
+
/** Response from {@link WidgetBaseSource#getRange}. */
|
|
76
88
|
export type RangeResponse = {min: number; max: number};
|
|
77
89
|
|
|
90
|
+
/** Response from {@link WidgetBaseSource#getTable}. */
|
|
78
91
|
export type TableResponse = {
|
|
79
92
|
totalCount: number;
|
|
80
93
|
rows: Record<string, number | string>[];
|
|
81
94
|
};
|
|
82
95
|
|
|
96
|
+
/** Response from {@link WidgetBaseSource#getScatter}. */
|
|
83
97
|
export type ScatterResponse = [number, number][];
|
|
84
98
|
|
|
99
|
+
/** Response from {@link WidgetBaseSource#getTimeSeries}. */
|
|
85
100
|
export type TimeSeriesResponse = {
|
|
86
101
|
rows: {name: string; value: number}[];
|
|
87
102
|
categories: string[];
|
|
88
103
|
};
|
|
89
104
|
|
|
105
|
+
/** Response from {@link WidgetBaseSource#getHistogram}. */
|
|
90
106
|
export type HistogramResponse = number[];
|
|
@@ -15,147 +15,196 @@ import {
|
|
|
15
15
|
TimeSeriesRequestOptions,
|
|
16
16
|
TimeSeriesResponse,
|
|
17
17
|
} from './types.js';
|
|
18
|
-
import {
|
|
18
|
+
import {FilterLogicalOperator, Filter} from '../types.js';
|
|
19
19
|
import {SourceOptions} from '@deck.gl/carto';
|
|
20
20
|
import {getApplicableFilters, normalizeObjectKeys} from '../utils.js';
|
|
21
|
-
import {ApiVersion, MapType} from '../constants.js';
|
|
22
21
|
import {
|
|
23
22
|
DEFAULT_API_BASE_URL,
|
|
24
23
|
DEFAULT_GEO_COLUMN,
|
|
24
|
+
ApiVersion,
|
|
25
25
|
} from '../constants-internal.js';
|
|
26
26
|
import {getClient} from '../client.js';
|
|
27
|
-
import {
|
|
27
|
+
import {ModelSource} from '../models/model.js';
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export interface WidgetBaseSourceProps extends SourceOptions, Credentials {
|
|
33
|
-
type?: MapType;
|
|
29
|
+
export interface WidgetBaseSourceProps extends Omit<SourceOptions, 'filters'> {
|
|
30
|
+
apiVersion?: ApiVersion;
|
|
31
|
+
geoColumn?: string;
|
|
34
32
|
filters?: Record<string, Filter>;
|
|
35
33
|
filtersLogicalOperator?: FilterLogicalOperator;
|
|
36
|
-
queryParameters?: unknown[];
|
|
37
|
-
provider?: string;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
export type WidgetSource = WidgetBaseSource<WidgetBaseSourceProps>;
|
|
41
37
|
|
|
42
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
40
|
+
*
|
|
41
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
42
|
+
*/
|
|
43
|
+
export abstract class WidgetBaseSource<Props extends WidgetBaseSourceProps> {
|
|
43
44
|
readonly props: Props;
|
|
44
|
-
readonly credentials: Required<Credentials> & {clientId: string};
|
|
45
|
-
readonly connectionName: string;
|
|
46
45
|
|
|
47
46
|
static defaultProps: Partial<WidgetBaseSourceProps> = {
|
|
47
|
+
apiVersion: ApiVersion.V3,
|
|
48
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
49
|
+
clientId: getClient(),
|
|
48
50
|
filters: {},
|
|
49
51
|
filtersLogicalOperator: 'and',
|
|
52
|
+
geoColumn: DEFAULT_GEO_COLUMN,
|
|
50
53
|
};
|
|
51
54
|
|
|
52
55
|
constructor(props: Props) {
|
|
53
56
|
this.props = {...WidgetBaseSource.defaultProps, ...props};
|
|
54
|
-
this.connectionName = props.connectionName;
|
|
55
|
-
this.credentials = {
|
|
56
|
-
apiVersion: props.apiVersion || ApiVersion.V3,
|
|
57
|
-
apiBaseUrl: props.apiBaseUrl || DEFAULT_API_BASE_URL,
|
|
58
|
-
clientId: props.clientId || getClient(),
|
|
59
|
-
accessToken: props.accessToken,
|
|
60
|
-
geoColumn: props.geoColumn || DEFAULT_GEO_COLUMN,
|
|
61
|
-
};
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Subclasses of {@link WidgetBaseSource} must implement this method, calling
|
|
61
|
+
* {@link WidgetBaseSource.prototype._getModelSource} for common source
|
|
62
|
+
* properties, and adding additional required properties including 'type' and
|
|
63
|
+
* 'data'.
|
|
64
|
+
*/
|
|
65
|
+
protected abstract getModelSource(owner: string | undefined): ModelSource;
|
|
66
|
+
|
|
67
|
+
protected _getModelSource(
|
|
68
|
+
owner?: string
|
|
69
|
+
): Omit<ModelSource, 'type' | 'data'> {
|
|
70
|
+
const props = this.props;
|
|
65
71
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
apiVersion: props.apiVersion as ApiVersion,
|
|
73
|
+
apiBaseUrl: props.apiBaseUrl as string,
|
|
74
|
+
clientId: props.clientId as string,
|
|
75
|
+
accessToken: props.accessToken,
|
|
76
|
+
connectionName: props.connectionName,
|
|
77
|
+
filters: getApplicableFilters(owner, props.filters),
|
|
78
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
79
|
+
geoColumn: props.geoColumn,
|
|
70
80
|
};
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
/****************************************************************************
|
|
84
|
+
* CATEGORIES
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Returns a list of labeled datapoints for categorical data. Suitable for
|
|
89
|
+
* charts including grouped bar charts, pie charts, and tree charts.
|
|
90
|
+
*/
|
|
91
|
+
async getCategories(
|
|
92
|
+
options: CategoryRequestOptions
|
|
93
|
+
): Promise<CategoryResponse> {
|
|
94
|
+
const {filterOwner, spatialFilter, abortController, ...params} = options;
|
|
95
|
+
const {column, operation, operationColumn} = params;
|
|
96
|
+
|
|
97
|
+
type CategoriesModelResponse = {rows: {name: string; value: number}[]};
|
|
98
|
+
|
|
99
|
+
return executeModel({
|
|
100
|
+
model: 'category',
|
|
101
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
102
|
+
params: {
|
|
103
|
+
column,
|
|
104
|
+
operation,
|
|
105
|
+
operationColumn: operationColumn || column,
|
|
106
|
+
},
|
|
107
|
+
opts: {abortController},
|
|
108
|
+
}).then((res: CategoriesModelResponse) => normalizeObjectKeys(res.rows));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/****************************************************************************
|
|
112
|
+
* FORMULA
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
117
|
+
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
118
|
+
*/
|
|
119
|
+
async getFormula(options: FormulaRequestOptions): Promise<FormulaResponse> {
|
|
74
120
|
const {
|
|
75
121
|
filterOwner,
|
|
76
122
|
spatialFilter,
|
|
77
123
|
abortController,
|
|
78
124
|
operationExp,
|
|
79
125
|
...params
|
|
80
|
-
} =
|
|
126
|
+
} = options;
|
|
81
127
|
const {column, operation} = params;
|
|
82
128
|
|
|
83
129
|
type FormulaModelResponse = {rows: {value: number}[]};
|
|
84
130
|
|
|
85
131
|
return executeModel({
|
|
86
132
|
model: 'formula',
|
|
87
|
-
source: this.
|
|
88
|
-
spatialFilter,
|
|
133
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
89
134
|
params: {column: column ?? '*', operation, operationExp},
|
|
90
135
|
opts: {abortController},
|
|
91
136
|
}).then((res: FormulaModelResponse) => normalizeObjectKeys(res.rows[0]));
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const {filterOwner, spatialFilter, abortController, ...params} = props;
|
|
98
|
-
const {column, operation, operationColumn} = params;
|
|
139
|
+
/****************************************************************************
|
|
140
|
+
* HISTOGRAM
|
|
141
|
+
*/
|
|
99
142
|
|
|
100
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
145
|
+
* over a numerical range. Suitable for histogram charts.
|
|
146
|
+
*/
|
|
147
|
+
async getHistogram(
|
|
148
|
+
options: HistogramRequestOptions
|
|
149
|
+
): Promise<HistogramResponse> {
|
|
150
|
+
const {filterOwner, spatialFilter, abortController, ...params} = options;
|
|
151
|
+
const {column, operation, ticks} = params;
|
|
101
152
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
operation,
|
|
109
|
-
operationColumn: operationColumn || column,
|
|
110
|
-
},
|
|
153
|
+
type HistogramModelResponse = {rows: {tick: number; value: number}[]};
|
|
154
|
+
|
|
155
|
+
const data = await executeModel({
|
|
156
|
+
model: 'histogram',
|
|
157
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
158
|
+
params: {column, operation, ticks},
|
|
111
159
|
opts: {abortController},
|
|
112
|
-
}).then((res:
|
|
160
|
+
}).then((res: HistogramModelResponse) => normalizeObjectKeys(res.rows));
|
|
161
|
+
|
|
162
|
+
if (data.length) {
|
|
163
|
+
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
164
|
+
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
165
|
+
const result = Array(ticks.length + 1).fill(0);
|
|
166
|
+
data.forEach(
|
|
167
|
+
({tick, value}: {tick: number; value: number}) => (result[tick] = value)
|
|
168
|
+
);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return [];
|
|
113
173
|
}
|
|
114
174
|
|
|
115
|
-
|
|
116
|
-
|
|
175
|
+
/****************************************************************************
|
|
176
|
+
* RANGE
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Returns a range (min and max) for a numerical column of matching rows.
|
|
181
|
+
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
182
|
+
* or rendering a range slider UI for filtering.
|
|
183
|
+
*/
|
|
184
|
+
async getRange(options: RangeRequestOptions): Promise<RangeResponse> {
|
|
185
|
+
const {filterOwner, spatialFilter, abortController, ...params} = options;
|
|
117
186
|
const {column} = params;
|
|
118
187
|
|
|
119
188
|
type RangeModelResponse = {rows: {min: number; max: number}[]};
|
|
120
189
|
|
|
121
190
|
return executeModel({
|
|
122
191
|
model: 'range',
|
|
123
|
-
source: this.
|
|
124
|
-
spatialFilter,
|
|
192
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
125
193
|
params: {column},
|
|
126
194
|
opts: {abortController},
|
|
127
195
|
}).then((res: RangeModelResponse) => normalizeObjectKeys(res.rows[0]));
|
|
128
196
|
}
|
|
129
197
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
type TableModelResponse = {
|
|
135
|
-
rows: Record<string, number | string>[];
|
|
136
|
-
metadata: {total: number};
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
return executeModel({
|
|
140
|
-
model: 'table',
|
|
141
|
-
source: this.getSource(filterOwner),
|
|
142
|
-
spatialFilter,
|
|
143
|
-
params: {
|
|
144
|
-
column: columns,
|
|
145
|
-
sortBy,
|
|
146
|
-
sortDirection,
|
|
147
|
-
limit: rowsPerPage,
|
|
148
|
-
offset: page * rowsPerPage,
|
|
149
|
-
},
|
|
150
|
-
opts: {abortController},
|
|
151
|
-
}).then((res: TableModelResponse) => ({
|
|
152
|
-
rows: normalizeObjectKeys(res.rows),
|
|
153
|
-
totalCount: res.metadata.total,
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
198
|
+
/****************************************************************************
|
|
199
|
+
* SCATTER
|
|
200
|
+
*/
|
|
156
201
|
|
|
157
|
-
|
|
158
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
204
|
+
* values. Suitable for rendering scatter plots.
|
|
205
|
+
*/
|
|
206
|
+
async getScatter(options: ScatterRequestOptions): Promise<ScatterResponse> {
|
|
207
|
+
const {filterOwner, spatialFilter, abortController, ...params} = options;
|
|
159
208
|
const {xAxisColumn, xAxisJoinOperation, yAxisColumn, yAxisJoinOperation} =
|
|
160
209
|
params;
|
|
161
210
|
|
|
@@ -166,8 +215,7 @@ export class WidgetBaseSource<Props extends WidgetBaseSourceProps> {
|
|
|
166
215
|
|
|
167
216
|
return executeModel({
|
|
168
217
|
model: 'scatterplot',
|
|
169
|
-
source: this.
|
|
170
|
-
spatialFilter,
|
|
218
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
171
219
|
params: {
|
|
172
220
|
xAxisColumn,
|
|
173
221
|
xAxisJoinOperation,
|
|
@@ -181,10 +229,52 @@ export class WidgetBaseSource<Props extends WidgetBaseSourceProps> {
|
|
|
181
229
|
.then((res) => res.map(({x, y}: {x: number; y: number}) => [x, y]));
|
|
182
230
|
}
|
|
183
231
|
|
|
232
|
+
/****************************************************************************
|
|
233
|
+
* TABLE
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns a list of arbitrary data rows, with support for pagination and
|
|
238
|
+
* sorting. Suitable for displaying tables and lists.
|
|
239
|
+
*/
|
|
240
|
+
async getTable(options: TableRequestOptions): Promise<TableResponse> {
|
|
241
|
+
const {filterOwner, spatialFilter, abortController, ...params} = options;
|
|
242
|
+
const {columns, sortBy, sortDirection, page = 0, rowsPerPage = 10} = params;
|
|
243
|
+
|
|
244
|
+
type TableModelResponse = {
|
|
245
|
+
rows: Record<string, number | string>[];
|
|
246
|
+
metadata: {total: number};
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return executeModel({
|
|
250
|
+
model: 'table',
|
|
251
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
252
|
+
params: {
|
|
253
|
+
column: columns,
|
|
254
|
+
sortBy,
|
|
255
|
+
sortDirection,
|
|
256
|
+
limit: rowsPerPage,
|
|
257
|
+
offset: page * rowsPerPage,
|
|
258
|
+
},
|
|
259
|
+
opts: {abortController},
|
|
260
|
+
}).then((res: TableModelResponse) => ({
|
|
261
|
+
rows: normalizeObjectKeys(res.rows),
|
|
262
|
+
totalCount: res.metadata.total,
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/****************************************************************************
|
|
267
|
+
* TIME SERIES
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
272
|
+
* time intervals. Suitable for rendering time series charts.
|
|
273
|
+
*/
|
|
184
274
|
async getTimeSeries(
|
|
185
|
-
|
|
275
|
+
options: TimeSeriesRequestOptions
|
|
186
276
|
): Promise<TimeSeriesResponse> {
|
|
187
|
-
const {filterOwner, abortController, spatialFilter, ...params} =
|
|
277
|
+
const {filterOwner, abortController, spatialFilter, ...params} = options;
|
|
188
278
|
const {
|
|
189
279
|
column,
|
|
190
280
|
operationColumn,
|
|
@@ -204,8 +294,7 @@ export class WidgetBaseSource<Props extends WidgetBaseSourceProps> {
|
|
|
204
294
|
|
|
205
295
|
return executeModel({
|
|
206
296
|
model: 'timeseries',
|
|
207
|
-
source: this.
|
|
208
|
-
spatialFilter,
|
|
297
|
+
source: {...this.getModelSource(filterOwner), spatialFilter},
|
|
209
298
|
params: {
|
|
210
299
|
column,
|
|
211
300
|
stepSize,
|
|
@@ -223,33 +312,4 @@ export class WidgetBaseSource<Props extends WidgetBaseSourceProps> {
|
|
|
223
312
|
categories: res.metadata?.categories,
|
|
224
313
|
}));
|
|
225
314
|
}
|
|
226
|
-
|
|
227
|
-
async getHistogram(
|
|
228
|
-
props: HistogramRequestOptions
|
|
229
|
-
): Promise<HistogramResponse> {
|
|
230
|
-
const {filterOwner, spatialFilter, abortController, ...params} = props;
|
|
231
|
-
const {column, operation, ticks} = params;
|
|
232
|
-
|
|
233
|
-
type HistogramModelResponse = {rows: {tick: number; value: number}[]};
|
|
234
|
-
|
|
235
|
-
const data = await executeModel({
|
|
236
|
-
model: 'histogram',
|
|
237
|
-
source: this.getSource(filterOwner),
|
|
238
|
-
spatialFilter,
|
|
239
|
-
params: {column, operation, ticks},
|
|
240
|
-
opts: {abortController},
|
|
241
|
-
}).then((res: HistogramModelResponse) => normalizeObjectKeys(res.rows));
|
|
242
|
-
|
|
243
|
-
if (data.length) {
|
|
244
|
-
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
245
|
-
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
246
|
-
const result = Array(ticks.length + 1).fill(0);
|
|
247
|
-
data.forEach(
|
|
248
|
-
({tick, value}: {tick: number; value: number}) => (result[tick] = value)
|
|
249
|
-
);
|
|
250
|
-
return result;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return [];
|
|
254
|
-
}
|
|
255
315
|
}
|
|
@@ -3,23 +3,46 @@ import {
|
|
|
3
3
|
QuadbinQuerySourceOptions,
|
|
4
4
|
VectorQuerySourceOptions,
|
|
5
5
|
} from '@deck.gl/carto';
|
|
6
|
-
import {MapType} from '../constants.js';
|
|
6
|
+
import {MapType} from '../constants-internal.js';
|
|
7
7
|
import {WidgetBaseSource, WidgetBaseSourceProps} from './widget-base-source.js';
|
|
8
|
-
import {
|
|
8
|
+
import {ModelSource} from '../models/model.js';
|
|
9
9
|
|
|
10
10
|
type LayerQuerySourceOptions =
|
|
11
|
-
| VectorQuerySourceOptions
|
|
12
|
-
| H3QuerySourceOptions
|
|
13
|
-
| QuadbinQuerySourceOptions
|
|
11
|
+
| Omit<VectorQuerySourceOptions, 'filters'>
|
|
12
|
+
| Omit<H3QuerySourceOptions, 'filters'>
|
|
13
|
+
| Omit<QuadbinQuerySourceOptions, 'filters'>;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
17
|
+
*
|
|
18
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
19
|
+
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
20
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
21
|
+
* for use by widget implementations.
|
|
22
|
+
*
|
|
23
|
+
* Example:
|
|
24
|
+
*
|
|
25
|
+
* ```javascript
|
|
26
|
+
* import { vectorQuerySource } from '@carto/api-client';
|
|
27
|
+
*
|
|
28
|
+
* const data = vectorQuerySource({
|
|
29
|
+
* accessToken: '••••',
|
|
30
|
+
* connectionName: 'carto_dw',
|
|
31
|
+
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* const { widgetSource } = await data;
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
15
37
|
export class WidgetQuerySource extends WidgetBaseSource<
|
|
16
38
|
LayerQuerySourceOptions & WidgetBaseSourceProps
|
|
17
39
|
> {
|
|
18
|
-
protected override
|
|
40
|
+
protected override getModelSource(owner: string): ModelSource {
|
|
19
41
|
return {
|
|
20
|
-
...super.
|
|
42
|
+
...super._getModelSource(owner),
|
|
21
43
|
type: MapType.QUERY,
|
|
22
44
|
data: this.props.sqlQuery,
|
|
45
|
+
queryParameters: this.props.queryParameters,
|
|
23
46
|
};
|
|
24
47
|
}
|
|
25
48
|
}
|