Skip to content

Commit

Permalink
feat(sources): Move sources from @deck.gl/carto (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy authored Nov 12, 2024
1 parent ab9c5a0 commit 69d138c
Show file tree
Hide file tree
Showing 60 changed files with 3,055 additions and 426 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"repository": "github:CartoDB/carto-api-client",
"author": "Don McCurdy <[email protected]>",
"packageManager": "[email protected]",
"version": "0.3.1",
"version": "0.4.0-alpha.5",
"license": "MIT",
"publishConfig": {
"access": "public",
Expand Down Expand Up @@ -52,7 +52,6 @@
"LICENSE.md"
],
"dependencies": {
"@deck.gl/carto": "^9.0.30",
"@turf/bbox-clip": "^7.1.0",
"@turf/bbox-polygon": "^7.1.0",
"@turf/helpers": "^7.1.0",
Expand All @@ -62,6 +61,7 @@
},
"devDependencies": {
"@deck.gl/aggregation-layers": "^9.0.30",
"@deck.gl/carto": "^9.0.30",
"@deck.gl/core": "^9.0.30",
"@deck.gl/extensions": "^9.0.30",
"@deck.gl/geo-layers": "^9.0.30",
Expand Down Expand Up @@ -98,5 +98,6 @@
"vite": "^5.2.10",
"vitest": "1.6.0",
"vue": "^3.4.27"
}
},
"stableVersion": "0.3.1"
}
88 changes: 88 additions & 0 deletions src/api/carto-api-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {MapType} from '../types';

export type APIRequestType =
| 'Map data'
| 'Map instantiation'
| 'Public map'
| 'Tile stats'
| 'SQL'
| 'Basemap style';

export type APIErrorContext = {
requestType: APIRequestType;
mapId?: string;
connection?: string;
source?: string;
type?: MapType;
};

/**
*
* Custom error for reported errors in CARTO Maps API.
* Provides useful debugging information in console and context for applications.
*
*/
export class CartoAPIError extends Error {
/** Source error from server */
error: Error;

/** Context (API call & parameters) in which error occured */
errorContext: APIErrorContext;

/** Response from server */
response?: Response;

/** JSON Response from server */
responseJson?: any;

constructor(
error: Error,
errorContext: APIErrorContext,
response?: Response,
responseJson?: any
) {
let responseString = 'Failed to connect';
if (response) {
responseString = 'Server returned: ';
if (response.status === 400) {
responseString += 'Bad request';
} else if (response.status === 401 || response.status === 403) {
responseString += 'Unauthorized access';
} else if (response.status === 404) {
responseString += 'Not found';
} else {
responseString += 'Error';
}

responseString += ` (${response.status}):`;
}
responseString += ` ${error.message || error}`;

let message = `${errorContext.requestType} API request failed`;
message += `\n${responseString}`;
for (const key of Object.keys(errorContext)) {
if (key === 'requestType') continue;
message += `\n${formatErrorKey(key)}: ${(errorContext as any)[key]}`;
}
message += '\n';

super(message);

this.name = 'CartoAPIError';
this.response = response;
this.responseJson = responseJson;
this.error = error;
this.errorContext = errorContext;
}
}

/**
* Converts camelCase to Camel Case
*/
function formatErrorKey(key: string) {
return key.replace(/([A-Z])/g, ' $1').replace(/^./, (s) => s.toUpperCase());
}
84 changes: 84 additions & 0 deletions src/api/endpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {MapType} from '../types.js';

export type V3Endpoint = 'maps' | 'stats' | 'sql';

function joinPath(...args: string[]): string {
return args
.map((part) => (part.endsWith('/') ? part.slice(0, -1) : part))
.join('/');
}

function buildV3Path(
apiBaseUrl: string,
version: 'v3',
endpoint: V3Endpoint,
...rest: string[]
): string {
return joinPath(apiBaseUrl, version, endpoint, ...rest);
}

/** @internal Required by fetchMap(). */
export function buildPublicMapUrl({
apiBaseUrl,
cartoMapId,
}: {
apiBaseUrl: string;
cartoMapId: string;
}): string {
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
}

/** @internal Required by fetchMap(). */
export function buildStatsUrl({
attribute,
apiBaseUrl,
connectionName,
source,
type,
}: {
attribute: string;
apiBaseUrl: string;
connectionName: string;
source: string;
type: MapType;
}): string {
if (type === 'query') {
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
}

// type === 'table'
return buildV3Path(
apiBaseUrl,
'v3',
'stats',
connectionName,
source,
attribute
);
}

export function buildSourceUrl({
apiBaseUrl,
connectionName,
endpoint,
}: {
apiBaseUrl: string;
connectionName: string;
endpoint: MapType;
}): string {
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
}

export function buildQueryUrl({
apiBaseUrl,
connectionName,
}: {
apiBaseUrl: string;
connectionName: string;
}): string {
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
}
14 changes: 14 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

export {
CartoAPIError,
APIErrorContext,
APIRequestType,
} from './carto-api-error.js';
// Internal, but required for fetchMap().
export {buildPublicMapUrl, buildStatsUrl} from './endpoints.js';
export {query} from './query.js';
export type {QueryOptions} from './query.js';
export {requestWithParameters} from './request-with-parameters.js';
56 changes: 56 additions & 0 deletions src/api/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {SOURCE_DEFAULTS} from '../sources/index';
import type {
SourceOptions,
QuerySourceOptions,
QueryResult,
} from '../sources/types';
import {buildQueryUrl} from './endpoints';
import {requestWithParameters} from './request-with-parameters';
import {APIErrorContext} from './carto-api-error';

export type QueryOptions = SourceOptions &
Omit<QuerySourceOptions, 'spatialDataColumn'>;
type UrlParameters = {q: string; queryParameters?: string};

export const query = async function (
options: QueryOptions
): Promise<QueryResult> {
const {
apiBaseUrl = SOURCE_DEFAULTS.apiBaseUrl,
clientId = SOURCE_DEFAULTS.clientId,
maxLengthURL = SOURCE_DEFAULTS.maxLengthURL,
connectionName,
sqlQuery,
queryParameters,
} = options;
const urlParameters: UrlParameters = {q: sqlQuery};

if (queryParameters) {
urlParameters.queryParameters = JSON.stringify(queryParameters);
}

const baseUrl = buildQueryUrl({apiBaseUrl, connectionName});
const headers = {
Authorization: `Bearer ${options.accessToken}`,
...options.headers,
};
const parameters = {client: clientId, ...urlParameters};

const errorContext: APIErrorContext = {
requestType: 'SQL',
connection: options.connectionName,
type: 'query',
source: JSON.stringify(parameters, undefined, 2),
};
return await requestWithParameters<QueryResult>({
baseUrl,
parameters,
headers,
errorContext,
maxLengthURL,
});
};
Loading

0 comments on commit 69d138c

Please sign in to comment.