mirror of
https://github.com/FlipsideCrypto/sdk.git
synced 2026-02-06 10:46:43 +00:00
Refactor query integration to new api and query result set builder
This commit is contained in:
parent
4ea1ae8dac
commit
7c0ceb89af
@ -1,103 +0,0 @@
|
||||
import {
|
||||
Query,
|
||||
CreateQueryResp,
|
||||
QueryResultResp,
|
||||
CreateQueryJson,
|
||||
QueryResultJson,
|
||||
ApiClient,
|
||||
} from "./types";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import { UnexpectedSDKError } from "./errors";
|
||||
|
||||
const PARSE_ERROR_MSG =
|
||||
"the api returned an error and there was a fatal client side error parsing that error msg";
|
||||
|
||||
export class API implements ApiClient {
|
||||
#baseUrl: string;
|
||||
#headers: Record<string, string>;
|
||||
#sdkVersion: string;
|
||||
#sdkPackage: string;
|
||||
|
||||
constructor(baseUrl: string, sdkPackage: string, sdkVersion: string, apiKey: string) {
|
||||
this.#baseUrl = baseUrl;
|
||||
this.#sdkPackage = sdkPackage;
|
||||
this.#sdkVersion = sdkVersion;
|
||||
this.#headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": apiKey,
|
||||
};
|
||||
}
|
||||
|
||||
getUrl(path: string): string {
|
||||
return `${this.#baseUrl}/${path}`;
|
||||
}
|
||||
|
||||
async createQuery(query: Query): Promise<CreateQueryResp> {
|
||||
let result;
|
||||
try {
|
||||
result = await axios.post(
|
||||
this.getUrl("queries"),
|
||||
{
|
||||
sql: query.sql,
|
||||
ttl_minutes: query.ttlMinutes,
|
||||
cached: query.cached,
|
||||
sdk_package: this.#sdkPackage,
|
||||
sdk_version: this.#sdkVersion,
|
||||
},
|
||||
{ headers: this.#headers }
|
||||
);
|
||||
} catch (err) {
|
||||
let errData = err as AxiosError;
|
||||
result = errData.response;
|
||||
if (!result) {
|
||||
throw new UnexpectedSDKError(PARSE_ERROR_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
let data: CreateQueryJson | null;
|
||||
if (result.status >= 200 && result.status < 300) {
|
||||
data = result.data;
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: result.status,
|
||||
statusMsg: result.statusText,
|
||||
errorMsg: data?.errors,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
async getQueryResult(queryID: string, pageNumber: number, pageSize: number): Promise<QueryResultResp> {
|
||||
let result;
|
||||
try {
|
||||
result = await axios.get(this.getUrl(`queries/${queryID}`), {
|
||||
params: { pageNumber: pageNumber, pageSize: pageSize },
|
||||
method: "GET",
|
||||
headers: this.#headers,
|
||||
});
|
||||
} catch (err) {
|
||||
let errData = err as AxiosError;
|
||||
result = errData.response;
|
||||
if (!result) {
|
||||
throw new UnexpectedSDKError(PARSE_ERROR_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
let data: QueryResultJson | null;
|
||||
if (result.status >= 200 && result.status < 300) {
|
||||
data = result.data;
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: result.status,
|
||||
statusMsg: result.statusText,
|
||||
errorMsg: data?.errors,
|
||||
data,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -25,43 +25,19 @@ import {
|
||||
|
||||
const PARSE_ERROR_MSG = "the api returned an error and there was a fatal client side error parsing that error msg";
|
||||
|
||||
class Api {
|
||||
export class Api {
|
||||
url: string;
|
||||
#baseUrl: string;
|
||||
#headers: Record<string, string>;
|
||||
#sdkVersion: string;
|
||||
#sdkPackage: string;
|
||||
#apiKey: string;
|
||||
#MAX_RETRIES: number;
|
||||
#BACKOFF_FACTOR: number;
|
||||
#STATUS_FORCE_LIST: number[];
|
||||
#METHOD_ALLOWLIST: string[];
|
||||
|
||||
constructor(
|
||||
baseUrl: string,
|
||||
sdkPackage: string,
|
||||
sdkVersion: string,
|
||||
apiKey: string,
|
||||
max_retries: number = 10,
|
||||
backoff_factor: number = 1,
|
||||
status_forcelist: number[] = [429, 500, 502, 503, 504],
|
||||
method_allowlist: string[] = ["HEAD", "GET", "PUT", "POST", "DELETE", "OPTIONS", "TRACE"]
|
||||
) {
|
||||
constructor(baseUrl: string, apiKey: string) {
|
||||
this.#baseUrl = baseUrl;
|
||||
this.url = this.getUrl();
|
||||
this.#apiKey = apiKey;
|
||||
this.#sdkPackage = sdkPackage;
|
||||
this.#sdkVersion = sdkVersion;
|
||||
this.#headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": apiKey,
|
||||
};
|
||||
// Session Settings
|
||||
this.#MAX_RETRIES = max_retries;
|
||||
this.#BACKOFF_FACTOR = backoff_factor;
|
||||
this.#STATUS_FORCE_LIST = status_forcelist;
|
||||
this.#METHOD_ALLOWLIST = method_allowlist;
|
||||
}
|
||||
|
||||
getUrl(): string {
|
||||
|
||||
17
js/src/defaults.ts
Normal file
17
js/src/defaults.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { version } from "../package.json";
|
||||
import { SdkDefaults } from "./types";
|
||||
|
||||
export const DEFAULTS: SdkDefaults = {
|
||||
apiBaseUrl: "https://api-v2.flipsidecrypto.xyz",
|
||||
ttlMinutes: 60,
|
||||
maxAgeMinutes: 0,
|
||||
cached: true,
|
||||
dataProvider: "flipside",
|
||||
dataSource: "snowflake-default",
|
||||
timeoutMinutes: 20,
|
||||
retryIntervalSeconds: 0.5,
|
||||
pageSize: 100000,
|
||||
pageNumber: 1,
|
||||
sdkPackage: "js",
|
||||
sdkVersion: version,
|
||||
};
|
||||
53
js/src/errors/api-error.ts
Normal file
53
js/src/errors/api-error.ts
Normal file
@ -0,0 +1,53 @@
|
||||
export class ApiError extends Error {
|
||||
constructor(name: string, code: number, message: string) {
|
||||
super(`${name}: message=${message}, code=${code}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const errorCodes: { [key: string]: number } = {
|
||||
MethodValidationError: -32000,
|
||||
QueryRunNotFound: -32099,
|
||||
SqlStatementNotFound: -32100,
|
||||
TemporalError: -32150,
|
||||
QueryRunNotFinished: -32151,
|
||||
ResultTransformError: -32152,
|
||||
ResultFormatNotSupported: -32153,
|
||||
RowCountCouldNotBeComputed: -32154,
|
||||
QueryResultColumnMetadataMissing: -32155,
|
||||
InvalidSortColumn: -32156,
|
||||
ColumnSummaryQueryFailed: -32157,
|
||||
QueryResultColumnMetadataMissingColumnName: -32158,
|
||||
QueryResultColumnMetadataMissingColumnType: -32159,
|
||||
NoQueryRunsFoundinQueryText: -32160,
|
||||
DuckDBError: -32161,
|
||||
RefreshableQueryNotFound: -32162,
|
||||
AuthorizationError: -32163,
|
||||
DataSourceNotFound: -32164,
|
||||
QueryRunInvalidStateToCancel: -32165,
|
||||
DataProviderAlreadyExists: -32166,
|
||||
DataProviderNotFound: -32167,
|
||||
DataSourceAlreadyExists: -32168,
|
||||
AdminOnly: -32169,
|
||||
RequestedPageSizeTooLarge: -32170,
|
||||
MaxConcurrentQueries: -32171,
|
||||
};
|
||||
|
||||
export function getExceptionByErrorCode(errorCode?: number, message?: string): ApiError {
|
||||
if (!errorCode || !message) {
|
||||
return new ApiError("UnknownAPIError", errorCode || -1, message || "");
|
||||
}
|
||||
|
||||
let errorName: string | null = null;
|
||||
for (const key of Object.keys(errorCodes)) {
|
||||
if (errorCodes[key] === errorCode) {
|
||||
errorName = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorName === null) {
|
||||
return new ApiError("UnknownAPIError", errorCode, message);
|
||||
}
|
||||
|
||||
return new ApiError(errorName, errorCode, message);
|
||||
}
|
||||
@ -3,3 +3,4 @@ export * from "./server-errors";
|
||||
export * from "./sdk-errors";
|
||||
export * from "./query-run-errors";
|
||||
export * from "./user-errors";
|
||||
export * from "./api-error";
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
import { API } from "./api";
|
||||
import { Api } from "./api";
|
||||
import { QueryIntegration } from "./integrations";
|
||||
import { version } from '../package.json';
|
||||
|
||||
|
||||
const API_BASE_URL = "https://api.flipsidecrypto.com";
|
||||
const SDK_PACKAGE = "js";
|
||||
const SDK_VERSION = version;
|
||||
import { QueryResultSet } from "./types";
|
||||
import { DEFAULTS } from "./defaults";
|
||||
|
||||
export class Flipside {
|
||||
query: QueryIntegration;
|
||||
|
||||
constructor(apiKey: string, apiBaseUrl: string = API_BASE_URL) {
|
||||
constructor(apiKey: string, apiBaseUrl: string = DEFAULTS.apiBaseUrl) {
|
||||
// Setup API, which will be passed to integrations
|
||||
const api = new API(apiBaseUrl, SDK_PACKAGE, SDK_VERSION, apiKey);
|
||||
const api = new Api(apiBaseUrl, apiKey);
|
||||
|
||||
// declare integrations on Flipside client
|
||||
this.query = new QueryIntegration(api);
|
||||
@ -21,5 +17,5 @@ export class Flipside {
|
||||
|
||||
export * from "./types";
|
||||
export * from "./errors";
|
||||
import { QueryResultSet } from "./types";
|
||||
|
||||
export { QueryResultSet };
|
||||
|
||||
@ -1,195 +1,270 @@
|
||||
import {
|
||||
Query,
|
||||
QueryDefaults,
|
||||
QueryStatusFinished,
|
||||
QueryStatusError,
|
||||
QueryResultJson,
|
||||
CreateQueryJson,
|
||||
ApiClient,
|
||||
QueryResultSet,
|
||||
CreateQueryRunRpcParams,
|
||||
CreateQueryRunRpcResponse,
|
||||
mapApiQueryStateToStatus,
|
||||
GetQueryRunRpcResponse,
|
||||
Filter,
|
||||
SortBy,
|
||||
QueryRun,
|
||||
ResultFormat,
|
||||
SqlStatement,
|
||||
} from "../../types";
|
||||
import {
|
||||
expBackOff,
|
||||
getElapsedLinearSeconds,
|
||||
linearBackOff,
|
||||
} from "../../utils/sleep";
|
||||
import { getElapsedLinearSeconds, linearBackOff } from "../../utils/sleep";
|
||||
import {
|
||||
QueryRunExecutionError,
|
||||
QueryRunRateLimitError,
|
||||
QueryRunTimeoutError,
|
||||
ServerError,
|
||||
UserError,
|
||||
UnexpectedSDKError,
|
||||
getExceptionByErrorCode,
|
||||
} from "../../errors";
|
||||
import { QueryResultSetBuilder } from "./query-result-set-builder";
|
||||
|
||||
const DEFAULTS: QueryDefaults = {
|
||||
ttlMinutes: 60,
|
||||
cached: true,
|
||||
timeoutMinutes: 20,
|
||||
retryIntervalSeconds: 0.5,
|
||||
pageSize: 100000,
|
||||
pageNumber: 1,
|
||||
};
|
||||
import { Api } from "../../api";
|
||||
import { DEFAULTS } from "../../defaults";
|
||||
|
||||
export class QueryIntegration {
|
||||
#api: ApiClient;
|
||||
#defaults: QueryDefaults;
|
||||
#api: Api;
|
||||
|
||||
constructor(api: ApiClient, defaults: QueryDefaults = DEFAULTS) {
|
||||
constructor(api: Api) {
|
||||
this.#api = api;
|
||||
this.#defaults = defaults;
|
||||
}
|
||||
|
||||
#setQueryDefaults(query: Query): Query {
|
||||
return { ...this.#defaults, ...query };
|
||||
}
|
||||
|
||||
async run(query: Query): Promise<QueryResultSet> {
|
||||
query = this.#setQueryDefaults(query);
|
||||
let createQueryRunParams: CreateQueryRunRpcParams = {
|
||||
resultTTLHours: query.ttlMinutes ? Math.floor(query.ttlMinutes / 60) : DEFAULTS.ttlMinutes,
|
||||
sql: query.sql,
|
||||
maxAgeMinutes: query.maxAgeMinutes ? query.maxAgeMinutes : DEFAULTS.maxAgeMinutes,
|
||||
tags: {
|
||||
sdk_language: "javascript",
|
||||
sdk_package: query.sdkPackage ? query.sdkPackage : DEFAULTS.sdkPackage,
|
||||
sdk_version: query.sdkVersion ? query.sdkVersion : DEFAULTS.sdkVersion,
|
||||
},
|
||||
dataSource: query.dataSource ? query.dataSource : DEFAULTS.dataSource,
|
||||
dataProvider: query.dataProvider ? query.dataProvider : DEFAULTS.dataProvider,
|
||||
};
|
||||
|
||||
const [createQueryJson, createQueryErr] = await this.#createQuery(query);
|
||||
if (createQueryErr) {
|
||||
const createQueryRunRpcResponse = await this.#createQuery(createQueryRunParams);
|
||||
if (createQueryRunRpcResponse.error) {
|
||||
return new QueryResultSetBuilder({
|
||||
queryResultJson: null,
|
||||
error: createQueryErr,
|
||||
error: getExceptionByErrorCode(createQueryRunRpcResponse.error.code, createQueryRunRpcResponse.error.message),
|
||||
});
|
||||
}
|
||||
|
||||
if (!createQueryJson) {
|
||||
if (!createQueryRunRpcResponse.result?.queryRun) {
|
||||
return new QueryResultSetBuilder({
|
||||
queryResultJson: null,
|
||||
error: new UnexpectedSDKError(
|
||||
"expected a `createQueryJson` but got null"
|
||||
),
|
||||
error: new UnexpectedSDKError("expected a `createQueryRunRpcResponse.result.queryRun` but got null"),
|
||||
});
|
||||
}
|
||||
|
||||
const [getQueryResultJson, getQueryErr] = await this.#getQueryResult(
|
||||
createQueryJson.token,
|
||||
query.pageNumber || 1,
|
||||
query.pageSize || 100000,
|
||||
);
|
||||
// loop to get query state
|
||||
const [queryRunRpcResp, queryError] = await this.#getQueryRunInLoop({
|
||||
queryRunId: createQueryRunRpcResponse.result?.queryRun.id,
|
||||
});
|
||||
|
||||
if (getQueryErr) {
|
||||
if (queryError) {
|
||||
return new QueryResultSetBuilder({
|
||||
queryResultJson: null,
|
||||
error: getQueryErr,
|
||||
error: queryError,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!getQueryResultJson) {
|
||||
if (queryRunRpcResp && queryRunRpcResp.error) {
|
||||
return new QueryResultSetBuilder({
|
||||
queryResultJson: null,
|
||||
error: new UnexpectedSDKError(
|
||||
"expected a `getQueryResultJson` but got null"
|
||||
),
|
||||
error: getExceptionByErrorCode(queryRunRpcResp.error.code, queryRunRpcResp.error.message),
|
||||
});
|
||||
}
|
||||
|
||||
const queryRun = queryRunRpcResp?.result?.queryRun;
|
||||
if (!queryRun) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: new UnexpectedSDKError("expected a `queryRunRpcResp.result.queryRun` but got null"),
|
||||
});
|
||||
}
|
||||
|
||||
// get the query results
|
||||
const queryResultResp = await this.#api.getQueryResult({
|
||||
queryRunId: queryRun.id,
|
||||
format: ResultFormat.csv,
|
||||
page: {
|
||||
number: query.pageNumber || 1,
|
||||
size: query.pageSize || 100000,
|
||||
},
|
||||
});
|
||||
|
||||
if (queryResultResp && queryResultResp.error) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: getExceptionByErrorCode(queryResultResp.error.code, queryResultResp.error.message),
|
||||
});
|
||||
}
|
||||
|
||||
const queryResults = queryResultResp.result;
|
||||
if (!queryResults) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: new UnexpectedSDKError("expected a `queryResultResp.result` but got null"),
|
||||
});
|
||||
}
|
||||
|
||||
return new QueryResultSetBuilder({
|
||||
queryResultJson: getQueryResultJson,
|
||||
getQueryRunResultsRpcResult: queryResults,
|
||||
getQueryRunRpcResult: queryRunRpcResp.result,
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
|
||||
async #createQuery(
|
||||
query: Query,
|
||||
attempts: number = 0
|
||||
): Promise<
|
||||
[
|
||||
CreateQueryJson | null,
|
||||
QueryRunRateLimitError | ServerError | UserError | null
|
||||
]
|
||||
> {
|
||||
const resp = await this.#api.createQuery(query);
|
||||
if (resp.statusCode <= 299) {
|
||||
return [resp.data, null];
|
||||
async getQueryResults({
|
||||
queryRunId,
|
||||
pageNumber = DEFAULTS.pageNumber,
|
||||
pageSize = DEFAULTS.pageSize,
|
||||
filters,
|
||||
sortBy,
|
||||
}: {
|
||||
queryRunId: string;
|
||||
pageNumber?: number;
|
||||
pageSize?: number;
|
||||
filters?: Filter[];
|
||||
sortBy?: SortBy[];
|
||||
}): Promise<QueryResultSet> {
|
||||
const queryRunResp = await this.#api.getQueryRun({ queryRunId });
|
||||
if (queryRunResp.error) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: getExceptionByErrorCode(queryRunResp.error.code, queryRunResp.error.message),
|
||||
});
|
||||
}
|
||||
|
||||
if (resp.statusCode !== 429) {
|
||||
if (resp.statusCode >= 400 && resp.statusCode <= 499) {
|
||||
let errorMsg = resp.statusMsg || "user error";
|
||||
if (resp.errorMsg) {
|
||||
errorMsg = resp.errorMsg;
|
||||
}
|
||||
return [null, new UserError(resp.statusCode, errorMsg)];
|
||||
}
|
||||
return [
|
||||
null,
|
||||
new ServerError(resp.statusCode, resp.statusMsg || "server error"),
|
||||
];
|
||||
if (!queryRunResp.result) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: new UnexpectedSDKError("expected an `rpc_response.result` but got null"),
|
||||
});
|
||||
}
|
||||
|
||||
let shouldContinue = await expBackOff({
|
||||
attempts,
|
||||
timeoutMinutes: this.#defaults.timeoutMinutes,
|
||||
intervalSeconds: this.#defaults.retryIntervalSeconds,
|
||||
if (!queryRunResp.result?.queryRun) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: new UnexpectedSDKError("expected an `rpc_response.result.queryRun` but got null"),
|
||||
});
|
||||
}
|
||||
|
||||
const queryRun = queryRunResp.result.redirectedToQueryRun
|
||||
? queryRunResp.result.redirectedToQueryRun
|
||||
: queryRunResp.result.queryRun;
|
||||
|
||||
const queryResultResp = await this.#api.getQueryResult({
|
||||
queryRunId: queryRun.id,
|
||||
format: ResultFormat.csv,
|
||||
page: {
|
||||
number: pageNumber,
|
||||
size: pageSize,
|
||||
},
|
||||
filters,
|
||||
sortBy,
|
||||
});
|
||||
|
||||
if (!shouldContinue) {
|
||||
return [null, new QueryRunRateLimitError()];
|
||||
if (queryResultResp.error) {
|
||||
return new QueryResultSetBuilder({
|
||||
error: getExceptionByErrorCode(queryResultResp.error.code, queryResultResp.error.message),
|
||||
});
|
||||
}
|
||||
|
||||
return this.#createQuery(query, attempts + 1);
|
||||
return new QueryResultSetBuilder({
|
||||
getQueryRunResultsRpcResult: queryResultResp.result,
|
||||
getQueryRunRpcResult: queryRunResp.result,
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
|
||||
async #getQueryResult(
|
||||
queryID: string,
|
||||
pageNumber: number,
|
||||
pageSize: number,
|
||||
attempts: number = 0
|
||||
): Promise<
|
||||
[
|
||||
QueryResultJson | null,
|
||||
QueryRunTimeoutError | ServerError | UserError | null
|
||||
]
|
||||
async getQueryRun({ queryRunId }: { queryRunId: string }): Promise<QueryRun> {
|
||||
const resp = await this.#api.getQueryRun({ queryRunId });
|
||||
if (resp.error) {
|
||||
throw getExceptionByErrorCode(resp.error.code, resp.error.message);
|
||||
}
|
||||
if (!resp.result) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result` but got null");
|
||||
}
|
||||
|
||||
if (!resp.result?.queryRun) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result.queryRun` but got null");
|
||||
}
|
||||
|
||||
return resp.result.redirectedToQueryRun ? resp.result.redirectedToQueryRun : resp.result.queryRun;
|
||||
}
|
||||
|
||||
async getSqlStatement({ sqlStatementId }: { sqlStatementId: string }): Promise<SqlStatement> {
|
||||
const resp = await this.#api.getSqlStatement({ sqlStatementId });
|
||||
if (resp.error) {
|
||||
throw getExceptionByErrorCode(resp.error.code, resp.error.message);
|
||||
}
|
||||
if (!resp.result) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result` but got null");
|
||||
}
|
||||
|
||||
if (!resp.result?.sqlStatement) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result.sqlStatement` but got null");
|
||||
}
|
||||
return resp.result.sqlStatement;
|
||||
}
|
||||
|
||||
async cancelQueryRun({ queryRunId }: { queryRunId: string }): Promise<QueryRun> {
|
||||
const resp = await this.#api.cancelQueryRun({ queryRunId });
|
||||
if (resp.error) {
|
||||
throw getExceptionByErrorCode(resp.error.code, resp.error.message);
|
||||
}
|
||||
if (!resp.result) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result` but got null");
|
||||
}
|
||||
|
||||
if (!resp.result?.queryRun) {
|
||||
throw new UnexpectedSDKError("expected an `rpc_response.result.queryRun` but got null");
|
||||
}
|
||||
return resp.result.queryRun;
|
||||
}
|
||||
|
||||
async #createQuery(params: CreateQueryRunRpcParams, attempts: number = 0): Promise<CreateQueryRunRpcResponse> {
|
||||
return await this.#api.createQuery(params);
|
||||
}
|
||||
|
||||
async #getQueryRunInLoop({
|
||||
queryRunId,
|
||||
attempts = 0,
|
||||
}: {
|
||||
queryRunId: string;
|
||||
attempts?: number;
|
||||
}): Promise<
|
||||
[GetQueryRunRpcResponse | null, QueryRunTimeoutError | QueryRunExecutionError | ServerError | UserError | null]
|
||||
> {
|
||||
const resp = await this.#api.getQueryResult(queryID, pageNumber, pageSize);
|
||||
if (resp.statusCode > 299) {
|
||||
if (resp.statusCode >= 400 && resp.statusCode <= 499) {
|
||||
let errorMsg = resp.statusMsg || "user input error";
|
||||
if (resp.errorMsg) {
|
||||
errorMsg = resp.errorMsg;
|
||||
}
|
||||
return [null, new UserError(resp.statusCode, errorMsg)];
|
||||
}
|
||||
return [
|
||||
null,
|
||||
new ServerError(resp.statusCode, resp.statusMsg || "server error"),
|
||||
];
|
||||
let resp = await this.#api.getQueryRun({ queryRunId });
|
||||
if (resp.error) {
|
||||
}
|
||||
const queryRun = resp.result?.redirectedToQueryRun ? resp.result.redirectedToQueryRun : resp.result?.queryRun;
|
||||
if (!queryRun) {
|
||||
throw new Error("query run not found");
|
||||
}
|
||||
|
||||
if (!resp.data) {
|
||||
throw new Error(
|
||||
"valid status msg returned from server but no data exists in the response"
|
||||
);
|
||||
const queryRunState = mapApiQueryStateToStatus(queryRun.state);
|
||||
if (queryRunState === QueryStatusFinished) {
|
||||
return [resp, null];
|
||||
}
|
||||
|
||||
if (resp.data.status === QueryStatusFinished) {
|
||||
return [resp.data, null];
|
||||
}
|
||||
|
||||
if (resp.data.status === QueryStatusError) {
|
||||
if (queryRunState === QueryStatusError) {
|
||||
return [null, new QueryRunExecutionError()];
|
||||
}
|
||||
|
||||
let shouldContinue = await linearBackOff({
|
||||
attempts,
|
||||
timeoutMinutes: this.#defaults.timeoutMinutes,
|
||||
intervalSeconds: this.#defaults.retryIntervalSeconds,
|
||||
timeoutMinutes: DEFAULTS.timeoutMinutes,
|
||||
intervalSeconds: DEFAULTS.retryIntervalSeconds,
|
||||
});
|
||||
|
||||
if (!shouldContinue) {
|
||||
const elapsedSeconds = getElapsedLinearSeconds({
|
||||
attempts,
|
||||
timeoutMinutes: this.#defaults.timeoutMinutes,
|
||||
intervalSeconds: this.#defaults.retryIntervalSeconds,
|
||||
timeoutMinutes: DEFAULTS.timeoutMinutes,
|
||||
intervalSeconds: DEFAULTS.retryIntervalSeconds,
|
||||
});
|
||||
return [null, new QueryRunTimeoutError(elapsedSeconds * 60)];
|
||||
}
|
||||
|
||||
return this.#getQueryResult(queryID, pageNumber, pageSize, attempts + 1);
|
||||
return this.#getQueryRunInLoop({ queryRunId, attempts: attempts + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,27 +5,43 @@ import {
|
||||
ServerError,
|
||||
UserError,
|
||||
UnexpectedSDKError,
|
||||
ApiError,
|
||||
} from "../../errors";
|
||||
import {
|
||||
QueryResultJson,
|
||||
QueryResultSet,
|
||||
Row,
|
||||
QueryResultRecord,
|
||||
QueryRunStats,
|
||||
QueryStatus,
|
||||
GetQueryRunResultsRpcResult,
|
||||
GetQueryRunRpcResult,
|
||||
mapApiQueryStateToStatus,
|
||||
} from "../../types";
|
||||
import { QueryResultSetBuilderInput } from "../../types/query-result-set-input.type";
|
||||
|
||||
interface QueryResultSetBuilderData {
|
||||
getQueryRunResultsRpcResult?: GetQueryRunResultsRpcResult | null;
|
||||
getQueryRunRpcResult?: GetQueryRunRpcResult | null;
|
||||
error:
|
||||
| ApiError
|
||||
| QueryRunRateLimitError
|
||||
| QueryRunTimeoutError
|
||||
| QueryRunExecutionError
|
||||
| ServerError
|
||||
| UserError
|
||||
| UnexpectedSDKError
|
||||
| null;
|
||||
}
|
||||
|
||||
export class QueryResultSetBuilder implements QueryResultSet {
|
||||
queryId: string | null;
|
||||
status: QueryStatus | null;
|
||||
columns: string[] | null;
|
||||
columnTypes: string[] | null;
|
||||
rows: Row[] | null;
|
||||
rows: any[] | null;
|
||||
runStats: QueryRunStats | null;
|
||||
records: QueryResultRecord[] | null;
|
||||
|
||||
error:
|
||||
| ApiError
|
||||
| QueryRunRateLimitError
|
||||
| QueryRunTimeoutError
|
||||
| QueryRunExecutionError
|
||||
@ -34,10 +50,10 @@ export class QueryResultSetBuilder implements QueryResultSet {
|
||||
| UnexpectedSDKError
|
||||
| null;
|
||||
|
||||
constructor(data: QueryResultSetBuilderInput) {
|
||||
this.error = data.error;
|
||||
const queryResultJson = data.queryResultJson;
|
||||
if (!queryResultJson) {
|
||||
constructor({ getQueryRunResultsRpcResult, getQueryRunRpcResult, error }: QueryResultSetBuilderData) {
|
||||
this.error = error;
|
||||
|
||||
if (!getQueryRunResultsRpcResult || !getQueryRunRpcResult) {
|
||||
this.queryId = null;
|
||||
this.status = null;
|
||||
this.columns = null;
|
||||
@ -48,51 +64,63 @@ export class QueryResultSetBuilder implements QueryResultSet {
|
||||
return;
|
||||
}
|
||||
|
||||
this.queryId = queryResultJson.queryId;
|
||||
this.status = queryResultJson.status;
|
||||
this.columns = queryResultJson.columnLabels;
|
||||
this.columnTypes = queryResultJson.columnTypes;
|
||||
this.rows = queryResultJson.results;
|
||||
this.runStats = this.#computeRunStats(queryResultJson);
|
||||
this.records = this.#createRecords(queryResultJson);
|
||||
this.queryId = getQueryRunRpcResult.queryRun.id;
|
||||
this.status = mapApiQueryStateToStatus(getQueryRunRpcResult.queryRun.state);
|
||||
this.columns = getQueryRunResultsRpcResult.columnNames;
|
||||
this.columnTypes = getQueryRunResultsRpcResult.columnTypes;
|
||||
this.rows = getQueryRunResultsRpcResult.rows;
|
||||
this.runStats = this.#computeRunStats(getQueryRunRpcResult);
|
||||
this.records = this.#createRecords(getQueryRunResultsRpcResult);
|
||||
}
|
||||
|
||||
#computeRunStats(
|
||||
queryResultJson: QueryResultJson | null
|
||||
): QueryRunStats | null {
|
||||
if (!queryResultJson) {
|
||||
#createRecords(getQueryRunResultsRpcResult: GetQueryRunResultsRpcResult | null): QueryResultRecord[] | null {
|
||||
if (!getQueryRunResultsRpcResult || !getQueryRunResultsRpcResult.columnNames || !getQueryRunResultsRpcResult.rows) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let startedAt = new Date(queryResultJson.startedAt);
|
||||
let endedAt = new Date(queryResultJson.endedAt);
|
||||
let elapsedSeconds = (endedAt.getTime() - startedAt.getTime()) / 1000;
|
||||
return {
|
||||
startedAt,
|
||||
endedAt,
|
||||
elapsedSeconds,
|
||||
recordCount: queryResultJson.results.length,
|
||||
};
|
||||
}
|
||||
let columnNames = getQueryRunResultsRpcResult.columnNames;
|
||||
|
||||
#createRecords(
|
||||
queryResultJson: QueryResultJson | null
|
||||
): QueryResultRecord[] | null {
|
||||
if (!queryResultJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let columnLabels = queryResultJson.columnLabels;
|
||||
if (!columnLabels) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return queryResultJson.results.map((result) => {
|
||||
return getQueryRunResultsRpcResult.rows.map((row) => {
|
||||
let record: QueryResultRecord = {};
|
||||
result.forEach((value, index) => {
|
||||
record[columnLabels[index].toLowerCase()] = value;
|
||||
row.forEach((value: any, index: number) => {
|
||||
record[columnNames[index].toLowerCase()] = value;
|
||||
});
|
||||
return record;
|
||||
});
|
||||
}
|
||||
|
||||
#computeRunStats(getQueryRunRpcResult: GetQueryRunRpcResult): QueryRunStats {
|
||||
const queryRun = getQueryRunRpcResult.queryRun;
|
||||
|
||||
if (
|
||||
!queryRun.startedAt ||
|
||||
!queryRun.endedAt ||
|
||||
!queryRun.createdAt ||
|
||||
!queryRun.queryStreamingEndedAt ||
|
||||
!queryRun.queryRunningEndedAt
|
||||
) {
|
||||
throw new Error("Query has no data");
|
||||
}
|
||||
|
||||
const createdAt = new Date(queryRun.createdAt);
|
||||
const startTime = new Date(queryRun.startedAt);
|
||||
const endTime = new Date(queryRun.endedAt);
|
||||
const streamingEndTime = new Date(queryRun.queryStreamingEndedAt);
|
||||
const queryExecEndAt = new Date(queryRun.queryRunningEndedAt);
|
||||
|
||||
return {
|
||||
startedAt: startTime,
|
||||
endedAt: endTime,
|
||||
elapsedSeconds: (endTime.getTime() - startTime.getTime()) / 1000,
|
||||
queryExecStartedAt: startTime,
|
||||
queryExecEndedAt: queryExecEndAt,
|
||||
streamingStartedAt: queryExecEndAt,
|
||||
streamingEndedAt: streamingEndTime,
|
||||
queuedSeconds: (startTime.getTime() - createdAt.getTime()) / 1000,
|
||||
streamingSeconds: (streamingEndTime.getTime() - queryExecEndAt.getTime()) / 1000,
|
||||
queryExecSeconds: (queryExecEndAt.getTime() - startTime.getTime()) / 1000,
|
||||
bytes: queryRun.totalSize ? queryRun.totalSize : 0,
|
||||
recordCount: queryRun.rowCount ? queryRun.rowCount : 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { assert, describe, it } from "vitest";
|
||||
import { QueryResultSetBuilder } from "../integrations/query-integration/query-result-set-builder";
|
||||
import { QueryResultSetBuilder } from "../integrations/query-integration/query-result-set-builder-old";
|
||||
import {
|
||||
QueryResultSetBuilderInput,
|
||||
QueryStatus,
|
||||
@ -8,9 +8,7 @@ import {
|
||||
QueryStatusPending,
|
||||
} from "../types";
|
||||
|
||||
function getQueryResultSetBuilder(
|
||||
status: QueryStatus
|
||||
): QueryResultSetBuilderInput {
|
||||
function getQueryResultSetBuilder(status: QueryStatus): QueryResultSetBuilderInput {
|
||||
return {
|
||||
queryResultJson: {
|
||||
queryId: "test",
|
||||
@ -23,13 +21,7 @@ function getQueryResultSetBuilder(
|
||||
],
|
||||
startedAt: "2022-05-19T00:00:00Z",
|
||||
endedAt: "2022-05-19T00:01:30Z",
|
||||
columnLabels: [
|
||||
"block_id",
|
||||
"tx_id",
|
||||
"from_address",
|
||||
"succeeded",
|
||||
"amount",
|
||||
],
|
||||
columnLabels: ["block_id", "tx_id", "from_address", "succeeded", "amount"],
|
||||
columnTypes: ["number", "string", "string", "boolean", "number"],
|
||||
message: "",
|
||||
errors: null,
|
||||
@ -41,9 +33,7 @@ function getQueryResultSetBuilder(
|
||||
}
|
||||
|
||||
describe("runStats", () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusFinished)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusFinished));
|
||||
it("runStats startedAt is Date type", async () => {
|
||||
assert.typeOf(queryResultSet.runStats?.startedAt, "Date");
|
||||
});
|
||||
@ -62,9 +52,7 @@ describe("runStats", () => {
|
||||
});
|
||||
|
||||
describe("records", () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusFinished)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusFinished));
|
||||
it("records length = rows length", async () => {
|
||||
assert.equal(queryResultSet.records?.length, queryResultSet.rows?.length);
|
||||
});
|
||||
@ -95,15 +83,11 @@ describe("records", () => {
|
||||
cells.forEach((cellValue, colIndex) => {
|
||||
let columns = queryResultSet?.columns;
|
||||
if (!columns) {
|
||||
throw new Error(
|
||||
"QueryResultSetBuilder columns cannot be null for tests"
|
||||
);
|
||||
throw new Error("QueryResultSetBuilder columns cannot be null for tests");
|
||||
}
|
||||
let column = columns[colIndex];
|
||||
if (records === null) {
|
||||
throw new Error(
|
||||
"QueryResultSetBuilder records cannot be null for tests"
|
||||
);
|
||||
throw new Error("QueryResultSetBuilder records cannot be null for tests");
|
||||
}
|
||||
let record = records[rowIndex];
|
||||
let recordValue = record[column];
|
||||
@ -116,36 +100,26 @@ describe("records", () => {
|
||||
|
||||
describe("status", () => {
|
||||
it("isFinished", async () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusFinished)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusFinished));
|
||||
assert.equal(queryResultSet?.status, QueryStatusFinished);
|
||||
});
|
||||
it("isPending", async () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusPending)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusPending));
|
||||
assert.equal(queryResultSet?.status, QueryStatusPending);
|
||||
});
|
||||
it("isError", async () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusError)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusError));
|
||||
assert.equal(queryResultSet?.status, QueryStatusError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("queryID", () => {
|
||||
it("queryId is set", async () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusFinished)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusFinished));
|
||||
assert.notEqual(queryResultSet?.queryId, null);
|
||||
});
|
||||
it("queryId is test", async () => {
|
||||
const queryResultSet = new QueryResultSetBuilder(
|
||||
getQueryResultSetBuilder(QueryStatusFinished)
|
||||
);
|
||||
const queryResultSet = new QueryResultSetBuilder(getQueryResultSetBuilder(QueryStatusFinished));
|
||||
assert.equal(queryResultSet?.queryId, "test");
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { Query } from "../query.type";
|
||||
import { CreateQueryResp } from "./create-query-resp.type";
|
||||
import { QueryResultResp } from "./query-result-resp.type";
|
||||
|
||||
export interface ApiClient {
|
||||
getUrl(path: string): string;
|
||||
createQuery(query: Query): Promise<CreateQueryResp>;
|
||||
getQueryResult(queryID: string, pageNumber: number, pageSize: number): Promise<QueryResultResp>;
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
export interface ApiResponse {
|
||||
statusCode: number;
|
||||
statusMsg: string | null;
|
||||
errorMsg: string | null | undefined;
|
||||
data: Record<string, any> | null;
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { ApiResponse } from "./api-response.type";
|
||||
|
||||
export type CreateQueryJson = {
|
||||
token: string;
|
||||
errors?: string | null;
|
||||
};
|
||||
|
||||
export interface CreateQueryResp extends ApiResponse {
|
||||
data: CreateQueryJson | null;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export type ApiError = Error;
|
||||
@ -1,5 +0,0 @@
|
||||
export * from "./create-query-resp.type";
|
||||
export * from "./errors.type";
|
||||
export * from "./query-result-resp.type";
|
||||
export * from "./api-client.type";
|
||||
export * from "./api-response.type";
|
||||
@ -1,21 +0,0 @@
|
||||
import { QueryStatus } from "../query-status.type";
|
||||
import { ApiResponse } from "./api-response.type";
|
||||
|
||||
export type Row = (string | number | boolean | null)[];
|
||||
export type QueryResultJson = {
|
||||
queryId: string;
|
||||
status: QueryStatus;
|
||||
results: Row[];
|
||||
startedAt: string;
|
||||
endedAt: string;
|
||||
columnLabels: string[];
|
||||
columnTypes: string[];
|
||||
message?: string;
|
||||
errors?: string | null;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
export interface QueryResultResp extends ApiResponse {
|
||||
data: QueryResultJson | null;
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
// Export classes from core
|
||||
export { Page } from "./page";
|
||||
export { PageStats } from "./page-stats";
|
||||
export { QueryRun } from "./query-run";
|
||||
export { QueryRequest } from "./query-request";
|
||||
export { ResultFormat } from "./result-format";
|
||||
export { RpcRequest, BaseRpcRequest } from "./rpc-request";
|
||||
export { RpcResponse, BaseRpcResponse } from "./rpc-response";
|
||||
export { SqlStatement } from "./sql-statement";
|
||||
export { Tags } from "./tags";
|
||||
export { Page } from "./page.type";
|
||||
export { PageStats } from "./page-stats.type";
|
||||
export { QueryRun } from "./query-run.type";
|
||||
export { QueryRequest } from "./query-request.type";
|
||||
export { ResultFormat } from "./result-format.type";
|
||||
export { RpcRequest, BaseRpcRequest } from "./rpc-request.type";
|
||||
export { RpcResponse, BaseRpcResponse } from "./rpc-response.type";
|
||||
export { SqlStatement } from "./sql-statement.type";
|
||||
export { Tags } from "./tags.type";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Tags } from "./tags";
|
||||
import { Tags } from "./tags.type";
|
||||
|
||||
export interface QueryRequest {
|
||||
id: string;
|
||||
@ -1,4 +1,4 @@
|
||||
import { Tags } from "./tags";
|
||||
import { Tags } from "./tags.type";
|
||||
|
||||
export interface QueryRun {
|
||||
id: string;
|
||||
@ -1,4 +1,4 @@
|
||||
import { RpcError } from "./rpc-error";
|
||||
import { RpcError } from "./rpc-error.type";
|
||||
|
||||
export interface RpcResponse<T> {
|
||||
jsonrpc: string;
|
||||
@ -1,5 +1,5 @@
|
||||
import { ColumnMetadata } from "./column-metadata";
|
||||
import { Tags } from "./tags";
|
||||
import { ColumnMetadata } from "./column-metadata.type";
|
||||
import { Tags } from "./tags.type";
|
||||
|
||||
export interface SqlStatement {
|
||||
id: string;
|
||||
@ -1,6 +1,7 @@
|
||||
export * from "./cancel-query-run";
|
||||
export * from "./create-query-run";
|
||||
export * from "./get-query-run-results";
|
||||
export * from "./get-query-run";
|
||||
export * from "./get-sql-statement";
|
||||
export * from "./query-results";
|
||||
export * from "./cancel-query-run.type";
|
||||
export * from "./create-query-run.type";
|
||||
export * from "./get-query-run-results.type";
|
||||
export * from "./get-query-run.type";
|
||||
export * from "./get-sql-statement.type";
|
||||
export * from "./query-results.type";
|
||||
export * from "./core";
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
export * from "./query.type";
|
||||
export * from "./query-defaults.type";
|
||||
export * from "./sdk-defaults.type";
|
||||
export * from "./query-status.type";
|
||||
export * from "./query-result-set.type";
|
||||
export * from "./query-result-set-input.type";
|
||||
export * from "./query-run-stats.type";
|
||||
export * from "./query-result-record.type";
|
||||
export * from "./sleep-config.type";
|
||||
export * from "./api";
|
||||
export * from "./compass";
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
export type QueryDefaults = {
|
||||
ttlMinutes: number;
|
||||
cached: boolean;
|
||||
timeoutMinutes: number;
|
||||
retryIntervalSeconds: number;
|
||||
pageSize: number;
|
||||
pageNumber: number;
|
||||
};
|
||||
@ -1,21 +0,0 @@
|
||||
import {
|
||||
QueryRunExecutionError,
|
||||
QueryRunRateLimitError,
|
||||
QueryRunTimeoutError,
|
||||
ServerError,
|
||||
UserError,
|
||||
UnexpectedSDKError,
|
||||
} from "../errors";
|
||||
import { QueryResultJson } from "./api/query-result-resp.type";
|
||||
|
||||
export type QueryResultSetBuilderInput = {
|
||||
queryResultJson: QueryResultJson | null;
|
||||
error:
|
||||
| QueryRunExecutionError
|
||||
| QueryRunRateLimitError
|
||||
| QueryRunTimeoutError
|
||||
| ServerError
|
||||
| UserError
|
||||
| UnexpectedSDKError
|
||||
| null;
|
||||
};
|
||||
@ -1,4 +1,3 @@
|
||||
import { Row } from "./api";
|
||||
import {
|
||||
QueryRunExecutionError,
|
||||
QueryRunRateLimitError,
|
||||
@ -6,6 +5,7 @@ import {
|
||||
ServerError,
|
||||
UserError,
|
||||
UnexpectedSDKError,
|
||||
ApiError,
|
||||
} from "../errors";
|
||||
import { QueryRunStats } from "./query-run-stats.type";
|
||||
import { QueryStatus } from "./query-status.type";
|
||||
@ -25,7 +25,7 @@ export interface QueryResultSet {
|
||||
columnTypes: string[] | null;
|
||||
|
||||
// The results of the query
|
||||
rows: Row[] | null;
|
||||
rows: any[] | null;
|
||||
|
||||
// Summary stats on the query run (i.e. the number of rows returned, the elapsed time, etc)
|
||||
runStats: QueryRunStats | null;
|
||||
@ -35,6 +35,7 @@ export interface QueryResultSet {
|
||||
|
||||
// If the query failed, this will contain the error
|
||||
error:
|
||||
| ApiError
|
||||
| QueryRunRateLimitError
|
||||
| QueryRunTimeoutError
|
||||
| QueryRunExecutionError
|
||||
|
||||
@ -2,5 +2,13 @@ export type QueryRunStats = {
|
||||
startedAt: Date;
|
||||
endedAt: Date;
|
||||
elapsedSeconds: number;
|
||||
queryExecStartedAt: Date;
|
||||
queryExecEndedAt: Date;
|
||||
streamingStartedAt: Date;
|
||||
streamingEndedAt: Date;
|
||||
queuedSeconds: number;
|
||||
streamingSeconds: number;
|
||||
queryExecSeconds: number;
|
||||
bytes: number; // the number of bytes returned by the query
|
||||
recordCount: number;
|
||||
};
|
||||
|
||||
@ -2,3 +2,22 @@ export const QueryStatusFinished = "finished";
|
||||
export const QueryStatusPending = "pending";
|
||||
export const QueryStatusError = "error";
|
||||
export type QueryStatus = "finished" | "pending" | "error";
|
||||
|
||||
export function mapApiQueryStateToStatus(state: string): QueryStatus {
|
||||
switch (state) {
|
||||
case "QUERY_STATE_READY":
|
||||
return QueryStatusPending;
|
||||
case "QUERY_STATE_RUNNING":
|
||||
return QueryStatusPending;
|
||||
case "QUERY_STATE_STREAMING_RESULTS":
|
||||
return QueryStatusPending;
|
||||
case "QUERY_STATE_FAILED":
|
||||
return QueryStatusError;
|
||||
case "QUERY_STATE_CANCELED":
|
||||
return QueryStatusError;
|
||||
case "QUERY_STATE_SUCCESS":
|
||||
return QueryStatusFinished;
|
||||
default:
|
||||
throw new Error(`Unknown query state: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,26 @@
|
||||
export type Query = {
|
||||
// SQL query to execute
|
||||
sql: string;
|
||||
// the maximum age of the query results in minutes you will accept, defaults to zero
|
||||
maxAgeMinutes?: number;
|
||||
// The number of minutes to cache the query results
|
||||
ttlMinutes?: number;
|
||||
// An override on the cahce. A value of true will reexecute the query.
|
||||
// An override on the cache. A value of true will reexecute the query.
|
||||
cached?: boolean;
|
||||
// The number of minutes until your query time out
|
||||
// The number of minutes until your query times out
|
||||
timeoutMinutes?: number;
|
||||
// The number of records to return
|
||||
pageSize?: number;
|
||||
// The page number to return
|
||||
pageNumber?: number;
|
||||
// The number of seconds to use between retries
|
||||
retryIntervalSeconds?: number | string;
|
||||
// The SDK package used for the query
|
||||
sdkPackage?: string;
|
||||
// The SDK version used for the query
|
||||
sdkVersion?: string;
|
||||
// The data source to execute the query against
|
||||
dataSource?: string;
|
||||
// The owner of the data source
|
||||
dataProvider?: string;
|
||||
};
|
||||
|
||||
14
js/src/types/sdk-defaults.type.ts
Normal file
14
js/src/types/sdk-defaults.type.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export type SdkDefaults = {
|
||||
apiBaseUrl: string;
|
||||
ttlMinutes: number;
|
||||
maxAgeMinutes: number;
|
||||
dataSource: string;
|
||||
dataProvider: string;
|
||||
cached: boolean;
|
||||
timeoutMinutes: number;
|
||||
retryIntervalSeconds: number;
|
||||
pageSize: number;
|
||||
pageNumber: number;
|
||||
sdkPackage: string;
|
||||
sdkVersion: string;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user