From 7c0ceb89af1b97b27db4ddbfde71a386c4288d64 Mon Sep 17 00:00:00 2001 From: Jim Myers Date: Mon, 15 May 2023 13:11:27 -0400 Subject: [PATCH] Refactor query integration to new api and query result set builder --- js/src/api-old.ts | 103 ------ js/src/api.ts | 28 +- js/src/defaults.ts | 17 + js/src/errors/api-error.ts | 53 +++ js/src/errors/index.ts | 1 + js/src/flipside.ts | 16 +- .../integrations/query-integration/index.ts | 315 +++++++++++------- .../query-result-set-builder.ts | 116 ++++--- js/src/tests/query-result-set-builder.test.ts | 50 +-- js/src/types/api/api-client.type.ts | 9 - js/src/types/api/api-response.type.ts | 6 - js/src/types/api/create-query-resp.type.ts | 10 - js/src/types/api/errors.type.ts | 1 - js/src/types/api/index.ts | 5 - js/src/types/api/query-result-resp.type.ts | 21 -- ...-query-run.ts => cancel-query-run.type.ts} | 0 ...mn-metadata.ts => column-metadata.type.ts} | 0 js/src/types/compass/core/index.ts | 18 +- .../{page-stats.ts => page-stats.type.ts} | 0 .../compass/core/{page.ts => page.type.ts} | 0 ...query-request.ts => query-request.type.ts} | 2 +- .../core/{query-run.ts => query-run.type.ts} | 2 +- ...result-format.ts => result-format.type.ts} | 0 .../core/{rpc-error.ts => rpc-error.type.ts} | 0 .../{rpc-request.ts => rpc-request.type.ts} | 0 .../{rpc-response.ts => rpc-response.type.ts} | 2 +- ...sql-statement.ts => sql-statement.type.ts} | 4 +- .../compass/core/{tags.ts => tags.type.ts} | 0 ...-query-run.ts => create-query-run.type.ts} | 0 ...sults.ts => get-query-run-results.type.ts} | 0 ...get-query-run.ts => get-query-run.type.ts} | 0 ...statement.ts => get-sql-statement.type.ts} | 0 js/src/types/compass/index.ts | 13 +- ...query-results.ts => query-results.type.ts} | 0 js/src/types/index.ts | 4 +- js/src/types/query-defaults.type.ts | 8 - js/src/types/query-result-set-input.type.ts | 21 -- js/src/types/query-result-set.type.ts | 5 +- js/src/types/query-run-stats.type.ts | 8 + js/src/types/query-status.type.ts | 19 ++ js/src/types/query.type.ts | 16 +- js/src/types/sdk-defaults.type.ts | 14 + 42 files changed, 438 insertions(+), 449 deletions(-) delete mode 100644 js/src/api-old.ts create mode 100644 js/src/defaults.ts create mode 100644 js/src/errors/api-error.ts delete mode 100644 js/src/types/api/api-client.type.ts delete mode 100644 js/src/types/api/api-response.type.ts delete mode 100644 js/src/types/api/create-query-resp.type.ts delete mode 100644 js/src/types/api/errors.type.ts delete mode 100644 js/src/types/api/index.ts delete mode 100644 js/src/types/api/query-result-resp.type.ts rename js/src/types/compass/{cancel-query-run.ts => cancel-query-run.type.ts} (100%) rename js/src/types/compass/core/{column-metadata.ts => column-metadata.type.ts} (100%) rename js/src/types/compass/core/{page-stats.ts => page-stats.type.ts} (100%) rename js/src/types/compass/core/{page.ts => page.type.ts} (100%) rename js/src/types/compass/core/{query-request.ts => query-request.type.ts} (88%) rename js/src/types/compass/core/{query-run.ts => query-run.type.ts} (95%) rename js/src/types/compass/core/{result-format.ts => result-format.type.ts} (100%) rename js/src/types/compass/core/{rpc-error.ts => rpc-error.type.ts} (100%) rename js/src/types/compass/core/{rpc-request.ts => rpc-request.type.ts} (100%) rename js/src/types/compass/core/{rpc-response.ts => rpc-response.type.ts} (90%) rename js/src/types/compass/core/{sql-statement.ts => sql-statement.type.ts} (68%) rename js/src/types/compass/core/{tags.ts => tags.type.ts} (100%) rename js/src/types/compass/{create-query-run.ts => create-query-run.type.ts} (100%) rename js/src/types/compass/{get-query-run-results.ts => get-query-run-results.type.ts} (100%) rename js/src/types/compass/{get-query-run.ts => get-query-run.type.ts} (100%) rename js/src/types/compass/{get-sql-statement.ts => get-sql-statement.type.ts} (100%) rename js/src/types/compass/{query-results.ts => query-results.type.ts} (100%) delete mode 100644 js/src/types/query-defaults.type.ts delete mode 100644 js/src/types/query-result-set-input.type.ts create mode 100644 js/src/types/sdk-defaults.type.ts diff --git a/js/src/api-old.ts b/js/src/api-old.ts deleted file mode 100644 index 7b657ca..0000000 --- a/js/src/api-old.ts +++ /dev/null @@ -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; - #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 { - 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 { - 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, - }; - } -} diff --git a/js/src/api.ts b/js/src/api.ts index 373e4b3..c871514 100644 --- a/js/src/api.ts +++ b/js/src/api.ts @@ -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; - #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 { diff --git a/js/src/defaults.ts b/js/src/defaults.ts new file mode 100644 index 0000000..b3bb2d0 --- /dev/null +++ b/js/src/defaults.ts @@ -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, +}; diff --git a/js/src/errors/api-error.ts b/js/src/errors/api-error.ts new file mode 100644 index 0000000..89754f1 --- /dev/null +++ b/js/src/errors/api-error.ts @@ -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); +} diff --git a/js/src/errors/index.ts b/js/src/errors/index.ts index 0170206..44a5396 100644 --- a/js/src/errors/index.ts +++ b/js/src/errors/index.ts @@ -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"; diff --git a/js/src/flipside.ts b/js/src/flipside.ts index a5fddfb..85c714a 100644 --- a/js/src/flipside.ts +++ b/js/src/flipside.ts @@ -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 }; diff --git a/js/src/integrations/query-integration/index.ts b/js/src/integrations/query-integration/index.ts index e4bec18..4df3fd2 100644 --- a/js/src/integrations/query-integration/index.ts +++ b/js/src/integrations/query-integration/index.ts @@ -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 { - 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 { + 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 { + 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 { + 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 { + 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 { + 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 }); } } diff --git a/js/src/integrations/query-integration/query-result-set-builder.ts b/js/src/integrations/query-integration/query-result-set-builder.ts index 86950a7..d57e46a 100644 --- a/js/src/integrations/query-integration/query-result-set-builder.ts +++ b/js/src/integrations/query-integration/query-result-set-builder.ts @@ -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, + }; + } } diff --git a/js/src/tests/query-result-set-builder.test.ts b/js/src/tests/query-result-set-builder.test.ts index f863132..984e230 100644 --- a/js/src/tests/query-result-set-builder.test.ts +++ b/js/src/tests/query-result-set-builder.test.ts @@ -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"); }); }); diff --git a/js/src/types/api/api-client.type.ts b/js/src/types/api/api-client.type.ts deleted file mode 100644 index 80e6287..0000000 --- a/js/src/types/api/api-client.type.ts +++ /dev/null @@ -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; - getQueryResult(queryID: string, pageNumber: number, pageSize: number): Promise; -} diff --git a/js/src/types/api/api-response.type.ts b/js/src/types/api/api-response.type.ts deleted file mode 100644 index 963ad06..0000000 --- a/js/src/types/api/api-response.type.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ApiResponse { - statusCode: number; - statusMsg: string | null; - errorMsg: string | null | undefined; - data: Record | null; -} diff --git a/js/src/types/api/create-query-resp.type.ts b/js/src/types/api/create-query-resp.type.ts deleted file mode 100644 index a69082e..0000000 --- a/js/src/types/api/create-query-resp.type.ts +++ /dev/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; -} diff --git a/js/src/types/api/errors.type.ts b/js/src/types/api/errors.type.ts deleted file mode 100644 index 49d3b67..0000000 --- a/js/src/types/api/errors.type.ts +++ /dev/null @@ -1 +0,0 @@ -export type ApiError = Error; diff --git a/js/src/types/api/index.ts b/js/src/types/api/index.ts deleted file mode 100644 index b86f6b9..0000000 --- a/js/src/types/api/index.ts +++ /dev/null @@ -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"; diff --git a/js/src/types/api/query-result-resp.type.ts b/js/src/types/api/query-result-resp.type.ts deleted file mode 100644 index 70c088b..0000000 --- a/js/src/types/api/query-result-resp.type.ts +++ /dev/null @@ -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; -} diff --git a/js/src/types/compass/cancel-query-run.ts b/js/src/types/compass/cancel-query-run.type.ts similarity index 100% rename from js/src/types/compass/cancel-query-run.ts rename to js/src/types/compass/cancel-query-run.type.ts diff --git a/js/src/types/compass/core/column-metadata.ts b/js/src/types/compass/core/column-metadata.type.ts similarity index 100% rename from js/src/types/compass/core/column-metadata.ts rename to js/src/types/compass/core/column-metadata.type.ts diff --git a/js/src/types/compass/core/index.ts b/js/src/types/compass/core/index.ts index 22194e5..796f867 100644 --- a/js/src/types/compass/core/index.ts +++ b/js/src/types/compass/core/index.ts @@ -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"; diff --git a/js/src/types/compass/core/page-stats.ts b/js/src/types/compass/core/page-stats.type.ts similarity index 100% rename from js/src/types/compass/core/page-stats.ts rename to js/src/types/compass/core/page-stats.type.ts diff --git a/js/src/types/compass/core/page.ts b/js/src/types/compass/core/page.type.ts similarity index 100% rename from js/src/types/compass/core/page.ts rename to js/src/types/compass/core/page.type.ts diff --git a/js/src/types/compass/core/query-request.ts b/js/src/types/compass/core/query-request.type.ts similarity index 88% rename from js/src/types/compass/core/query-request.ts rename to js/src/types/compass/core/query-request.type.ts index 866193f..db9a6d0 100644 --- a/js/src/types/compass/core/query-request.ts +++ b/js/src/types/compass/core/query-request.type.ts @@ -1,4 +1,4 @@ -import { Tags } from "./tags"; +import { Tags } from "./tags.type"; export interface QueryRequest { id: string; diff --git a/js/src/types/compass/core/query-run.ts b/js/src/types/compass/core/query-run.type.ts similarity index 95% rename from js/src/types/compass/core/query-run.ts rename to js/src/types/compass/core/query-run.type.ts index def525e..06ad88b 100644 --- a/js/src/types/compass/core/query-run.ts +++ b/js/src/types/compass/core/query-run.type.ts @@ -1,4 +1,4 @@ -import { Tags } from "./tags"; +import { Tags } from "./tags.type"; export interface QueryRun { id: string; diff --git a/js/src/types/compass/core/result-format.ts b/js/src/types/compass/core/result-format.type.ts similarity index 100% rename from js/src/types/compass/core/result-format.ts rename to js/src/types/compass/core/result-format.type.ts diff --git a/js/src/types/compass/core/rpc-error.ts b/js/src/types/compass/core/rpc-error.type.ts similarity index 100% rename from js/src/types/compass/core/rpc-error.ts rename to js/src/types/compass/core/rpc-error.type.ts diff --git a/js/src/types/compass/core/rpc-request.ts b/js/src/types/compass/core/rpc-request.type.ts similarity index 100% rename from js/src/types/compass/core/rpc-request.ts rename to js/src/types/compass/core/rpc-request.type.ts diff --git a/js/src/types/compass/core/rpc-response.ts b/js/src/types/compass/core/rpc-response.type.ts similarity index 90% rename from js/src/types/compass/core/rpc-response.ts rename to js/src/types/compass/core/rpc-response.type.ts index 163266b..8907361 100644 --- a/js/src/types/compass/core/rpc-response.ts +++ b/js/src/types/compass/core/rpc-response.type.ts @@ -1,4 +1,4 @@ -import { RpcError } from "./rpc-error"; +import { RpcError } from "./rpc-error.type"; export interface RpcResponse { jsonrpc: string; diff --git a/js/src/types/compass/core/sql-statement.ts b/js/src/types/compass/core/sql-statement.type.ts similarity index 68% rename from js/src/types/compass/core/sql-statement.ts rename to js/src/types/compass/core/sql-statement.type.ts index 8a3c6ed..9600774 100644 --- a/js/src/types/compass/core/sql-statement.ts +++ b/js/src/types/compass/core/sql-statement.type.ts @@ -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; diff --git a/js/src/types/compass/core/tags.ts b/js/src/types/compass/core/tags.type.ts similarity index 100% rename from js/src/types/compass/core/tags.ts rename to js/src/types/compass/core/tags.type.ts diff --git a/js/src/types/compass/create-query-run.ts b/js/src/types/compass/create-query-run.type.ts similarity index 100% rename from js/src/types/compass/create-query-run.ts rename to js/src/types/compass/create-query-run.type.ts diff --git a/js/src/types/compass/get-query-run-results.ts b/js/src/types/compass/get-query-run-results.type.ts similarity index 100% rename from js/src/types/compass/get-query-run-results.ts rename to js/src/types/compass/get-query-run-results.type.ts diff --git a/js/src/types/compass/get-query-run.ts b/js/src/types/compass/get-query-run.type.ts similarity index 100% rename from js/src/types/compass/get-query-run.ts rename to js/src/types/compass/get-query-run.type.ts diff --git a/js/src/types/compass/get-sql-statement.ts b/js/src/types/compass/get-sql-statement.type.ts similarity index 100% rename from js/src/types/compass/get-sql-statement.ts rename to js/src/types/compass/get-sql-statement.type.ts diff --git a/js/src/types/compass/index.ts b/js/src/types/compass/index.ts index ebc5009..6f0af0b 100644 --- a/js/src/types/compass/index.ts +++ b/js/src/types/compass/index.ts @@ -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"; diff --git a/js/src/types/compass/query-results.ts b/js/src/types/compass/query-results.type.ts similarity index 100% rename from js/src/types/compass/query-results.ts rename to js/src/types/compass/query-results.type.ts diff --git a/js/src/types/index.ts b/js/src/types/index.ts index 0ae0f38..cae2d99 100644 --- a/js/src/types/index.ts +++ b/js/src/types/index.ts @@ -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"; diff --git a/js/src/types/query-defaults.type.ts b/js/src/types/query-defaults.type.ts deleted file mode 100644 index 631aa46..0000000 --- a/js/src/types/query-defaults.type.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type QueryDefaults = { - ttlMinutes: number; - cached: boolean; - timeoutMinutes: number; - retryIntervalSeconds: number; - pageSize: number; - pageNumber: number; -}; diff --git a/js/src/types/query-result-set-input.type.ts b/js/src/types/query-result-set-input.type.ts deleted file mode 100644 index bfbe58d..0000000 --- a/js/src/types/query-result-set-input.type.ts +++ /dev/null @@ -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; -}; diff --git a/js/src/types/query-result-set.type.ts b/js/src/types/query-result-set.type.ts index 8efc2a5..2f9bacd 100644 --- a/js/src/types/query-result-set.type.ts +++ b/js/src/types/query-result-set.type.ts @@ -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 diff --git a/js/src/types/query-run-stats.type.ts b/js/src/types/query-run-stats.type.ts index 9836c1d..9e9b1fc 100644 --- a/js/src/types/query-run-stats.type.ts +++ b/js/src/types/query-run-stats.type.ts @@ -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; }; diff --git a/js/src/types/query-status.type.ts b/js/src/types/query-status.type.ts index 817c728..ed762c1 100644 --- a/js/src/types/query-status.type.ts +++ b/js/src/types/query-status.type.ts @@ -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}`); + } +} diff --git a/js/src/types/query.type.ts b/js/src/types/query.type.ts index c663a8f..db93fd3 100644 --- a/js/src/types/query.type.ts +++ b/js/src/types/query.type.ts @@ -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; }; diff --git a/js/src/types/sdk-defaults.type.ts b/js/src/types/sdk-defaults.type.ts new file mode 100644 index 0000000..39b62db --- /dev/null +++ b/js/src/types/sdk-defaults.type.ts @@ -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; +};