From 5e04d2c6b59d60649a6c82bac581da5aab58ef18 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 11:38:52 +0300 Subject: [PATCH 01/51] rename to ts --- .../cubejs-client-core/src/{HttpTransport.js => HttpTransport.ts} | 0 packages/cubejs-client-core/src/{Meta.js => Meta.ts} | 0 .../src/{ProgressResult.js => ProgressResult.ts} | 0 .../cubejs-client-core/src/{RequestError.js => RequestError.ts} | 0 packages/cubejs-client-core/src/{ResultSet.js => ResultSet.ts} | 0 packages/cubejs-client-core/src/{SqlQuery.js => SqlQuery.ts} | 0 packages/cubejs-client-core/src/{index.js => index.ts} | 0 packages/cubejs-client-core/src/{index.umd.js => index.umd.ts} | 0 packages/cubejs-client-core/src/{time.js => time.ts} | 0 packages/cubejs-client-core/src/{utils.js => utils.ts} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename packages/cubejs-client-core/src/{HttpTransport.js => HttpTransport.ts} (100%) rename packages/cubejs-client-core/src/{Meta.js => Meta.ts} (100%) rename packages/cubejs-client-core/src/{ProgressResult.js => ProgressResult.ts} (100%) rename packages/cubejs-client-core/src/{RequestError.js => RequestError.ts} (100%) rename packages/cubejs-client-core/src/{ResultSet.js => ResultSet.ts} (100%) rename packages/cubejs-client-core/src/{SqlQuery.js => SqlQuery.ts} (100%) rename packages/cubejs-client-core/src/{index.js => index.ts} (100%) rename packages/cubejs-client-core/src/{index.umd.js => index.umd.ts} (100%) rename packages/cubejs-client-core/src/{time.js => time.ts} (100%) rename packages/cubejs-client-core/src/{utils.js => utils.ts} (100%) diff --git a/packages/cubejs-client-core/src/HttpTransport.js b/packages/cubejs-client-core/src/HttpTransport.ts similarity index 100% rename from packages/cubejs-client-core/src/HttpTransport.js rename to packages/cubejs-client-core/src/HttpTransport.ts diff --git a/packages/cubejs-client-core/src/Meta.js b/packages/cubejs-client-core/src/Meta.ts similarity index 100% rename from packages/cubejs-client-core/src/Meta.js rename to packages/cubejs-client-core/src/Meta.ts diff --git a/packages/cubejs-client-core/src/ProgressResult.js b/packages/cubejs-client-core/src/ProgressResult.ts similarity index 100% rename from packages/cubejs-client-core/src/ProgressResult.js rename to packages/cubejs-client-core/src/ProgressResult.ts diff --git a/packages/cubejs-client-core/src/RequestError.js b/packages/cubejs-client-core/src/RequestError.ts similarity index 100% rename from packages/cubejs-client-core/src/RequestError.js rename to packages/cubejs-client-core/src/RequestError.ts diff --git a/packages/cubejs-client-core/src/ResultSet.js b/packages/cubejs-client-core/src/ResultSet.ts similarity index 100% rename from packages/cubejs-client-core/src/ResultSet.js rename to packages/cubejs-client-core/src/ResultSet.ts diff --git a/packages/cubejs-client-core/src/SqlQuery.js b/packages/cubejs-client-core/src/SqlQuery.ts similarity index 100% rename from packages/cubejs-client-core/src/SqlQuery.js rename to packages/cubejs-client-core/src/SqlQuery.ts diff --git a/packages/cubejs-client-core/src/index.js b/packages/cubejs-client-core/src/index.ts similarity index 100% rename from packages/cubejs-client-core/src/index.js rename to packages/cubejs-client-core/src/index.ts diff --git a/packages/cubejs-client-core/src/index.umd.js b/packages/cubejs-client-core/src/index.umd.ts similarity index 100% rename from packages/cubejs-client-core/src/index.umd.js rename to packages/cubejs-client-core/src/index.umd.ts diff --git a/packages/cubejs-client-core/src/time.js b/packages/cubejs-client-core/src/time.ts similarity index 100% rename from packages/cubejs-client-core/src/time.js rename to packages/cubejs-client-core/src/time.ts diff --git a/packages/cubejs-client-core/src/utils.js b/packages/cubejs-client-core/src/utils.ts similarity index 100% rename from packages/cubejs-client-core/src/utils.js rename to packages/cubejs-client-core/src/utils.ts From 0c2d5348484c31f84056db78c7f097817329528f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 12:02:26 +0300 Subject: [PATCH 02/51] Move to TS whole codebase --- packages/cubejs-client-core/index.d.ts | 1354 ----------------- .../cubejs-client-core/src/HttpTransport.ts | 70 +- packages/cubejs-client-core/src/Meta.ts | 157 +- .../cubejs-client-core/src/ProgressResult.ts | 10 +- .../cubejs-client-core/src/RequestError.ts | 6 +- packages/cubejs-client-core/src/ResultSet.ts | 763 ++++++++-- packages/cubejs-client-core/src/SqlQuery.ts | 22 +- packages/cubejs-client-core/src/index.ts | 460 ++++-- packages/cubejs-client-core/src/index.umd.ts | 9 +- packages/cubejs-client-core/src/time.ts | 70 +- packages/cubejs-client-core/src/types.ts | 514 +++++++ packages/cubejs-client-core/src/utils.ts | 169 +- 12 files changed, 1846 insertions(+), 1758 deletions(-) create mode 100644 packages/cubejs-client-core/src/types.ts diff --git a/packages/cubejs-client-core/index.d.ts b/packages/cubejs-client-core/index.d.ts index cd176f21f7837..e69de29bb2d1d 100644 --- a/packages/cubejs-client-core/index.d.ts +++ b/packages/cubejs-client-core/index.d.ts @@ -1,1354 +0,0 @@ -/** - * @title @cubejs-client/core - * @permalink /@cubejs-client-core - * @menuCategory Reference - * @subcategory Frontend - * @menuOrder 2 - * @description Vanilla JavaScript Cube.js client. - */ - -declare module '@cubejs-client/core' { - export type TransportOptions = { - /** - * [jwt auth token](security) - */ - authorization: string; - /** - * path to `/cubejs-api/v1` - */ - apiUrl: string; - /** - * custom headers - */ - headers?: Record; - credentials?: 'omit' | 'same-origin' | 'include'; - method?: 'GET' | 'PUT' | 'POST' | 'PATCH'; - /** - * Fetch timeout in milliseconds. Would be passed as AbortSignal.timeout() - */ - fetchTimeout?: number; - /** - * AbortSignal to cancel requests - */ - signal?: AbortSignal; - }; - - export interface ITransportResponse { - subscribe: (cb: (result: R, resubscribe: () => Promise) => CBResult) => Promise; - // Optional, supported in WebSocketTransport - unsubscribe?: () => Promise; - } - - export interface ITransport { - request(method: string, params: Record): ITransportResponse; - } - - /** - * Default transport implementation. - * @order 3 - */ - export class HttpTransport implements ITransport { - /** - * @hidden - */ - protected authorization: TransportOptions['authorization']; - /** - * @hidden - */ - protected apiUrl: TransportOptions['apiUrl']; - /** - * @hidden - */ - protected method: TransportOptions['method']; - /** - * @hidden - */ - protected headers: TransportOptions['headers']; - /** - * @hidden - */ - protected credentials: TransportOptions['credentials']; - /** - * @hidden - */ - protected signal?: TransportOptions['signal']; - - constructor(options: TransportOptions); - - public request(method: string, params: any): ITransportResponse; - } - - export type CubeApiOptions = { - /** - * URL of your Cube.js Backend. By default, in the development environment it is `http://localhost:4000/cubejs-api/v1` - */ - apiUrl: string; - /** - * Transport implementation to use. [HttpTransport](#http-transport) will be used by default. - */ - transport?: ITransport; - headers?: Record; - pollInterval?: number; - credentials?: 'omit' | 'same-origin' | 'include'; - parseDateMeasures?: boolean; - resType?: 'default' | 'compact'; - castNumerics?: boolean; - /** - * How many network errors would be retried before returning to users. Default to 0. - */ - networkErrorRetries?: number; - /** - * AbortSignal to cancel requests - */ - signal?: AbortSignal; - }; - - export type LoadMethodOptions = { - /** - * Key to store the current request's MUTEX inside the `mutexObj`. MUTEX object is used to reject orphaned queries results when new queries are sent. For example: if two queries are sent with the same `mutexKey` only the last one will return results. - */ - mutexKey?: string; - /** - * Object to store MUTEX - */ - mutexObj?: Object; - /** - * Pass `true` to use continuous fetch behavior. - */ - subscribe?: boolean; - /** - * A Cube API instance. If not provided will be taken from `CubeProvider` - */ - cubeApi?: CubeApi; - /** - * If enabled, all members of the 'number' type will be automatically converted to numerical values on the client side - */ - castNumerics?: boolean; - /** - * Function that receives `ProgressResult` on each `Continue wait` message. - */ - progressCallback?(result: ProgressResult): void; - /** - * AbortSignal to cancel the request - */ - signal?: AbortSignal; - }; - - export type LoadMethodCallback = (error: Error | null, resultSet: T) => void; - - export type QueryOrder = 'asc' | 'desc'; - - export type TQueryOrderObject = { [key: string]: QueryOrder }; - export type TQueryOrderArray = Array<[string, QueryOrder]>; - - export type Annotation = { - title: string; - shortTitle: string; - type: string; - format?: 'currency' | 'percent' | 'number'; - }; - - export type QueryAnnotations = { - dimensions: Record; - measures: Record; - timeDimensions: Record; - }; - - type PivotQuery = Query & { - queryType: QueryType; - }; - - type QueryType = 'regularQuery' | 'compareDateRangeQuery' | 'blendingQuery'; - - type LeafMeasure = { - measure: string; - additive: boolean; - type: 'count' | 'countDistinct' | 'sum' | 'min' | 'max' | 'number' | 'countDistinctApprox' - }; - - export type TransformedQuery = { - allFiltersWithinSelectedDimensions: boolean; - granularityHierarchies: Record; - hasMultipliedMeasures: boolean; - hasNoTimeDimensionsWithoutGranularity: boolean; - isAdditive: boolean; - leafMeasureAdditive: boolean; - leafMeasures: string[]; - measures: string[]; - sortedDimensions: string[]; - sortedTimeDimensions: [[string, string]]; - measureToLeafMeasures?: Record; - ownedDimensions: string[]; - ownedTimeDimensionsAsIs: [[string, string | null]]; - ownedTimeDimensionsWithRollupGranularity: [[string, string]]; - }; - - export type PreAggregationType = 'rollup' | 'rollupJoin' | 'rollupLambda' | 'originalSql'; - - type UsedPreAggregation = { - targetTableName: string; - type: PreAggregationType; - }; - - type LoadResponseResult = { - annotation: QueryAnnotations; - lastRefreshTime: string; - query: Query; - data: T[]; - external: boolean | null; - dbType: string; - extDbType: string; - requestId?: string; - usedPreAggregations?: Record; - transformedQuery?: TransformedQuery; - total?: number - }; - - export type LoadResponse = { - queryType: QueryType; - results: LoadResponseResult[]; - pivotQuery: PivotQuery; - [key: string]: any; - }; - - /** - * Configuration object that contains information about pivot axes and other options. - * - * Let's apply `pivotConfig` and see how it affects the axes - * ```js - * // Example query - * { - * measures: ['Orders.count'], - * dimensions: ['Users.country', 'Users.gender'] - * } - * ``` - * If we put the `Users.gender` dimension on **y** axis - * ```js - * resultSet.tablePivot({ - * x: ['Users.country'], - * y: ['Users.gender', 'measures'] - * }) - * ``` - * - * The resulting table will look the following way - * - * | Users Country | male, Orders.count | female, Orders.count | - * | ------------- | ------------------ | -------------------- | - * | Australia | 3 | 27 | - * | Germany | 10 | 12 | - * | US | 5 | 7 | - * - * Now let's put the `Users.country` dimension on **y** axis instead - * ```js - * resultSet.tablePivot({ - * x: ['Users.gender'], - * y: ['Users.country', 'measures'], - * }); - * ``` - * - * in this case the `Users.country` values will be laid out on **y** or **columns** axis - * - * | Users Gender | Australia, Orders.count | Germany, Orders.count | US, Orders.count | - * | ------------ | ----------------------- | --------------------- | ---------------- | - * | male | 3 | 10 | 5 | - * | female | 27 | 12 | 7 | - * - * It's also possible to put the `measures` on **x** axis. But in either case it should always be the last item of the array. - * ```js - * resultSet.tablePivot({ - * x: ['Users.gender', 'measures'], - * y: ['Users.country'], - * }); - * ``` - * - * | Users Gender | measures | Australia | Germany | US | - * | ------------ | ------------ | --------- | ------- | --- | - * | male | Orders.count | 3 | 10 | 5 | - * | female | Orders.count | 27 | 12 | 7 | - */ - export type PivotConfig = { - /** - * Dimensions to put on **x** or **rows** axis. - */ - x?: string[]; - /** - * Dimensions to put on **y** or **columns** axis. - */ - y?: string[]; - /** - * If `true` missing dates on the time dimensions will be filled with fillWithValue or `0` by default for all measures.Note: the `fillMissingDates` option set to `true` will override any **order** applied to the query - */ - fillMissingDates?: boolean | null; - /** - * Value to autofill all the missing date's measure. - */ - fillWithValue?: string | number | null; - /** - * Give each series a prefix alias. Should have one entry for each query:measure. See [chartPivot](#result-set-chart-pivot) - */ - aliasSeries?: string[]; - }; - - export type DrillDownLocator = { - xValues: string[]; - yValues?: string[]; - }; - - export type Series = { - key: string; - title: string; - shortTitle: string; - series: T[]; - }; - - export type Column = { - key: string; - title: string; - series: []; - }; - - export type SeriesNamesColumn = { - key: string; - title: string; - shortTitle: string; - yValues: string[]; - }; - - export type ChartPivotRow = { - x: string; - xValues: string[]; - [key: string]: any; - }; - - export type TableColumn = { - key: string; - dataIndex: string; - meta: any; - type: string | number; - title: string; - shortTitle: string; - format?: any; - children?: TableColumn[]; - }; - - export type PivotRow = { - xValues: Array; - yValuesArray: Array<[string[], number]>; - }; - - export type SerializedResult = { - loadResponse: LoadResponse; - }; - - /** - * Provides a convenient interface for data manipulation. - */ - export class ResultSet { - /** - * @hidden - */ - static measureFromAxis(axisValues: string[]): string; - static getNormalizedPivotConfig(query: PivotQuery, pivotConfig?: Partial): PivotConfig; - /** - * ```js - * import { ResultSet } from '@cubejs-client/core'; - * - * const resultSet = await cubeApi.load(query); - * // You can store the result somewhere - * const tmp = resultSet.serialize(); - * - * // and restore it later - * const resultSet = ResultSet.deserialize(tmp); - * ``` - * @param data the result of [serialize](#result-set-serialize) - */ - static deserialize(data: Object, options?: Object): ResultSet; - - /** - * Can be used to stash the `ResultSet` in a storage and restored later with [deserialize](#result-set-deserialize) - */ - serialize(): SerializedResult; - - /** - * Can be used when you need access to the methods that can't be used with some query types (eg `compareDateRangeQuery` or `blendingQuery`) - * ```js - * resultSet.decompose().forEach((currentResultSet) => { - * console.log(currentResultSet.rawData()); - * }); - * ``` - */ - decompose(): ResultSet[]; - - /** - * @hidden - */ - normalizePivotConfig(pivotConfig?: PivotConfig): PivotConfig; - - /** - * Returns a measure drill down query. - * - * Provided you have a measure with the defined `drillMemebers` on the `Orders` cube - * ```js - * measures: { - * count: { - * type: `count`, - * drillMembers: [Orders.status, Users.city, count], - * }, - * // ... - * } - * ``` - * - * Then you can use the `drillDown` method to see the rows that contribute to that metric - * ```js - * resultSet.drillDown( - * { - * xValues, - * yValues, - * }, - * // you should pass the `pivotConfig` if you have used it for axes manipulation - * pivotConfig - * ) - * ``` - * - * the result will be a query with the required filters applied and the dimensions/measures filled out - * ```js - * { - * measures: ['Orders.count'], - * dimensions: ['Orders.status', 'Users.city'], - * filters: [ - * // dimension and measure filters - * ], - * timeDimensions: [ - * //... - * ] - * } - * ``` - * - * In case when you want to add `order` or `limit` to the query, you can simply spread it - * - * ```js - * // An example for React - * const drillDownResponse = useCubeQuery( - * { - * ...drillDownQuery, - * limit: 30, - * order: { - * 'Orders.ts': 'desc' - * } - * }, - * { - * skip: !drillDownQuery - * } - * ); - * ``` - * @returns Drill down query - */ - drillDown(drillDownLocator: DrillDownLocator, pivotConfig?: PivotConfig): Query | null; - - /** - * Returns an array of series with key, title and series data. - * ```js - * // For the query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.series() will return - * [ - * { - * key: 'Stories.count', - * title: 'Stories Count', - * shortTitle: 'Count', - * series: [ - * { x: '2015-01-01T00:00:00', value: 27120 }, - * { x: '2015-02-01T00:00:00', value: 25861 }, - * { x: '2015-03-01T00:00:00', value: 29661 }, - * //... - * ], - * }, - * ] - * ``` - */ - series(pivotConfig?: PivotConfig): Series[]; - - /** - * Returns an array of series objects, containing `key` and `title` parameters. - * ```js - * // For query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.seriesNames() will return - * [ - * { - * key: 'Stories.count', - * title: 'Stories Count', - * shortTitle: 'Count', - * yValues: ['Stories.count'], - * }, - * ] - * ``` - * @returns An array of series names - */ - seriesNames(pivotConfig?: PivotConfig): SeriesNamesColumn[]; - - /** - * Base method for pivoting [ResultSet](#result-set) data. - * Most of the times shouldn't be used directly and [chartPivot](#result-set-chart-pivot) - * or [tablePivot](#table-pivot) should be used instead. - * - * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) - * ```js - * // For query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-03-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.pivot({ x: ['Stories.time'], y: ['measures'] }) will return - * [ - * { - * xValues: ["2015-01-01T00:00:00"], - * yValuesArray: [ - * [['Stories.count'], 27120] - * ] - * }, - * { - * xValues: ["2015-02-01T00:00:00"], - * yValuesArray: [ - * [['Stories.count'], 25861] - * ] - * }, - * { - * xValues: ["2015-03-01T00:00:00"], - * yValuesArray: [ - * [['Stories.count'], 29661] - * ] - * } - * ] - * ``` - * @returns An array of pivoted rows. - */ - pivot(pivotConfig?: PivotConfig): PivotRow[]; - - /** - * Returns normalized query result data in the following format. - * - * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) - * ```js - * // For the query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.chartPivot() will return - * [ - * { "x":"2015-01-01T00:00:00", "Stories.count": 27120, "xValues": ["2015-01-01T00:00:00"] }, - * { "x":"2015-02-01T00:00:00", "Stories.count": 25861, "xValues": ["2015-02-01T00:00:00"] }, - * { "x":"2015-03-01T00:00:00", "Stories.count": 29661, "xValues": ["2015-03-01T00:00:00"] }, - * //... - * ] - * - * ``` - * When using `chartPivot()` or `seriesNames()`, you can pass `aliasSeries` in the [pivotConfig](#types-pivot-config) - * to give each series a unique prefix. This is useful for `blending queries` which use the same measure multiple times. - * - * ```js - * // For the queries - * { - * measures: ['Stories.count'], - * timeDimensions: [ - * { - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month', - * }, - * ], - * }, - * { - * measures: ['Stories.count'], - * timeDimensions: [ - * { - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month', - * }, - * ], - * filters: [ - * { - * member: 'Stores.read', - * operator: 'equals', - * value: ['true'], - * }, - * ], - * }, - * - * // ResultSet.chartPivot({ aliasSeries: ['one', 'two'] }) will return - * [ - * { - * x: '2015-01-01T00:00:00', - * 'one,Stories.count': 27120, - * 'two,Stories.count': 8933, - * xValues: ['2015-01-01T00:00:00'], - * }, - * { - * x: '2015-02-01T00:00:00', - * 'one,Stories.count': 25861, - * 'two,Stories.count': 8344, - * xValues: ['2015-02-01T00:00:00'], - * }, - * { - * x: '2015-03-01T00:00:00', - * 'one,Stories.count': 29661, - * 'two,Stories.count': 9023, - * xValues: ['2015-03-01T00:00:00'], - * }, - * //... - * ]; - * ``` - */ - chartPivot(pivotConfig?: PivotConfig): ChartPivotRow[]; - - /** - * Returns normalized query result data prepared for visualization in the table format. - * - * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) - * - * For example: - * ```js - * // For the query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.tablePivot() will return - * [ - * { "Stories.time": "2015-01-01T00:00:00", "Stories.count": 27120 }, - * { "Stories.time": "2015-02-01T00:00:00", "Stories.count": 25861 }, - * { "Stories.time": "2015-03-01T00:00:00", "Stories.count": 29661 }, - * //... - * ] - * ``` - * @returns An array of pivoted rows - */ - tablePivot(pivotConfig?: PivotConfig): Array<{ [key: string]: string | number | boolean }>; - - /** - * Returns an array of column definitions for `tablePivot`. - * - * For example: - * ```js - * // For the query - * { - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * } - * - * // ResultSet.tableColumns() will return - * [ - * { - * key: 'Stories.time', - * dataIndex: 'Stories.time', - * title: 'Stories Time', - * shortTitle: 'Time', - * type: 'time', - * format: undefined, - * }, - * { - * key: 'Stories.count', - * dataIndex: 'Stories.count', - * title: 'Stories Count', - * shortTitle: 'Count', - * type: 'count', - * format: undefined, - * }, - * //... - * ] - * ``` - * - * In case we want to pivot the table axes - * ```js - * // Let's take this query as an example - * { - * measures: ['Orders.count'], - * dimensions: ['Users.country', 'Users.gender'] - * } - * - * // and put the dimensions on `y` axis - * resultSet.tableColumns({ - * x: [], - * y: ['Users.country', 'Users.gender', 'measures'] - * }) - * ``` - * - * then `tableColumns` will group the table head and return - * ```js - * { - * key: 'Germany', - * type: 'string', - * title: 'Users Country Germany', - * shortTitle: 'Germany', - * meta: undefined, - * format: undefined, - * children: [ - * { - * key: 'male', - * type: 'string', - * title: 'Users Gender male', - * shortTitle: 'male', - * meta: undefined, - * format: undefined, - * children: [ - * { - * // ... - * dataIndex: 'Germany.male.Orders.count', - * shortTitle: 'Count', - * }, - * ], - * }, - * { - * // ... - * shortTitle: 'female', - * children: [ - * { - * // ... - * dataIndex: 'Germany.female.Orders.count', - * shortTitle: 'Count', - * }, - * ], - * }, - * ], - * }, - * // ... - * ``` - * @returns An array of columns - */ - tableColumns(pivotConfig?: PivotConfig): TableColumn[]; - totalRow(pivotConfig?: PivotConfig): ChartPivotRow; - categories(pivotConfig?: PivotConfig): ChartPivotRow[]; - - tableRow(): ChartPivotRow; - query(): Query; - rawData(): T[]; - annotation(): QueryAnnotations; - - /** - * @return the total number of rows if the `total` option was set, when sending the query - */ - totalRows(): number | null; - } - - export type Filter = BinaryFilter | UnaryFilter | LogicalOrFilter | LogicalAndFilter; - export type LogicalAndFilter = { - and: Filter[]; - }; - - export type LogicalOrFilter = { - or: Filter[]; - }; - - export interface BinaryFilter { - /** - * @deprecated Use `member` instead. - */ - dimension?: string; - member?: string; - operator: BinaryOperator; - values: string[]; - } - export interface UnaryFilter { - /** - * @deprecated Use `member` instead. - */ - dimension?: string; - member?: string; - operator: UnaryOperator; - values?: never; - } - export type UnaryOperator = 'set' | 'notSet'; - export type BinaryOperator = - | 'equals' - | 'notEquals' - | 'contains' - | 'notContains' - | 'startsWith' - | 'notStartsWith' - | 'endsWith' - | 'notEndsWith' - | 'gt' - | 'gte' - | 'lt' - | 'lte' - | 'inDateRange' - | 'notInDateRange' - | 'beforeDate' - | 'beforeOrOnDate' - | 'afterDate' - | 'afterOrOnDate'; - - export type TimeDimensionPredefinedGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; - - export type TimeDimensionGranularity = TimeDimensionPredefinedGranularity | string; - - export type DateRange = string | [string, string]; - - export interface TimeDimensionBase { - dimension: string; - granularity?: TimeDimensionGranularity; - } - - type TimeDimensionComparisonFields = { - compareDateRange: Array; - dateRange?: never; - }; - export type TimeDimensionComparison = TimeDimensionBase & TimeDimensionComparisonFields; - - type TimeDimensionRangedFields = { - dateRange?: DateRange; - }; - export type TimeDimensionRanged = TimeDimensionBase & TimeDimensionRangedFields; - - export type TimeDimension = TimeDimensionComparison | TimeDimensionRanged; - - type DeeplyReadonly = { - readonly [K in keyof T]: DeeplyReadonly; - }; - - export interface Query { - measures?: string[]; - dimensions?: string[]; - filters?: Filter[]; - timeDimensions?: TimeDimension[]; - segments?: string[]; - limit?: null | number; - offset?: number; - order?: TQueryOrderObject | TQueryOrderArray; - timezone?: string; - renewQuery?: boolean; - ungrouped?: boolean; - responseFormat?: 'compact' | 'default'; - total?: boolean; - } - - export type QueryRecordType> = - T extends DeeplyReadonly ? QueryArrayRecordType : - T extends DeeplyReadonly ? SingleQueryRecordType : - never; - - type QueryArrayRecordType> = - T extends readonly [infer First, ...infer Rest] - ? SingleQueryRecordType> | QueryArrayRecordType> - : never; - - // If we can't infer any members at all, then return any. - type SingleQueryRecordType> = ExtractMembers extends never - ? any - : { [K in string & ExtractMembers]: string | number | boolean | null }; - - type ExtractMembers> = - | (T extends { dimensions: readonly (infer Names)[]; } ? Names : never) - | (T extends { measures: readonly (infer Names)[]; } ? Names : never) - | (T extends { timeDimensions: (infer U); } ? ExtractTimeMembers : never); - - type ExtractTimeMembers = - T extends readonly [infer First, ...infer Rest] - ? ExtractTimeMember | ExtractTimeMembers - : never; - - type ExtractTimeMember = - T extends { dimension: infer Dimension, granularity: infer Granularity } - ? Dimension | `${Dimension & string}.${Granularity & string}` - : never; - - export class ProgressResult { - stage(): string; - timeElapsed(): string; - } - - export interface UnsubscribeObj { - /** - * Allows to stop requests in-flight in long polling or web socket subscribe loops. - * It doesn't cancel any submitted requests to the underlying databases. - */ - unsubscribe(): Promise; - } - - export type SqlQueryTuple = [string, any[], any]; - - export type SqlData = { - aliasNameToMember: Record; - cacheKeyQueries: SqlQueryTuple[]; - dataSource: boolean; - external: boolean; - sql: SqlQueryTuple; - preAggregations: any[]; - rollupMatchResults: any[]; - }; - - export class SqlQuery { - rawQuery(): SqlData; - sql(): string; - } - - export type MemberType = 'measures' | 'dimensions' | 'segments'; - - type TOrderMember = { - id: string; - title: string; - order: QueryOrder | 'none'; - }; - - type TCubeMemberType = 'time' | 'number' | 'string' | 'boolean'; - - // @see BaseCubeMember - // @deprecated - export type TCubeMember = { - type: TCubeMemberType; - name: string; - title: string; - shortTitle: string; - description?: string; - /** - * @deprecated use `public` instead - */ - isVisible?: boolean; - public?: boolean; - meta?: any; - }; - - export type BaseCubeMember = { - type: TCubeMemberType; - name: string; - title: string; - shortTitle: string; - description?: string; - /** - * @deprecated use `public` instead - */ - isVisible?: boolean; - public?: boolean; - meta?: any; - }; - - export type TCubeMeasure = BaseCubeMember & { - aggType: 'count' | 'number'; - cumulative: boolean; - cumulativeTotal: boolean; - drillMembers: string[]; - drillMembersGrouped: { - measures: string[]; - dimensions: string[]; - }; - format?: 'currency' | 'percent'; - }; - - export type CubeTimeDimensionGranularity = { - name: string; - title: string; - } - - export type BaseCubeDimension = BaseCubeMember & { - primaryKey?: boolean; - suggestFilterValues: boolean; - } - - export type CubeTimeDimension = BaseCubeDimension & - { type: 'time'; granularities?: CubeTimeDimensionGranularity[] }; - - export type TCubeDimension = - (BaseCubeDimension & { type: Exclude }) | - CubeTimeDimension; - - export type TCubeSegment = Omit; - - type TCubeMemberByType = T extends 'measures' - ? TCubeMeasure - : T extends 'dimensions' - ? TCubeDimension - : T extends 'segments' - ? TCubeSegment - : never; - - export type CubeMember = TCubeMeasure | TCubeDimension | TCubeSegment; - - export type TCubeFolder = { - name: string; - members: string[]; - }; - - export type TCubeHierarchy = { - name: string; - title?: string; - levels: string[]; - public?: boolean; - }; - - /** - * @deprecated use DryRunResponse - */ - type TDryRunResponse = { - queryType: QueryType; - normalizedQueries: Query[]; - pivotQuery: PivotQuery; - queryOrder: Array<{ [k: string]: QueryOrder }>; - transformedQueries: TransformedQuery[]; - }; - - export type DryRunResponse = { - queryType: QueryType; - normalizedQueries: Query[]; - pivotQuery: PivotQuery; - queryOrder: Array<{ [k: string]: QueryOrder }>; - transformedQueries: TransformedQuery[]; - }; - - export type Cube = { - name: string; - title: string; - description?: string; - measures: TCubeMeasure[]; - dimensions: TCubeDimension[]; - segments: TCubeSegment[]; - folders: TCubeFolder[]; - hierarchies: TCubeHierarchy[]; - connectedComponent?: number; - type?: 'view' | 'cube'; - /** - * @deprecated use `public` instead - */ - isVisible?: boolean; - public?: boolean; - meta?: any; - }; - - - export type CubeMap = { - measures: Record; - dimensions: Record; - segments: Record; - }; - - export type CubesMap = Record< - string, - CubeMap - >; - - export type MetaResponse = { - cubes: Cube[]; - }; - - type FilterOperator = { - name: string; - title: string; - }; - - /** - * Contains information about available cubes and it's members. - * @order 4 - */ - export class Meta { - - constructor(metaResponse: MetaResponse); - - /** - * Raw meta response - */ - meta: MetaResponse; - - /** - * An array of all available cubes with their members - */ - cubes: Cube[]; - - /** - * A map of all cubes where the key is a cube name - */ - cubesMap: CubesMap; - - /** - * Get all members of a specific type for a given query. - * If empty query is provided no filtering is done based on query context and all available members are retrieved. - * @param query - context query to provide filtering of members available to add to this query - */ - membersForQuery(query: DeeplyReadonly | null, memberType: MemberType): TCubeMeasure[] | TCubeDimension[] | TCubeMember[]; - - /** - * Get meta information for a cube member - * Member meta information contains: - * ```javascript - * { - * name, - * title, - * shortTitle, - * type, - * description, - * format - * } - * ``` - * @param memberName - Fully qualified member name in a form `Cube.memberName` - * @return An object containing meta information about member - */ - resolveMember( - memberName: string, - memberType: T | T[] - ): { title: string; error: string } | TCubeMemberByType; - defaultTimeDimensionNameFor(memberName: string): string; - filterOperatorsForMember(memberName: string, memberType: MemberType | MemberType[]): FilterOperator[]; - - // todo: types - membersGroupedByCube(): any; - } - - /** - * Main class for accessing Cube API - * - * @order 2 - */ - export class CubeApi { - load>( - query: QueryType, - options?: LoadMethodOptions, - ): Promise>>; - /** - * Fetch data for the passed `query`. - * - * ```js - * import cube from '@cubejs-client/core'; - * import Chart from 'chart.js'; - * import chartjsConfig from './toChartjsData'; - * - * const cubeApi = cube('CUBEJS_TOKEN'); - * - * const resultSet = await cubeApi.load({ - * measures: ['Stories.count'], - * timeDimensions: [{ - * dimension: 'Stories.time', - * dateRange: ['2015-01-01', '2015-12-31'], - * granularity: 'month' - * }] - * }); - * - * const context = document.getElementById('myChart'); - * new Chart(context, chartjsConfig(resultSet)); - * ``` - * @param query - [Query object](/product/apis-integrations/rest-api/query-format) - */ - load>( - query: QueryType, - options?: LoadMethodOptions, - callback?: LoadMethodCallback>>, - ): UnsubscribeObj; - - load>( - query: QueryType, - options?: LoadMethodOptions, - callback?: LoadMethodCallback, - responseFormat?: string - ): Promise>>; - - /** - * Allows you to fetch data and receive updates over time. See [Real-Time Data Fetch](/product/apis-integrations/rest-api/real-time-data-fetch) - * - * ```js - * // Subscribe to a query's updates - * const subscription = await cubeApi.subscribe( - * { - * measures: ['Logs.count'], - * timeDimensions: [ - * { - * dimension: 'Logs.time', - * granularity: 'hour', - * dateRange: 'last 1440 minutes', - * }, - * ], - * }, - * options, - * (error, resultSet) => { - * if (!error) { - * // handle the update - * } - * } - * ); - * - * // Unsubscribe from a query's updates - * subscription.unsubscribe(); - * ``` - */ - subscribe>( - query: QueryType, - options: LoadMethodOptions | null, - callback: LoadMethodCallback>>, - ): UnsubscribeObj; - - sql(query: DeeplyReadonly, options?: LoadMethodOptions): Promise; - /** - * Get generated SQL string for the given `query`. - * @param query - [Query object](query-format) - */ - sql(query: DeeplyReadonly, options?: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; - - meta(options?: LoadMethodOptions): Promise; - /** - * Get meta description of cubes available for querying. - */ - meta(options?: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; - - dryRun(query: DeeplyReadonly, options?: LoadMethodOptions): Promise; - /** - * Get query related meta without query execution - */ - dryRun(query: DeeplyReadonly, options: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; - } - - /** - * Creates an instance of the `CubeApi`. The API entry point. - * - * ```js - * import cube from '@cubejs-client/core'; - * const cubeApi = cube( - * 'CUBE-API-TOKEN', - * { apiUrl: 'http://localhost:4000/cubejs-api/v1' } - * ); - * ``` - * - * You can also pass an async function or a promise that will resolve to the API token - * - * ```js - * import cube from '@cubejs-client/core'; - * const cubeApi = cube( - * async () => await Auth.getJwtToken(), - * { apiUrl: 'http://localhost:4000/cubejs-api/v1' } - * ); - * ``` - * - * @param apiToken - [API token](/product/auth) is used to authorize requests and determine SQL database you're accessing. In the development mode, Cube.js Backend will print the API token to the console on startup. In case of async function `authorization` is updated for `options.transport` on each request. - * @order 1 - */ - export default function cube(apiToken: string | (() => Promise), options: CubeApiOptions): CubeApi; - export default function cube(options: CubeApiOptions): CubeApi; - - /** - * @hidden - */ - export type TSourceAxis = 'x' | 'y'; - - export type ChartType = 'line' | 'bar' | 'table' | 'area' | 'number' | 'pie'; - - export type TDefaultHeuristicsOptions = { - meta: Meta; - sessionGranularity?: TimeDimensionGranularity; - }; - - export type TDefaultHeuristicsResponse = { - shouldApplyHeuristicOrder: boolean; - pivotConfig: PivotConfig | null; - query: Query; - chartType?: ChartType; - }; - - export type TDefaultHeuristicsState = { - query: Query; - chartType?: ChartType; - }; - - export function defaultHeuristics( - newState: TDefaultHeuristicsState, - oldQuery: Query, - options: TDefaultHeuristicsOptions - ): TDefaultHeuristicsResponse; - /** - * @hidden - */ - export function isQueryPresent(query: DeeplyReadonly | null | undefined): boolean; - export function movePivotItem( - pivotConfig: PivotConfig, - sourceIndex: number, - destinationIndex: number, - sourceAxis: TSourceAxis, - destinationAxis: TSourceAxis - ): PivotConfig; - /** - * @hidden - */ - export function moveItemInArray(list: T[], sourceIndex: number, destinationIndex: number): T[]; - - export function defaultOrder(query: DeeplyReadonly): { [key: string]: QueryOrder }; - - export interface TFlatFilter { - /** - * @deprecated Use `member` instead. - */ - dimension?: string; - member?: string; - operator: BinaryOperator; - values: string[]; - } - - /** - * @hidden - */ - export function flattenFilters(filters: Filter[]): TFlatFilter[]; - - type TGranularityMap = { - name: TimeDimensionGranularity | undefined; - title: string; - }; - - /** - * @hidden - */ - export function getOrderMembersFromOrder( - orderMembers: any, - order: TQueryOrderObject | TQueryOrderArray - ): TOrderMember[]; - - export const GRANULARITIES: TGranularityMap[]; - /** - * @hidden - */ - export function getQueryMembers(query: DeeplyReadonly): string[]; - - export function areQueriesEqual(query1: DeeplyReadonly | null, query2: DeeplyReadonly | null): boolean; - - export function validateQuery(query: DeeplyReadonly | null | undefined): Query; - - export type ProgressResponse = { - stage: string; - timeElapsed: number; - }; - - export function granularityFor(dateStr: string): string; - - export function minGranularityForIntervals(i1: string, i2: string): string; - - export function isPredefinedGranularity(granularity: TimeDimensionGranularity): boolean; -} diff --git a/packages/cubejs-client-core/src/HttpTransport.ts b/packages/cubejs-client-core/src/HttpTransport.ts index 91dd8b9a1e1f4..bcb9b6a602810 100644 --- a/packages/cubejs-client-core/src/HttpTransport.ts +++ b/packages/cubejs-client-core/src/HttpTransport.ts @@ -1,8 +1,61 @@ import fetch from 'cross-fetch'; import 'url-search-params-polyfill'; -class HttpTransport { - constructor({ authorization, apiUrl, method, headers = {}, credentials, fetchTimeout, signal }) { +export interface ErrorResponse { + error: string; +} + +export type TransportOptions = { + /** + * [jwt auth token](security) + */ + authorization?: string; + /** + * path to `/cubejs-api/v1` + */ + apiUrl: string; + /** + * custom headers + */ + headers: Record; + credentials?: 'omit' | 'same-origin' | 'include'; + method?: 'GET' | 'PUT' | 'POST' | 'PATCH'; + /** + * Fetch timeout in milliseconds. Would be passed as AbortSignal.timeout() + */ + fetchTimeout?: number; +}; + +export interface ITransportResponse { + subscribe: (cb: (result: R | ErrorResponse, resubscribe: () => Promise) => CBResult) => Promise; + // Optional, supported in WebSocketTransport + unsubscribe?: () => Promise; +} + +export interface ITransport { + request(method: string, params: Record): ITransportResponse; + authorization: TransportOptions['authorization']; +} + +/** + * Default transport implementation. + */ +class HttpTransport implements ITransport { + public authorization: TransportOptions['authorization']; + + protected apiUrl: TransportOptions['apiUrl']; + + protected method: TransportOptions['method']; + + protected headers: TransportOptions['headers']; + + protected credentials: TransportOptions['credentials']; + + protected fetchTimeout: number | undefined; + + private signal: any; + + public constructor({ authorization, apiUrl, method, headers = {}, credentials, fetchTimeout, signal }: Omit & { headers?: TransportOptions['headers'] }) { this.authorization = authorization; this.apiUrl = apiUrl; this.method = method; @@ -12,7 +65,7 @@ class HttpTransport { this.signal = signal; } - request(method, { baseRequestId, signal, ...params }) { + public request(method: string, { baseRequestId, signal, ...params }: any): ITransportResponse { let spanCounter = 1; const searchParams = new URLSearchParams( params && Object.keys(params) @@ -36,7 +89,7 @@ class HttpTransport { Authorization: this.authorization, 'x-request-id': baseRequestId && `${baseRequestId}-span-${spanCounter++}`, ...this.headers - }, + } as HeadersInit, credentials: this.credentials, body: requestMethod === 'POST' ? JSON.stringify(params) : null, signal: signal || this.signal || (this.fetchTimeout ? AbortSignal.timeout(this.fetchTimeout) : undefined), @@ -45,12 +98,11 @@ class HttpTransport { return { /* eslint no-unsafe-finally: off */ async subscribe(callback) { - let result = { - error: 'network Error' // add default error message - }; try { - result = await runRequest(); - } finally { + const result = await runRequest(); + return callback(result, () => this.subscribe(callback)); + } catch (e) { + const result: ErrorResponse = { error: 'network Error' }; return callback(result, () => this.subscribe(callback)); } } diff --git a/packages/cubejs-client-core/src/Meta.ts b/packages/cubejs-client-core/src/Meta.ts index 38a7408877e03..b22438ced8567 100644 --- a/packages/cubejs-client-core/src/Meta.ts +++ b/packages/cubejs-client-core/src/Meta.ts @@ -1,10 +1,37 @@ -/** - * @module @cubejs-client/core - */ - import { unnest, fromPairs } from 'ramda'; +import { + Cube, + CubesMap, + MemberType, + MetaResponse, + TCubeMeasure, + TCubeDimension, + TCubeMember, + TCubeMemberByType, + Query, + FilterOperator, + TCubeSegment, NotFoundMember +} from './types'; +import { DeeplyReadonly } from './index'; + +export interface CubeMemberWrapper { + cubeName: string; + cubeTitle: string; + type?: 'view' | 'cube'; + public?: boolean; + members: T[]; +} + +export type AggregatedMembers = { + measures: CubeMemberWrapper[]; + dimensions: CubeMemberWrapper[]; + segments: CubeMemberWrapper[]; + timeDimensions: CubeMemberWrapper[]; +}; -const memberMap = (memberArray) => fromPairs(memberArray.map((m) => [m.name, m])); +const memberMap = (memberArray: any[]) => fromPairs( + memberArray.map((m) => [m.name, m]) +); const operators = { string: [ @@ -44,8 +71,23 @@ const operators = { /** * Contains information about available cubes and it's members. */ -class Meta { - constructor(metaResponse) { +export default class Meta { + /** + * Raw meta response + */ + public readonly meta: MetaResponse; + + /** + * An array of all available cubes with their members + */ + public readonly cubes: Cube[]; + + /** + * A map of all cubes where the key is a cube name + */ + public readonly cubesMap: CubesMap; + + public constructor(metaResponse: MetaResponse) { this.meta = metaResponse; const { cubes } = this.meta; this.cubes = cubes; @@ -61,34 +103,65 @@ class Meta { ); } - membersForQuery(query, memberType) { - return unnest(this.cubes.map((c) => c[memberType])).sort((a, b) => (a.title > b.title ? 1 : -1)); + /** + * Get all members of a specific type for a given query. + * If empty query is provided no filtering is done based on query context and all available members are retrieved. + * @param _query - context query to provide filtering of members available to add to this query + * @param memberType + */ + public membersForQuery(_query: DeeplyReadonly | null, memberType: MemberType): (TCubeMeasure | TCubeDimension | TCubeMember | TCubeSegment)[] { + return unnest(this.cubes.map((c) => c[memberType])) + .sort((a, b) => (a.title > b.title ? 1 : -1)); } - membersGroupedByCube() { + public membersGroupedByCube() { const memberKeys = ['measures', 'dimensions', 'segments', 'timeDimensions']; - return this.cubes.reduce( + return this.cubes.reduce( (memo, cube) => { memberKeys.forEach((key) => { - let members = cube[key]; - - if (key === 'timeDimensions') { - members = cube.dimensions.filter((m) => m.type === 'time'); + let members: TCubeMeasure[] | TCubeDimension[] | TCubeSegment[] = []; + + // eslint-disable-next-line default-case + switch (key) { + case 'measures': + members = cube.measures || []; + break; + case 'dimensions': + members = cube.dimensions || []; + break; + case 'segments': + members = cube.segments || []; + break; + case 'timeDimensions': + members = cube.dimensions.filter((m) => m.type === 'time') || []; + break; } - memo[key] = [ - ...memo[key], - { - cubeName: cube.name, - cubeTitle: cube.title, - type: cube.type, - public: cube.public, - members - }, - ]; + // TODO: Convince TS this is working + // @ts-ignore + memo[key].push({ + cubeName: cube.name, + cubeTitle: cube.title, + type: cube.type, + public: cube.public, + members, + }); }); + const timeDimensionMembers = cube.dimensions.filter((m) => m.type === 'time'); + + memo.timeDimensions = [ + ...memo.timeDimensions, + { + cubeName: cube.name, + cubeTitle: cube.title, + type: cube.type, + public: cube.public, + members: timeDimensionMembers, + }, + ]; + return memo; }, { @@ -100,7 +173,27 @@ class Meta { ); } - resolveMember(memberName, memberType) { + /** + * Get meta information for a cube member + * meta information contains: + * ```javascript + * { + * name, + * title, + * shortTitle, + * type, + * description, + * format + * } + * ``` + * @param memberName - Fully qualified member name in a form `Cube.memberName` + * @param memberType + * @return An object containing meta information about member + */ + public resolveMember( + memberName: string, + memberType: T | T[] + ): NotFoundMember | TCubeMemberByType { const [cube] = memberName.split('.'); if (!this.cubesMap[cube]) { @@ -119,10 +212,10 @@ class Meta { }; } - return member; + return member as TCubeMemberByType; } - defaultTimeDimensionNameFor(memberName) { + public defaultTimeDimensionNameFor(memberName: string): string | null | undefined { const [cube] = memberName.split('.'); if (!this.cubesMap[cube]) { return null; @@ -132,11 +225,13 @@ class Meta { ); } - filterOperatorsForMember(memberName, memberType) { + public filterOperatorsForMember(memberName: string, memberType: MemberType | MemberType[]): FilterOperator[] { const member = this.resolveMember(memberName, memberType); + if ('error' in member || !('type' in member) || member.type === 'boolean') { + return operators.string; + } + return operators[member.type] || operators.string; } } - -export default Meta; diff --git a/packages/cubejs-client-core/src/ProgressResult.ts b/packages/cubejs-client-core/src/ProgressResult.ts index fd02aec278540..a8ecad1cfb47f 100644 --- a/packages/cubejs-client-core/src/ProgressResult.ts +++ b/packages/cubejs-client-core/src/ProgressResult.ts @@ -1,13 +1,17 @@ +import { ProgressResponse } from './types'; + export default class ProgressResult { - constructor(progressResponse) { + progressResponse: ProgressResponse; + + public constructor(progressResponse: ProgressResponse) { this.progressResponse = progressResponse; } - stage() { + public stage(): string { return this.progressResponse.stage; } - timeElapsed() { + public timeElapsed(): number { return this.progressResponse.timeElapsed; } } diff --git a/packages/cubejs-client-core/src/RequestError.ts b/packages/cubejs-client-core/src/RequestError.ts index 74364fe2b1f36..a0c86d59de78c 100644 --- a/packages/cubejs-client-core/src/RequestError.ts +++ b/packages/cubejs-client-core/src/RequestError.ts @@ -1,5 +1,9 @@ export default class RequestError extends Error { - constructor(message, response, status) { + response: any; + + status: number; + + public constructor(message: string, response: any, status: number) { super(message); this.response = response; this.status = status; diff --git a/packages/cubejs-client-core/src/ResultSet.ts b/packages/cubejs-client-core/src/ResultSet.ts index 02777216fa126..a00ea2900390a 100644 --- a/packages/cubejs-client-core/src/ResultSet.ts +++ b/packages/cubejs-client-core/src/ResultSet.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { - groupBy, pipe, fromPairs, uniq, filter, map, dropLast, equals, reduce, minBy, maxBy, clone, mergeDeepLeft, - pluck, mergeAll, flatten, + groupBy, pipe, fromPairs, uniq, map, dropLast, equals, reduce, minBy, maxBy, clone, mergeDeepLeft, + flatten, } from 'ramda'; import { aliasSeries } from './utils'; @@ -14,8 +14,25 @@ import { TIME_SERIES, timeSeriesFromCustomInterval } from './time'; - -const groupByToPairs = (keyFn) => { +import { + Annotation, + ChartPivotRow, DateRange, + DrillDownLocator, + LoadResponse, + LoadResponseResult, Pivot, + PivotConfig, + PivotQuery, + PivotRow, + Query, + QueryAnnotations, QueryType, + SerializedResult, + Series, + SeriesNamesColumn, + TableColumn, + TimeDimension +} from './types'; + +const groupByToPairs = function groupByToPairsImpl(keyFn: (item: T) => K): (data: T[]) => [K, T[]][] { const acc = new Map(); return (data) => { @@ -33,8 +50,8 @@ const groupByToPairs = (keyFn) => { }; }; -const unnest = (arr) => { - const res = []; +const unnest = (arr: any[][]): any[] => { + const res: any[] = []; arr.forEach((subArr) => { subArr.forEach(element => res.push(element)); }); @@ -42,38 +59,73 @@ const unnest = (arr) => { return res; }; -export const QUERY_TYPE = { +export const QUERY_TYPE: Record = { REGULAR_QUERY: 'regularQuery', COMPARE_DATE_RANGE_QUERY: 'compareDateRangeQuery', BLENDING_QUERY: 'blendingQuery', }; -class ResultSet { - static measureFromAxis(axisValues) { +export type ResultSetOptions = { + parseDateMeasures?: boolean; +}; + +/** + * Provides a convenient interface for data manipulation. + */ +export default class ResultSet> { + loadResponse: LoadResponse; + + loadResponses: LoadResponseResult[]; + + queryType: QueryType; + + parseDateMeasures: boolean | undefined; + + options: {}; + + backwardCompatibleData: Record[][]; + + public static measureFromAxis(axisValues: string[]): string { return axisValues[axisValues.length - 1]; } - static timeDimensionMember(td) { + public static timeDimensionMember(td: TimeDimension) { return `${td.dimension}.${td.granularity}`; } - static deserialize(data, options = {}) { + /** + * ```js + * import { ResultSet } from '@cubejs-client/core'; + * + * const resultSet = await cubeApi.load(query); + * // You can store the result somewhere + * const tmp = resultSet.serialize(); + * + * // and restore it later + * const resultSet = ResultSet.deserialize(tmp); + * ``` + * @param data the result of [serialize](#result-set-serialize) + * @param options + */ + public static deserialize = any>(data: SerializedResult, options?: Object): ResultSet { return new ResultSet(data.loadResponse, options); } - constructor(loadResponse, options = {}) { - this.loadResponse = loadResponse; - - if (this.loadResponse.queryType != null) { + public constructor(loadResponse: LoadResponse | LoadResponseResult, options: ResultSetOptions = {}) { + if ('queryType' in loadResponse && loadResponse.queryType != null) { + this.loadResponse = loadResponse; this.queryType = loadResponse.queryType; this.loadResponses = loadResponse.results; } else { this.queryType = QUERY_TYPE.REGULAR_QUERY; - this.loadResponse.pivotQuery = { - ...loadResponse.query, - queryType: this.queryType - }; - this.loadResponses = [loadResponse]; + this.loadResponse = { + ...loadResponse, + pivotQuery: { + ...loadResponse.query, + queryType: this.queryType + } + } as LoadResponse; + this.loadResponses = [loadResponse as LoadResponseResult]; } if (!Object.values(QUERY_TYPE).includes(this.queryType)) { @@ -86,7 +138,66 @@ class ResultSet { this.backwardCompatibleData = []; } - drillDown(drillDownLocator, pivotConfig) { + /** + * Returns a measure drill down query. + * + * Provided you have a measure with the defined `drillMembers` on the `Orders` cube + * ```js + * measures: { + * count: { + * type: `count`, + * drillMembers: [Orders.status, Users.city, count], + * }, + * // ... + * } + * ``` + * + * Then you can use the `drillDown` method to see the rows that contribute to that metric + * ```js + * resultSet.drillDown( + * { + * xValues, + * yValues, + * }, + * // you should pass the `pivotConfig` if you have used it for axes manipulation + * pivotConfig + * ) + * ``` + * + * the result will be a query with the required filters applied and the dimensions/measures filled out + * ```js + * { + * measures: ['Orders.count'], + * dimensions: ['Orders.status', 'Users.city'], + * filters: [ + * // dimension and measure filters + * ], + * timeDimensions: [ + * //... + * ] + * } + * ``` + * + * In case when you want to add `order` or `limit` to the query, you can simply spread it + * + * ```js + * // An example for React + * const drillDownResponse = useCubeQuery( + * { + * ...drillDownQuery, + * limit: 30, + * order: { + * 'Orders.ts': 'desc' + * } + * }, + * { + * skip: !drillDownQuery + * } + * ); + * ``` + * @returns Drill down query + */ + public drillDown(drillDownLocator: DrillDownLocator, pivotConfig?: PivotConfig): Query | null { if (this.queryType === QUERY_TYPE.COMPARE_DATE_RANGE_QUERY) { throw new Error('compareDateRange drillDown query is not currently supported'); } @@ -95,12 +206,13 @@ class ResultSet { } const { query } = this.loadResponses[0]; - const { xValues = [], yValues = [] } = drillDownLocator; + const xValues = drillDownLocator?.xValues ?? []; + const yValues = drillDownLocator?.yValues ?? []; const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig); - const values = []; - normalizedPivotConfig.x.forEach((member, currentIndex) => values.push([member, xValues[currentIndex]])); - normalizedPivotConfig.y.forEach((member, currentIndex) => values.push([member, yValues[currentIndex]])); + const values: string[][] = []; + normalizedPivotConfig?.x.forEach((member, currentIndex) => values.push([member, xValues[currentIndex]])); + normalizedPivotConfig?.y.forEach((member, currentIndex) => values.push([member, yValues[currentIndex]])); const { filters: parentFilters = [], segments = [] } = this.query(); const { measures } = this.loadResponses[0].annotation; @@ -110,7 +222,7 @@ class ResultSet { [measureName] = Object.keys(measures); } - if (!(measures[measureName] && measures[measureName].drillMembers || []).length) { + if (!(measures[measureName]?.drillMembers?.length ?? 0)) { return null; } @@ -129,7 +241,7 @@ class ResultSet { if (granularity !== undefined) { const range = dayRange(value, value).snapTo(granularity); - const originalTimeDimension = query.timeDimensions.find((td) => td.dimension); + const originalTimeDimension = query.timeDimensions?.find((td) => td.dimension); let dateRange = [ range.start, @@ -165,6 +277,7 @@ class ResultSet { if ( timeDimensions.length === 0 && + Array.isArray(query.timeDimensions) && query.timeDimensions.length > 0 && query.timeDimensions[0].granularity == null ) { @@ -181,32 +294,66 @@ class ResultSet { }; } - series(pivotConfig) { + /** + * Returns an array of series with key, title and series data. + * ```js + * // For the query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.series() will return + * [ + * { + * key: 'Stories.count', + * title: 'Stories Count', + * shortTitle: 'Count', + * series: [ + * { x: '2015-01-01T00:00:00', value: 27120 }, + * { x: '2015-02-01T00:00:00', value: 25861 }, + * { x: '2015-03-01T00:00:00', value: 29661 }, + * //... + * ], + * }, + * ] + * ``` + */ + public series(pivotConfig?: PivotConfig): Series[] { return this.seriesNames(pivotConfig).map(({ title, shortTitle, key }) => ({ title, shortTitle, key, series: this.chartPivot(pivotConfig).map(({ x, ...obj }) => ({ value: obj[key], x })) - })); + } as Series)); } - axisValues(axis, resultIndex = 0) { + private axisValues(axis: string[], resultIndex = 0) { const { query } = this.loadResponses[resultIndex]; - return row => { - const value = (measure) => axis.filter(d => d !== 'measures') - .map(d => (row[d] != null ? row[d] : null)).concat(measure ? [measure] : []); + return (row: Record) => { + const value = (measure?: string) => axis + .filter(d => d !== 'measures') + .map((d: string) => { + const val = row[d]; + return val != null ? val : null; + }) + .concat(measure ? [measure] : []); if (axis.find(d => d === 'measures') && (query.measures || []).length) { - return query.measures.map(value); + return (query.measures || []).map(value); } return [value()]; }; } - axisValuesString(axisValues, delimiter) { - const formatValue = (v) => { + private axisValuesString(axisValues: (string | number)[], delimiter: string = ', '): string { + const formatValue = (v: string | number) => { if (v == null) { return '∅'; } else if (v === '') { @@ -215,11 +362,11 @@ class ResultSet { return v; } }; - return axisValues.map(formatValue).join(delimiter || ', '); + return axisValues.map(formatValue).join(delimiter); } - static getNormalizedPivotConfig(query = {}, pivotConfig = null) { - const defaultPivotConfig = { + public static getNormalizedPivotConfig(query: PivotQuery, pivotConfig?: Partial): PivotConfig { + const defaultPivotConfig: PivotConfig = { x: [], y: [], fillMissingDates: true, @@ -241,70 +388,75 @@ class ResultSet { y: [] }); - pivotConfig = mergeDeepLeft(pivotConfig, defaultPivotConfig); + const normalizedPivotConfig = mergeDeepLeft(pivotConfig, defaultPivotConfig) as PivotConfig; - const substituteTimeDimensionMembers = axis => axis.map( + const substituteTimeDimensionMembers = (axis: string[]) => axis.map( subDim => ( ( timeDimensions.find(td => td.dimension === subDim) && !dimensions.find(d => d === subDim) ) ? - ResultSet.timeDimensionMember(query.timeDimensions.find(td => td.dimension === subDim)) : + ResultSet.timeDimensionMember((query.timeDimensions || []).find(td => td.dimension === subDim)!) : subDim ) ); - pivotConfig.x = substituteTimeDimensionMembers(pivotConfig.x); - pivotConfig.y = substituteTimeDimensionMembers(pivotConfig.y); + normalizedPivotConfig.x = substituteTimeDimensionMembers(normalizedPivotConfig.x); + normalizedPivotConfig.y = substituteTimeDimensionMembers(normalizedPivotConfig.y); - const allIncludedDimensions = pivotConfig.x.concat(pivotConfig.y); + const allIncludedDimensions = normalizedPivotConfig.x.concat(normalizedPivotConfig.y); const allDimensions = timeDimensions.map(td => ResultSet.timeDimensionMember(td)).concat(dimensions); - const dimensionFilter = (key) => allDimensions.includes(key) || key === 'measures'; + const dimensionFilter = (key: string) => allDimensions.includes(key) || key === 'measures'; - pivotConfig.x = pivotConfig.x.concat( + normalizedPivotConfig.x = normalizedPivotConfig.x.concat( allDimensions.filter(d => !allIncludedDimensions.includes(d) && d !== 'compareDateRange') ) .filter(dimensionFilter); - pivotConfig.y = pivotConfig.y.filter(dimensionFilter); + normalizedPivotConfig.y = normalizedPivotConfig.y.filter(dimensionFilter); - if (!pivotConfig.x.concat(pivotConfig.y).find(d => d === 'measures')) { - pivotConfig.y.push('measures'); + if (!normalizedPivotConfig.x.concat(normalizedPivotConfig.y).find(d => d === 'measures')) { + normalizedPivotConfig.y.push('measures'); } - if (dimensions.includes('compareDateRange') && !pivotConfig.y.concat(pivotConfig.x).includes('compareDateRange')) { - pivotConfig.y.unshift('compareDateRange'); + if (dimensions.includes('compareDateRange') && !normalizedPivotConfig.y.concat(normalizedPivotConfig.x).includes('compareDateRange')) { + normalizedPivotConfig.y.unshift('compareDateRange'); } if (!measures.length) { - pivotConfig.x = pivotConfig.x.filter(d => d !== 'measures'); - pivotConfig.y = pivotConfig.y.filter(d => d !== 'measures'); + normalizedPivotConfig.x = normalizedPivotConfig.x.filter(d => d !== 'measures'); + normalizedPivotConfig.y = normalizedPivotConfig.y.filter(d => d !== 'measures'); } - return pivotConfig; + return normalizedPivotConfig; } - normalizePivotConfig(pivotConfig) { + public normalizePivotConfig(pivotConfig?: Partial): PivotConfig { return ResultSet.getNormalizedPivotConfig(this.loadResponse.pivotQuery, pivotConfig); } - timeSeries(timeDimension, resultIndex, annotations) { + public timeSeries(timeDimension: TimeDimension, resultIndex?: number, annotations?: Record) { if (!timeDimension.granularity) { return null; } - let { dateRange } = timeDimension; + let dateRange: DateRange | null | undefined; + dateRange = timeDimension.dateRange; if (!dateRange) { const member = ResultSet.timeDimensionMember(timeDimension); - const dates = pipe( - map(row => row[member] && internalDayjs(row[member])), - filter(Boolean) - )(this.timeDimensionBackwardCompatibleData(resultIndex)); + const rawRows: Record[] = this.timeDimensionBackwardCompatibleData(resultIndex || 0); + + const dates = rawRows + .map(row => { + const value = row[member]; + return value ? internalDayjs(value) : null; + }) + .filter((d): d is dayjs.Dayjs => Boolean(d)); dateRange = dates.length && [ - reduce(minBy(d => d.toDate()), dates[0], dates), - reduce(maxBy(d => d.toDate()), dates[0], dates) + (reduce(minBy((d: dayjs.Dayjs): Date => d.toDate()), dates[0], dates)).toString(), + (reduce(maxBy((d: dayjs.Dayjs): Date => d.toDate()), dates[0], dates)).toString(), ] || null; } @@ -313,7 +465,7 @@ class ResultSet { } const padToDay = timeDimension.dateRange ? - timeDimension.dateRange.find(d => d.match(DateRegex)) : + (timeDimension.dateRange as string[]).find(d => d.match(DateRegex)) : !['hour', 'minute', 'second'].includes(timeDimension.granularity); const [start, end] = dateRange; @@ -325,29 +477,70 @@ class ResultSet { ); } - if (!annotations[`${timeDimension.dimension}.${timeDimension.granularity}`]) { + if (!annotations?.[`${timeDimension.dimension}.${timeDimension.granularity}`]) { throw new Error(`Granularity "${timeDimension.granularity}" not found in time dimension "${timeDimension.dimension}"`); } return timeSeriesFromCustomInterval( - start, end, annotations[`${timeDimension.dimension}.${timeDimension.granularity}`].granularity + start, end, annotations[`${timeDimension.dimension}.${timeDimension.granularity}`].granularity! ); } - pivot(pivotConfig) { - pivotConfig = this.normalizePivotConfig(pivotConfig); + /** + * Base method for pivoting [ResultSet](#result-set) data. + * Most of the time shouldn't be used directly and [chartPivot](#result-set-chart-pivot) + * or [tablePivot](#table-pivot) should be used instead. + * + * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) + * ```js + * // For query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-03-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.pivot({ x: ['Stories.time'], y: ['measures'] }) will return + * [ + * { + * xValues: ["2015-01-01T00:00:00"], + * yValuesArray: [ + * [['Stories.count'], 27120] + * ] + * }, + * { + * xValues: ["2015-02-01T00:00:00"], + * yValuesArray: [ + * [['Stories.count'], 25861] + * ] + * }, + * { + * xValues: ["2015-03-01T00:00:00"], + * yValuesArray: [ + * [['Stories.count'], 29661] + * ] + * } + * ] + * ``` + * @returns An array of pivoted rows. + */ + public pivot(pivotConfig?: Partial): PivotRow[] { + const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig); const { pivotQuery: query } = this.loadResponse; const pivotImpl = (resultIndex = 0) => { - let groupByXAxis = groupByToPairs(({ xValues }) => this.axisValuesString(xValues)); + let groupByXAxis = groupByToPairs<{ xValues: string[], row: Record }, string>(({ xValues }) => this.axisValuesString(xValues)); - const measureValue = (row, measure) => row[measure] || pivotConfig.fillWithValue || 0; + const measureValue = (row: Record, measure: string) => row[measure] || normalizedPivotConfig.fillWithValue || 0; if ( - pivotConfig.fillMissingDates && - pivotConfig.x.length === 1 && + normalizedPivotConfig.fillMissingDates && + normalizedPivotConfig.x.length === 1 && (equals( - pivotConfig.x, + normalizedPivotConfig.x, (query.timeDimensions || []) .filter(td => Boolean(td.granularity)) .map(td => ResultSet.timeDimensionMember(td)) @@ -355,7 +548,7 @@ class ResultSet { ) { const series = this.loadResponses.map( (loadResponse) => this.timeSeries( - loadResponse.query.timeDimensions[0], + loadResponse.query.timeDimensions![0], resultIndex, loadResponse.annotation.timeDimensions ) ); @@ -366,21 +559,21 @@ class ResultSet { ({ xValues }) => xValues[0], rows ); - return series[resultIndex].map(d => [d, byXValues[d] || [{ xValues: [d], row: {} }]]); + return series[resultIndex]?.map(d => [d, byXValues[d] || [{ xValues: [d], row: {} }]]) ?? []; }; } } - const xGrouped = pipe( - map(row => this.axisValues(pivotConfig.x, resultIndex)(row).map(xValues => ({ xValues, row }))), + const xGrouped: [string, { xValues: string[], row: Record }[]][] = pipe( + map((row: Record) => this.axisValues(normalizedPivotConfig.x, resultIndex)(row).map(xValues => ({ xValues, row }))), unnest, groupByXAxis )(this.timeDimensionBackwardCompatibleData(resultIndex)); - const yValuesMap = {}; + const yValuesMap: Record = {}; xGrouped.forEach(([, rows]) => { rows.forEach(({ row }) => { - this.axisValues(pivotConfig.y, resultIndex)(row).forEach((values) => { + this.axisValues(normalizedPivotConfig.y, resultIndex)(row).forEach((values) => { if (Object.keys(row).length > 0) { yValuesMap[values.join()] = values; } @@ -389,14 +582,14 @@ class ResultSet { }); const allYValues = Object.values(yValuesMap); - const measureOnX = Boolean(pivotConfig.x.find(d => d === 'measures')); + const measureOnX = Boolean((normalizedPivotConfig.x).find(d => d === 'measures')); return xGrouped.map(([, rows]) => { const { xValues } = rows[0]; - const yGrouped = {}; + const yGrouped: Record = {}; rows.forEach(({ row }) => { - const arr = this.axisValues(pivotConfig.y, resultIndex)(row).map(yValues => ({ yValues, row })); + const arr = this.axisValues(normalizedPivotConfig.y, resultIndex)(row).map(yValues => ({ yValues, row })); arr.forEach((res) => { yGrouped[this.axisValuesString(res.yValues)] = res; }); @@ -421,18 +614,18 @@ class ResultSet { : []; return pivots.length - ? this.mergePivots(pivots, pivotConfig.joinDateRange) + ? this.mergePivots(pivots, normalizedPivotConfig.joinDateRange || false) : pivotImpl(); } - mergePivots(pivots, joinDateRange) { - const minLengthPivot = pivots.reduce( + private mergePivots(pivots: Pivot[][], joinDateRange: ((pivots: Pivot, joinDateRange: any) => PivotRow[]) | false): PivotRow[] { + const minLengthPivot: Pivot[] = pivots.reduce( (memo, current) => (memo != null && current.length >= memo.length ? memo : current), null - ); + ) || []; - return minLengthPivot.map((_, index) => { + return minLengthPivot.map((_: any, index: number) => { const xValues = joinDateRange - ? [pivots.map((pivot) => pivot[index] && pivot[index].xValues || []).join(', ')] + ? [pivots.map((pivot) => pivot[index]?.xValues || []).join(', ')] : minLengthPivot[index].xValues; return { @@ -442,12 +635,89 @@ class ResultSet { }); } - pivotedRows(pivotConfig) { // TODO - return this.chartPivot(pivotConfig); - } - - chartPivot(pivotConfig) { - const validate = (value) => { + /** + * Returns normalized query result data in the following format. + * + * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) + * ```js + * // For the query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.chartPivot() will return + * [ + * { "x":"2015-01-01T00:00:00", "Stories.count": 27120, "xValues": ["2015-01-01T00:00:00"] }, + * { "x":"2015-02-01T00:00:00", "Stories.count": 25861, "xValues": ["2015-02-01T00:00:00"] }, + * { "x":"2015-03-01T00:00:00", "Stories.count": 29661, "xValues": ["2015-03-01T00:00:00"] }, + * //... + * ] + * + * ``` + * When using `chartPivot()` or `seriesNames()`, you can pass `aliasSeries` in the [pivotConfig](#types-pivot-config) + * to give each series a unique prefix. This is useful for `blending queries` which use the same measure multiple times. + * + * ```js + * // For the queries + * { + * measures: ['Stories.count'], + * timeDimensions: [ + * { + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month', + * }, + * ], + * }, + * { + * measures: ['Stories.count'], + * timeDimensions: [ + * { + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month', + * }, + * ], + * filters: [ + * { + * member: 'Stores.read', + * operator: 'equals', + * value: ['true'], + * }, + * ], + * }, + * + * // ResultSet.chartPivot({ aliasSeries: ['one', 'two'] }) will return + * [ + * { + * x: '2015-01-01T00:00:00', + * 'one,Stories.count': 27120, + * 'two,Stories.count': 8933, + * xValues: ['2015-01-01T00:00:00'], + * }, + * { + * x: '2015-02-01T00:00:00', + * 'one,Stories.count': 25861, + * 'two,Stories.count': 8344, + * xValues: ['2015-02-01T00:00:00'], + * }, + * { + * x: '2015-03-01T00:00:00', + * 'one,Stories.count': 29661, + * 'two,Stories.count': 9023, + * xValues: ['2015-03-01T00:00:00'], + * }, + * //... + * ] + * ``` + */ + public chartPivot(pivotConfig?: Partial): ChartPivotRow[] { + const validate = (value: string) => { if (this.parseDateMeasures && LocalDateRegex.test(value)) { return new Date(value); } else if (!Number.isNaN(Number.parseFloat(value))) { @@ -457,17 +727,17 @@ class ResultSet { return value; }; - const duplicateMeasures = new Set(); + const duplicateMeasures = new Set(); if (this.queryType === QUERY_TYPE.BLENDING_QUERY) { - const allMeasures = flatten(this.loadResponses.map(({ query }) => query.measures)); + const allMeasures = flatten(this.loadResponses.map(({ query }) => query.measures ?? [])); allMeasures.filter((e, i, a) => a.indexOf(e) !== i).forEach(m => duplicateMeasures.add(m)); } return this.pivot(pivotConfig).map(({ xValues, yValuesArray }) => { - const yValuesMap = {}; + const yValuesMap: Record = {}; yValuesArray - .forEach(([yValues, m], i) => { + .forEach(([yValues, m]: [string[], string], i: number) => { yValuesMap[this.axisValuesString(aliasSeries(yValues, i, pivotConfig, duplicateMeasures), ',')] = m && validate(m); }); @@ -475,36 +745,168 @@ class ResultSet { x: this.axisValuesString(xValues, ','), xValues, ...yValuesMap - }); + } as ChartPivotRow); }); } - tablePivot(pivotConfig) { + /** + * Returns normalized query result data prepared for visualization in the table format. + * + * You can find the examples of using the `pivotConfig` [here](#types-pivot-config) + * + * For example: + * ```js + * // For the query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.tablePivot() will return + * [ + * { "Stories.time": "2015-01-01T00:00:00", "Stories.count": 27120 }, + * { "Stories.time": "2015-02-01T00:00:00", "Stories.count": 25861 }, + * { "Stories.time": "2015-03-01T00:00:00", "Stories.count": 29661 }, + * //... + * ] + * ``` + * @returns An array of pivoted rows + */ + public tablePivot(pivotConfig?: Partial): Array<{ [key: string]: string | number | boolean }> { const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig || {}); const isMeasuresPresent = normalizedPivotConfig.x.concat(normalizedPivotConfig.y).includes('measures'); return this.pivot(normalizedPivotConfig).map(({ xValues, yValuesArray }) => fromPairs( - normalizedPivotConfig.x - .map((key, index) => [key, xValues[index]]) - .concat( - isMeasuresPresent ? yValuesArray.map(([yValues, measure]) => [ + [ + ...(normalizedPivotConfig.x).map((key, index): [string, string | number] => [ + key, + xValues[index] ?? '' + ]), + ...(isMeasuresPresent + ? yValuesArray.map(([yValues, measure]): [string, string | number] => [ yValues.length ? yValues.join() : 'value', measure - ]) : [] - ) + ]) + : []) + ] )); } - tableColumns(pivotConfig) { + /** + * Returns an array of column definitions for `tablePivot`. + * + * For example: + * ```js + * // For the query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.tableColumns() will return + * [ + * { + * key: 'Stories.time', + * dataIndex: 'Stories.time', + * title: 'Stories Time', + * shortTitle: 'Time', + * type: 'time', + * format: undefined, + * }, + * { + * key: 'Stories.count', + * dataIndex: 'Stories.count', + * title: 'Stories Count', + * shortTitle: 'Count', + * type: 'count', + * format: undefined, + * }, + * //... + * ] + * ``` + * + * In case we want to pivot the table axes + * ```js + * // Let's take this query as an example + * { + * measures: ['Orders.count'], + * dimensions: ['Users.country', 'Users.gender'] + * } + * + * // and put the dimensions on `y` axis + * resultSet.tableColumns({ + * x: [], + * y: ['Users.country', 'Users.gender', 'measures'] + * }) + * ``` + * + * then `tableColumns` will group the table head and return + * ```js + * { + * key: 'Germany', + * type: 'string', + * title: 'Users Country Germany', + * shortTitle: 'Germany', + * meta: undefined, + * format: undefined, + * children: [ + * { + * key: 'male', + * type: 'string', + * title: 'Users Gender male', + * shortTitle: 'male', + * meta: undefined, + * format: undefined, + * children: [ + * { + * // ... + * dataIndex: 'Germany.male.Orders.count', + * shortTitle: 'Count', + * }, + * ], + * }, + * { + * // ... + * shortTitle: 'female', + * children: [ + * { + * // ... + * dataIndex: 'Germany.female.Orders.count', + * shortTitle: 'Count', + * }, + * ], + * }, + * ], + * }, + * // ... + * ``` + * @returns An array of columns + */ + public tableColumns(pivotConfig?: PivotConfig): TableColumn[] { const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig || {}); - const annotations = pipe( - pluck('annotation'), - reduce(mergeDeepLeft(), {}) - )(this.loadResponses); + + const annotations: QueryAnnotations = this.loadResponses + .map((r) => r.annotation) + .reduce((acc, annotation) => mergeDeepLeft(acc, annotation) as QueryAnnotations, + { + dimensions: {}, + measures: {}, + timeDimensions: {}, + segments: {}, + }); + const flatMeta = Object.values(annotations).reduce((a, b) => ({ ...a, ...b }), {}); - const schema = {}; + const schema: Record = {}; - const extractFields = (key) => { + const extractFields = (key: string) => { const { title, shortTitle, type, format, meta } = flatMeta[key] || {}; return { @@ -519,7 +921,7 @@ class ResultSet { const pivot = this.pivot(normalizedPivotConfig); - (pivot[0] && pivot[0].yValuesArray || []).forEach(([yValues]) => { + (pivot[0]?.yValuesArray || []).forEach(([yValues]) => { if (yValues.length > 0) { let currentItem = schema; @@ -529,7 +931,7 @@ class ResultSet { memberId: normalizedPivotConfig.y[index] === 'measures' ? value : normalizedPivotConfig.y[index], - children: (currentItem[`_${value}`] && currentItem[`_${value}`].children) || {} + children: currentItem[`_${value}`]?.children || {} }; currentItem = currentItem[`_${value}`].children; @@ -537,7 +939,7 @@ class ResultSet { } }); - const toColumns = (item = {}, path = []) => { + const toColumns = (item: Record = {}, path: string[] = []): TableColumn[] => { if (Object.keys(item).length === 0) { return []; } @@ -559,7 +961,7 @@ class ResultSet { dataIndex: [...path, key].join(), title: [title, dimensionValue].join(' ').trim(), shortTitle: dimensionValue || shortTitle, - }; + } as TableColumn; } return { @@ -568,11 +970,11 @@ class ResultSet { title: [title, dimensionValue].join(' ').trim(), shortTitle: dimensionValue || shortTitle, children, - }; + } as TableColumn; }); }; - let otherColumns = []; + let otherColumns: TableColumn[] = []; if (!pivot.length && normalizedPivotConfig.y.includes('measures')) { otherColumns = (this.loadResponses[0].query.measures || []).map( @@ -580,7 +982,7 @@ class ResultSet { ); } - // Syntatic column to display the measure value + // Synthetic column to display the measure value if (!normalizedPivotConfig.y.length && normalizedPivotConfig.x.includes('measures')) { otherColumns.push({ key: 'value', @@ -591,58 +993,81 @@ class ResultSet { }); } - return normalizedPivotConfig.x - .map((key) => { - if (key === 'measures') { - return { - key: 'measures', - dataIndex: 'measures', - title: 'Measures', - shortTitle: 'Measures', - type: 'string', - }; - } + return (normalizedPivotConfig.x).map((key) => { + if (key === 'measures') { + return { + key: 'measures', + dataIndex: 'measures', + title: 'Measures', + shortTitle: 'Measures', + type: 'string', + } as TableColumn; + } - return ({ ...extractFields(key), dataIndex: key }); - }) + return ({ ...extractFields(key), dataIndex: key }); + }) .concat(toColumns(schema)) .concat(otherColumns); } - totalRow(pivotConfig) { + public totalRow(pivotConfig?: PivotConfig): ChartPivotRow { return this.chartPivot(pivotConfig)[0]; } - categories(pivotConfig) { // TODO + public categories(pivotConfig?: PivotConfig): ChartPivotRow[] { return this.chartPivot(pivotConfig); } - seriesNames(pivotConfig) { - pivotConfig = this.normalizePivotConfig(pivotConfig); - const measures = pipe( - pluck('annotation'), - pluck('measures'), - mergeAll - )(this.loadResponses); + /** + * Returns an array of series objects, containing `key` and `title` parameters. + * ```js + * // For query + * { + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * } + * + * // ResultSet.seriesNames() will return + * [ + * { + * key: 'Stories.count', + * title: 'Stories Count', + * shortTitle: 'Count', + * yValues: ['Stories.count'], + * }, + * ] + * ``` + * @returns An array of series names + */ + public seriesNames(pivotConfig?: Partial): SeriesNamesColumn[] { + const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig); + const measures = this.loadResponses + .map(r => r.annotation.measures) + .reduce((acc, m) => ({ ...acc, ...m }), {}); const seriesNames = unnest(this.loadResponses.map((_, index) => pipe( - map(this.axisValues(pivotConfig.y, index)), + map(this.axisValues(normalizedPivotConfig.y, index)), unnest, uniq )( this.timeDimensionBackwardCompatibleData(index) ))); - const duplicateMeasures = new Set(); + + const duplicateMeasures = new Set(); if (this.queryType === QUERY_TYPE.BLENDING_QUERY) { - const allMeasures = flatten(this.loadResponses.map(({ query }) => query.measures)); + const allMeasures = flatten(this.loadResponses.map(({ query }) => query.measures ?? [])); allMeasures.filter((e, i, a) => a.indexOf(e) !== i).forEach(m => duplicateMeasures.add(m)); } return seriesNames.map((axisValues, i) => { - const aliasedAxis = aliasSeries(axisValues, i, pivotConfig, duplicateMeasures); + const aliasedAxis = aliasSeries(axisValues, i, normalizedPivotConfig, duplicateMeasures); return { title: this.axisValuesString( - pivotConfig.y.find(d => d === 'measures') ? + normalizedPivotConfig.y.find(d => d === 'measures') ? dropLast(1, aliasedAxis).concat( measures[ ResultSet.measureFromAxis(axisValues) @@ -651,7 +1076,7 @@ class ResultSet { aliasedAxis, ', ' ), shortTitle: this.axisValuesString( - pivotConfig.y.find(d => d === 'measures') ? + normalizedPivotConfig.y.find(d => d === 'measures') ? dropLast(1, aliasedAxis).concat( measures[ ResultSet.measureFromAxis(axisValues) @@ -665,7 +1090,7 @@ class ResultSet { }); } - query() { + public query(): Query { if (this.queryType !== QUERY_TYPE.REGULAR_QUERY) { throw new Error(`Method is not supported for a '${this.queryType}' query type. Please use decompose`); } @@ -673,15 +1098,18 @@ class ResultSet { return this.loadResponses[0].query; } - pivotQuery() { + public pivotQuery(): PivotQuery { return this.loadResponse.pivotQuery || null; } - totalRows() { + /** + * @return the total number of rows if the `total` option was set, when sending the query + */ + public totalRows(): number | null | undefined { return this.loadResponses[0].total; } - rawData() { + public rawData(): T[] { if (this.queryType !== QUERY_TYPE.REGULAR_QUERY) { throw new Error(`Method is not supported for a '${this.queryType}' query type. Please use decompose`); } @@ -689,7 +1117,7 @@ class ResultSet { return this.loadResponses[0].data; } - annotation() { + public annotation(): QueryAnnotations { if (this.queryType !== QUERY_TYPE.REGULAR_QUERY) { throw new Error(`Method is not supported for a '${this.queryType}' query type. Please use decompose`); } @@ -697,7 +1125,7 @@ class ResultSet { return this.loadResponses[0].annotation; } - timeDimensionBackwardCompatibleData(resultIndex) { + private timeDimensionBackwardCompatibleData(resultIndex: number) { if (resultIndex === undefined) { throw new Error('resultIndex is required'); } @@ -712,10 +1140,12 @@ class ResultSet { ...( fromPairs(Object.keys(row) .filter( - field => timeDimensions.find(d => d.dimension === field) && - !row[ResultSet.timeDimensionMember(timeDimensions.find(d => d.dimension === field))] + field => { + const foundTd = timeDimensions.find(d => d.dimension === field); + return foundTd && !row[ResultSet.timeDimensionMember(foundTd)]; + } ).map(field => ( - [ResultSet.timeDimensionMember(timeDimensions.find(d => d.dimension === field)), row[field]] + [ResultSet.timeDimensionMember(timeDimensions.find(d => d.dimension === field)!), row[field]] ))) ) } @@ -725,7 +1155,15 @@ class ResultSet { return this.backwardCompatibleData[resultIndex]; } - decompose() { + /** + * Can be used when you need access to the methods that can't be used with some query types (eg `compareDateRangeQuery` or `blendingQuery`) + * ```js + * resultSet.decompose().forEach((currentResultSet) => { + * console.log(currentResultSet.rawData()); + * }); + * ``` + */ + public decompose(): ResultSet[] { return this.loadResponses.map((result) => new ResultSet({ queryType: QUERY_TYPE.REGULAR_QUERY, pivotQuery: { @@ -736,11 +1174,12 @@ class ResultSet { }, this.options)); } - serialize() { + /** + * Can be used to stash the `ResultSet` in a storage and restored later with [deserialize](#result-set-deserialize) + */ + public serialize(): SerializedResult { return { loadResponse: clone(this.loadResponse) }; } } - -export default ResultSet; diff --git a/packages/cubejs-client-core/src/SqlQuery.ts b/packages/cubejs-client-core/src/SqlQuery.ts index 9a9a2b6ea84ba..2c450d311ca8c 100644 --- a/packages/cubejs-client-core/src/SqlQuery.ts +++ b/packages/cubejs-client-core/src/SqlQuery.ts @@ -1,13 +1,27 @@ +export type SqlQueryTuple = [string, any[], any]; + +export type SqlData = { + aliasNameToMember: Record; + cacheKeyQueries: SqlQueryTuple[]; + dataSource: boolean; + external: boolean; + sql: SqlQueryTuple; + preAggregations: any[]; + rollupMatchResults: any[]; +}; + export default class SqlQuery { - constructor(sqlQuery) { + private readonly sqlQuery: SqlData; + + public constructor(sqlQuery: SqlData) { this.sqlQuery = sqlQuery; } - rawQuery() { - return this.sqlQuery.sql; + public rawQuery(): SqlData { + return this.sqlQuery; } - sql() { + public sql(): string { return this.rawQuery().sql[0]; } } diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index 80820c3efb686..bb80a34a938e1 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -3,42 +3,205 @@ import ResultSet from './ResultSet'; import SqlQuery from './SqlQuery'; import Meta from './Meta'; import ProgressResult from './ProgressResult'; -import HttpTransport from './HttpTransport'; +import HttpTransport, { ErrorResponse, ITransport, TransportOptions } from './HttpTransport'; import RequestError from './RequestError'; +import { + ExtractTimeMembers, + LoadResponse, + MetaResponse, + PivotQuery, + ProgressResponse, + Query, + QueryOrder, + QueryType, + TransformedQuery +} from './types'; + +export type LoadMethodCallback = (error: Error | null, resultSet: T) => void; + +export type LoadMethodOptions = { + /** + * Key to store the current request's MUTEX inside the `mutexObj`. MUTEX object is used to reject orphaned queries results when new queries are sent. For example: if two queries are sent with the same `mutexKey` only the last one will return results. + */ + mutexKey?: string; + /** + * Object to store MUTEX + */ + mutexObj?: { [key: string]: any }; + /** + * Pass `true` to use continuous fetch behavior. + */ + subscribe?: boolean; + /** + * A Cube API instance. If not provided will be taken from `CubeProvider` + */ + // eslint-disable-next-line no-use-before-define + cubeApi?: CubeApi; + /** + * If enabled, all members of the 'number' type will be automatically converted to numerical values on the client side + */ + castNumerics?: boolean; + /** + * Function that receives `ProgressResult` on each `Continue wait` message. + */ + progressCallback?(result: ProgressResult): void; +}; -let mutexCounter = 0; +export type DeeplyReadonly = { + readonly [K in keyof T]: DeeplyReadonly; +}; -const MUTEX_ERROR = 'Mutex has been changed'; +export type ExtractMembers> = + | (T extends { dimensions: readonly (infer Names)[]; } ? Names : never) + | (T extends { measures: readonly (infer Names)[]; } ? Names : never) + | (T extends { timeDimensions: (infer U); } ? ExtractTimeMembers : never); + +// If we can't infer any members at all, then return any. +export type SingleQueryRecordType> = ExtractMembers extends never + ? any + : { [K in string & ExtractMembers]: string | number | boolean | null }; + +export type QueryArrayRecordType> = + T extends readonly [infer First, ...infer Rest] + ? SingleQueryRecordType> | QueryArrayRecordType> + : never; + +export type QueryRecordType> = + T extends DeeplyReadonly ? QueryArrayRecordType : + T extends DeeplyReadonly ? SingleQueryRecordType : + never; + +export interface UnsubscribeObj { + /** + * Allows to stop requests in-flight in long polling or web socket subscribe loops. + * It doesn't cancel any submitted requests to the underlying databases. + */ + unsubscribe(): Promise; +} /** - * Query result dataset formats enum. + * @deprecated use DryRunResponse */ -const ResultType = { - DEFAULT: 'default', - COMPACT: 'compact' +export type TDryRunResponse = { + queryType: QueryType; + normalizedQueries: Query[]; + pivotQuery: PivotQuery; + queryOrder: Array<{ [k: string]: QueryOrder }>; + transformedQueries: TransformedQuery[]; +}; + +export type DryRunResponse = { + queryType: QueryType; + normalizedQueries: Query[]; + pivotQuery: PivotQuery; + queryOrder: Array<{ [k: string]: QueryOrder }>; + transformedQueries: TransformedQuery[]; }; -function mutexPromise(promise) { - return new Promise(async (resolve, reject) => { - try { - resolve(await promise); - } catch (error) { +interface BodyResponse { + error?: string; + [key: string]: any; +} + +let mutexCounter = 0; + +const MUTEX_ERROR = 'Mutex has been changed'; + +function mutexPromise(promise: Promise) { + return promise + .then((result) => result) + .catch((error) => { if (error !== MUTEX_ERROR) { - reject(error); + throw error; } - } - }); + }); } +export type ResponseFormat = 'compact' | 'default' | undefined; + +export type CubeApiOptions = { + /** + * URL of your Cube.js Backend. By default, in the development environment it is `http://localhost:4000/cubejs-api/v1` + */ + apiUrl: string; + /** + * Transport implementation to use. [HttpTransport](#http-transport) will be used by default. + */ + transport?: ITransport; + method?: TransportOptions['method']; + headers?: TransportOptions['headers']; + pollInterval?: number; + credentials?: TransportOptions['credentials']; + parseDateMeasures?: boolean; + resType?: 'default' | 'compact'; + castNumerics?: boolean; + /** + * How many network errors would be retried before returning to users. Default to 0. + */ + networkErrorRetries?: number; +}; + +/** + * Main class for accessing Cube API + */ class CubeApi { - constructor(apiToken, options) { - if (apiToken !== null && !Array.isArray(apiToken) && typeof apiToken === 'object') { + apiToken: string | (() => Promise) | (CubeApiOptions & any[]) | undefined; + + apiUrl: string; + + method: TransportOptions['method']; + + headers: TransportOptions['headers']; + + credentials: TransportOptions['credentials']; + + transport: ITransport; + + pollInterval: number; + + parseDateMeasures: boolean | undefined; + + castNumerics: boolean; + + networkErrorRetries: number; + + updateAuthorizationPromise: Promise | null; + + public constructor(apiToken: string | (() => Promise) | undefined, options: CubeApiOptions); + + public constructor(options: CubeApiOptions); + + /** + * Creates an instance of the `CubeApi`. The API entry point. + * + * ```js + * import cube from '@cubejs-client/core'; + * const cubeApi = cube( + * 'CUBE-API-TOKEN', + * { apiUrl: 'http://localhost:4000/cubejs-api/v1' } + * ); + * ``` + * + * You can also pass an async function or a promise that will resolve to the API token + * + * ```js + * import cube from '@cubejs-client/core'; + * const cubeApi = cube( + * async () => await Auth.getJwtToken(), + * { apiUrl: 'http://localhost:4000/cubejs-api/v1' } + * ); + * ``` + */ + public constructor( + apiToken: string | (() => Promise) | undefined | CubeApiOptions, + options?: CubeApiOptions + ) { + if (apiToken && !Array.isArray(apiToken) && typeof apiToken === 'object') { options = apiToken; apiToken = undefined; } - options = options || {}; - if (!options.transport && !options.apiUrl) { + if (!options || (!options.transport && !options.apiUrl)) { throw new Error('The `apiUrl` option is required'); } @@ -47,8 +210,9 @@ class CubeApi { this.method = options.method; this.headers = options.headers || {}; this.credentials = options.credentials; + this.transport = options.transport || new HttpTransport({ - authorization: typeof apiToken === 'function' ? undefined : apiToken, + authorization: typeof apiToken === 'string' ? apiToken : undefined, apiUrl: this.apiUrl, method: this.method, headers: this.headers, @@ -56,6 +220,7 @@ class CubeApi { fetchTimeout: options.fetchTimeout, signal: options.signal }); + this.pollInterval = options.pollInterval || 5; this.parseDateMeasures = options.parseDateMeasures; this.castNumerics = typeof options.castNumerics === 'boolean' ? options.castNumerics : false; @@ -64,14 +229,14 @@ class CubeApi { this.updateAuthorizationPromise = null; } - request(method, params) { + private request(method: string, params?: any) { return this.transport.request(method, { baseRequestId: uuidv4(), ...params }); } - loadMethod(request, toResult, options, callback) { + private loadMethod(request: CallableFunction, toResult: CallableFunction, options?: LoadMethodOptions, callback?: CallableFunction) { const mutexValue = ++mutexCounter; if (typeof options === 'function' && !callback) { callback = options; @@ -95,10 +260,7 @@ class CubeApi { const checkMutex = async () => { const requestInstance = await requestPromise; - if ( - options.mutexObj && - options.mutexObj[mutexKey] !== mutexValue - ) { + if (options?.mutexObj?.[mutexKey] !== mutexValue) { unsubscribed = true; if (requestInstance.unsubscribe) { await requestInstance.unsubscribe(); @@ -109,47 +271,48 @@ class CubeApi { let networkRetries = this.networkErrorRetries; - const loadImpl = async (response, next) => { + const loadImpl = async (response: Response | ErrorResponse, next: CallableFunction) => { const requestInstance = await requestPromise; const subscribeNext = async () => { - if (options.subscribe && !unsubscribed) { + if (options?.subscribe && !unsubscribed) { if (requestInstance.unsubscribe) { return next(); } else { - await new Promise(resolve => setTimeout(() => resolve(), this.pollInterval * 1000)); + await new Promise(resolve => setTimeout(() => resolve(), this.pollInterval * 1000)); return next(); } } return null; }; - const continueWait = async (wait) => { + const continueWait = async (wait: boolean = false) => { if (!unsubscribed) { if (wait) { - await new Promise(resolve => setTimeout(() => resolve(), this.pollInterval * 1000)); + await new Promise(resolve => setTimeout(() => resolve(), this.pollInterval * 1000)); } return next(); } return null; }; - if (options.subscribe && !skipAuthorizationUpdate) { + if (options?.subscribe && !skipAuthorizationUpdate) { await this.updateTransportAuthorization(); } skipAuthorizationUpdate = false; - if (response.status === 502 || - response.error && - response.error.toLowerCase() === 'network error' && + if (('status' in response && response.status === 502) || + ('error' in response && response.error?.toLowerCase() === 'network error') && --networkRetries >= 0 ) { await checkMutex(); return continueWait(true); } - let body = {}; + // From here we're sure that response is only fetch Response + response = (response as Response); + let body: BodyResponse = {}; let text = ''; try { text = await response.text(); @@ -160,19 +323,19 @@ class CubeApi { if (body.error === 'Continue wait') { await checkMutex(); - if (options.progressCallback) { - options.progressCallback(new ProgressResult(body)); + if (options?.progressCallback) { + options.progressCallback(new ProgressResult(body as ProgressResponse)); } return continueWait(); } if (response.status !== 200) { await checkMutex(); - if (!options.subscribe && requestInstance.unsubscribe) { + if (!options?.subscribe && requestInstance.unsubscribe) { await requestInstance.unsubscribe(); } - const error = new RequestError(body.error, body, response.status); // TODO error class + const error = new RequestError(body.error || '', body, response.status); if (callback) { callback(error); } else { @@ -182,7 +345,7 @@ class CubeApi { return subscribeNext(); } await checkMutex(); - if (!options.subscribe && requestInstance.unsubscribe) { + if (!options?.subscribe && requestInstance.unsubscribe) { await requestInstance.unsubscribe(); } const result = toResult(body); @@ -214,46 +377,43 @@ class CubeApi { } } - async updateTransportAuthorization() { + private async updateTransportAuthorization() { if (this.updateAuthorizationPromise) { await this.updateAuthorizationPromise; return; } - if (typeof this.apiToken === 'function') { - this.updateAuthorizationPromise = new Promise(async (resolve, reject) => { + const tokenFetcher = this.apiToken; + + if (typeof tokenFetcher === 'function') { + const promise = (async () => { try { - const token = await this.apiToken(); + const token = await tokenFetcher(); + if (this.transport.authorization !== token) { this.transport.authorization = token; } - resolve(); - } catch (error) { - reject(error); } finally { this.updateAuthorizationPromise = null; } - }); + })(); - await this.updateAuthorizationPromise; + this.updateAuthorizationPromise = promise; + await promise; } } /** * Add system properties to a query object. - * @param {Query} query - * @param {string} responseFormat - * @returns {void} - * @private */ - patchQueryInternal(query, responseFormat) { + private patchQueryInternal(query: DeeplyReadonly, responseFormat: ResponseFormat): DeeplyReadonly { if ( - responseFormat === ResultType.COMPACT && - query.responseFormat !== ResultType.COMPACT + responseFormat === 'compact' && + query.responseFormat !== 'compact' ) { return { ...query, - responseFormat: ResultType.COMPACT, + responseFormat: 'compact', }; } else { return query; @@ -263,26 +423,22 @@ class CubeApi { /** * Process result fetched from the gateway#load method according * to the network protocol. - * @param {*} response - * @returns ResultSet - * @private */ - loadResponseInternal(response, options = {}) { + protected loadResponseInternal(response: LoadResponse, options: LoadMethodOptions | null = {}): ResultSet { if ( response.results.length ) { - if (options.castNumerics) { + if (options?.castNumerics) { response.results.forEach((result) => { const numericMembers = Object.entries({ ...result.annotation.measures, ...result.annotation.dimensions, - }).map(([k, v]) => { + }).reduce((acc, [k, v]) => { if (v.type === 'number') { - return k; + acc.push(k); } - - return undefined; - }).filter(Boolean); + return acc; + }, []); result.data = result.data.map((row) => { numericMembers.forEach((key) => { @@ -297,12 +453,13 @@ class CubeApi { } if (response.results[0].query.responseFormat && - response.results[0].query.responseFormat === ResultType.COMPACT) { + response.results[0].query.responseFormat === 'compact') { response.results.forEach((result, j) => { - const data = []; - result.data.dataset.forEach((r) => { - const row = {}; - result.data.members.forEach((m, i) => { + const data: Record[] = []; + const { dataset, members } = result.data as unknown as { dataset: any[]; members: string[] }; + dataset.forEach((r) => { + const row: Record = {}; + members.forEach((m, i) => { row[m] = r[i]; }); data.push(row); @@ -317,94 +474,197 @@ class CubeApi { }); } - load(query, options, callback, responseFormat = ResultType.DEFAULT) { - options = { - castNumerics: this.castNumerics, - ...options - }; + public load>( + query: QueryType, + options?: LoadMethodOptions, + ): Promise>>; - if (responseFormat === ResultType.COMPACT) { - if (Array.isArray(query)) { - query = query.map((q) => this.patchQueryInternal(q, ResultType.COMPACT)); - } else { - query = this.patchQueryInternal(query, ResultType.COMPACT); - } - } + public load>( + query: QueryType, + options?: LoadMethodOptions, + callback?: LoadMethodCallback>>, + ): UnsubscribeObj; + + public load>( + query: QueryType, + options?: LoadMethodOptions, + callback?: LoadMethodCallback>, + responseFormat?: string + ): Promise>>; + + /** + * Fetch data for the passed `query`. + * + * ```js + * import cube from '@cubejs-client/core'; + * import Chart from 'chart.js'; + * import chartjsConfig from './toChartjsData'; + * + * const cubeApi = cube('CUBEJS_TOKEN'); + * + * const resultSet = await cubeApi.load({ + * measures: ['Stories.count'], + * timeDimensions: [{ + * dimension: 'Stories.time', + * dateRange: ['2015-01-01', '2015-12-31'], + * granularity: 'month' + * }] + * }); + * + * const context = document.getElementById('myChart'); + * new Chart(context, chartjsConfig(resultSet)); + * ``` + * @param query - [Query object](/product/apis-integrations/rest-api/query-format) + * @param options + * @param callback + * @param responseFormat + */ + public load>(query: QueryType, options?: LoadMethodOptions, callback?: CallableFunction, responseFormat: ResponseFormat = 'default') { + [query, options] = this.prepareQueryOptions(query, options, responseFormat); return this.loadMethod( () => this.request('load', { query, queryType: 'multi', signal: options.signal }), - (response) => this.loadResponseInternal(response, options), + (response: any) => this.loadResponseInternal(response, options), options, callback ); } - subscribe(query, options, callback, responseFormat = ResultType.DEFAULT) { + private prepareQueryOptions>(query: QueryType, options?: LoadMethodOptions | null, responseFormat: ResponseFormat = 'default'): [query: QueryType, options: LoadMethodOptions] { options = { castNumerics: this.castNumerics, ...options }; - if (responseFormat === ResultType.COMPACT) { + if (responseFormat === 'compact') { if (Array.isArray(query)) { - query = query.map((q) => this.patchQueryInternal(q, ResultType.COMPACT)); + const patched = query.map((q) => this.patchQueryInternal(q, 'compact')); + return [patched as unknown as QueryType, options]; } else { - query = this.patchQueryInternal(query, ResultType.COMPACT); + const patched = this.patchQueryInternal(query as DeeplyReadonly, 'compact'); + return [patched as QueryType, options]; } } + + return [query, options]; + } + + /** + * Allows you to fetch data and receive updates over time. See [Real-Time Data Fetch](/product/apis-integrations/rest-api/real-time-data-fetch) + * + * ```js + * // Subscribe to a query's updates + * const subscription = await cubeApi.subscribe( + * { + * measures: ['Logs.count'], + * timeDimensions: [ + * { + * dimension: 'Logs.time', + * granularity: 'hour', + * dateRange: 'last 1440 minutes', + * }, + * ], + * }, + * options, + * (error, resultSet) => { + * if (!error) { + * // handle the update + * } + * } + * ); + * + * // Unsubscribe from a query's updates + * subscription.unsubscribe(); + * ``` + */ + public subscribe>( + query: QueryType, + options: LoadMethodOptions | null, + callback: LoadMethodCallback>>, + responseFormat: ResponseFormat = 'default' + ): UnsubscribeObj { + [query, options] = this.prepareQueryOptions(query, options, responseFormat); return this.loadMethod( () => this.request('subscribe', { query, queryType: 'multi', signal: options.signal }), - (response) => this.loadResponseInternal(response, options), + (response: any) => this.loadResponseInternal(response, options), { ...options, subscribe: true }, callback - ); + ) as UnsubscribeObj; } - sql(query, options, callback) { + public sql(query: DeeplyReadonly, options?: LoadMethodOptions): Promise; + + public sql(query: DeeplyReadonly, options?: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; + + /** + * Get generated SQL string for the given `query`. + */ + public sql(query: DeeplyReadonly, options?: LoadMethodOptions, callback?: LoadMethodCallback): Promise | UnsubscribeObj { return this.loadMethod( () => this.request('sql', { query, signal: options?.signal }), - (response) => (Array.isArray(response) ? response.map((body) => new SqlQuery(body)) : new SqlQuery(response)), + (response: any) => (Array.isArray(response) ? response.map((body) => new SqlQuery(body)) : new SqlQuery(response)), options, callback ); } - meta(options, callback) { + public meta(options?: LoadMethodOptions): Promise; + + public meta(options?: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; + + /** + * Get meta description of cubes available for querying. + */ + public meta(options?: LoadMethodOptions, callback?: LoadMethodCallback): Promise | UnsubscribeObj { return this.loadMethod( () => this.request('meta', { signal: options?.signal }), - (body) => new Meta(body), + (body: MetaResponse) => new Meta(body), options, callback ); } - dryRun(query, options, callback) { + public dryRun(query: DeeplyReadonly, options?: LoadMethodOptions): Promise; + + public dryRun(query: DeeplyReadonly, options: LoadMethodOptions, callback?: LoadMethodCallback): UnsubscribeObj; + + /** + * Get query related meta without query execution + */ + public dryRun(query: DeeplyReadonly, options?: LoadMethodOptions, callback?: LoadMethodCallback): Promise | UnsubscribeObj { return this.loadMethod( () => this.request('dry-run', { query, signal: options?.signal }), - (response) => response, + (response: DryRunResponse) => response, options, callback ); } } -export default (apiToken, options) => new CubeApi(apiToken, options); +export default (apiToken: string | (() => Promise), options: CubeApiOptions) => new CubeApi(apiToken, options); -export { CubeApi, HttpTransport, ResultSet, RequestError, Meta }; +export { CubeApi }; +export * from './Meta'; +export * from './SqlQuery'; +export * from './RequestError'; +export * from './ProgressResult'; +export { default as ResultSet } from './ResultSet'; +export * from './HttpTransport'; export * from './utils'; export * from './time'; +export * from './types'; diff --git a/packages/cubejs-client-core/src/index.umd.ts b/packages/cubejs-client-core/src/index.umd.ts index 635c3171426e4..c9906e017b67f 100644 --- a/packages/cubejs-client-core/src/index.umd.ts +++ b/packages/cubejs-client-core/src/index.umd.ts @@ -1,8 +1,9 @@ -import cube from './index'; -import * as clientCoreExports from './index'; +import cube, * as clientCoreExports from './index'; + +const cubeAll: any = cube; Object.keys(clientCoreExports).forEach((key) => { - cube[key] = clientCoreExports[key]; + cubeAll[key] = (clientCoreExports as Record)[key]; }); -export default cube; +export default cubeAll; diff --git a/packages/cubejs-client-core/src/time.ts b/packages/cubejs-client-core/src/time.ts index 3ac4e8e01762c..94b5144d134e7 100644 --- a/packages/cubejs-client-core/src/time.ts +++ b/packages/cubejs-client-core/src/time.ts @@ -8,7 +8,42 @@ dayjs.extend(quarterOfYear); dayjs.extend(duration); dayjs.extend(isoWeek); -export const GRANULARITIES = [ +export type SqlInterval = string; + +// TODO: Define a better type as unitOfTime.DurationConstructor in moment.js +export type ParsedInterval = Record; + +export type Granularity = { + interval: SqlInterval; + origin?: string; + offset?: SqlInterval; +}; + +export type DayRange = { + by: (value: any) => dayjs.Dayjs[]; + snapTo: (value: any) => DayRange; + start: dayjs.Dayjs; + end: dayjs.Dayjs; +}; + +export type TimeDimensionPredefinedGranularity = + 'second' + | 'minute' + | 'hour' + | 'day' + | 'week' + | 'month' + | 'quarter' + | 'year'; + +export type TimeDimensionGranularity = TimeDimensionPredefinedGranularity | string; + +export type TGranularityMap = { + name: TimeDimensionGranularity | undefined; + title: string; +}; + +export const GRANULARITIES: TGranularityMap[] = [ { name: undefined, title: 'w/o grouping' }, { name: 'second', title: 'Second' }, { name: 'minute', title: 'Minute' }, @@ -24,9 +59,9 @@ export const DEFAULT_GRANULARITY = 'day'; // When granularity is week, weekStart Value must be 1. However, since the client can change it globally // (https://day.js.org/docs/en/i18n/changing-locale) So the function below has been added. -export const internalDayjs = (...args) => dayjs(...args).locale({ ...en, weekStart: 1 }); +export const internalDayjs = (...args: any[]): dayjs.Dayjs => dayjs(...args).locale({ ...en, weekStart: 1 }); -export const TIME_SERIES = { +export const TIME_SERIES: Record string[]> = { day: (range) => range.by('d').map(d => d.format('YYYY-MM-DDT00:00:00.000')), month: (range) => range.snapTo('month').by('M').map(d => d.format('YYYY-MM-01T00:00:00.000')), year: (range) => range.snapTo('year').by('y').map(d => d.format('YYYY-01-01T00:00:00.000')), @@ -39,13 +74,13 @@ export const TIME_SERIES = { )), }; -export const isPredefinedGranularity = (granularity) => !!TIME_SERIES[granularity]; +export const isPredefinedGranularity = (granularity: TimeDimensionGranularity): boolean => !!TIME_SERIES[granularity]; export const DateRegex = /^\d\d\d\d-\d\d-\d\d$/; export const LocalDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z?$/; -export const dayRange = (from, to) => ({ - by: (value) => { +export const dayRange = (from: any, to: any): DayRange => ({ + by: (value: any) => { const results = []; let start = internalDayjs(from); @@ -58,7 +93,7 @@ export const dayRange = (from, to) => ({ return results; }, - snapTo: (value) => dayRange(internalDayjs(from).startOf(value), internalDayjs(to).endOf(value)), + snapTo: (value: any): DayRange => dayRange(internalDayjs(from).startOf(value), internalDayjs(to).endOf(value)), start: internalDayjs(from), end: internalDayjs(to), }); @@ -73,8 +108,8 @@ export const dayRange = (from, to) => ({ * It's not referenced to omit imports of moment.js staff. * Probably one day we should choose one implementation and reuse it in other places. */ -export function parseSqlInterval(intervalStr) { - const interval = {}; +export function parseSqlInterval(intervalStr: SqlInterval): ParsedInterval { + const interval: ParsedInterval = {}; const parts = intervalStr.split(/\s+/); for (let i = 0; i < parts.length; i += 2) { @@ -97,7 +132,7 @@ export function parseSqlInterval(intervalStr) { * @param interval * @returns {dayjs} */ -export function addInterval(date, interval) { +export function addInterval(date: dayjs.Dayjs, interval: ParsedInterval): dayjs.Dayjs { let res = date.clone(); Object.entries(interval).forEach(([key, value]) => { @@ -115,7 +150,7 @@ export function addInterval(date, interval) { * @param interval * @returns {dayjs} */ -export function subtractInterval(date, interval) { +export function subtractInterval(date: dayjs.Dayjs, interval: ParsedInterval): dayjs.Dayjs { let res = date.clone(); Object.entries(interval).forEach(([key, value]) => { @@ -130,7 +165,7 @@ export function subtractInterval(date, interval) { * TODO: It's copy/paste of alignToOrigin from @cubejs-backend/shared [time.ts] * but operates with dayjs instead of moment.js */ -function alignToOrigin(startDate, interval, origin) { +function alignToOrigin(startDate: dayjs.Dayjs, interval: ParsedInterval, origin: dayjs.Dayjs): dayjs.Dayjs { let alignedDate = startDate.clone(); let intervalOp; let isIntervalNegative = false; @@ -172,7 +207,7 @@ function alignToOrigin(startDate, interval, origin) { * TODO: It's almost a copy/paste of timeSeriesFromCustomInterval from * @cubejs-backend/shared [time.ts] but operates with dayjs instead of moment.js */ -export const timeSeriesFromCustomInterval = (from, to, granularity) => { +export const timeSeriesFromCustomInterval = (from: string, to: string, granularity: Granularity): string[] => { const intervalParsed = parseSqlInterval(granularity.interval); const start = internalDayjs(from); const end = internalDayjs(to); @@ -194,11 +229,8 @@ export const timeSeriesFromCustomInterval = (from, to, granularity) => { /** * Returns the lowest time unit for the interval - * @protected - * @param {string} interval - * @returns {string} */ -export const diffTimeUnitForInterval = (interval) => { +export const diffTimeUnitForInterval = (interval: string): string => { if (/second/i.test(interval)) { return 'second'; } else if (/minute/i.test(interval)) { @@ -220,7 +252,7 @@ export const diffTimeUnitForInterval = (interval) => { const granularityOrder = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second']; -export const minGranularityForIntervals = (i1, i2) => { +export const minGranularityForIntervals = (i1: string, i2: string): string => { const g1 = diffTimeUnitForInterval(i1); const g2 = diffTimeUnitForInterval(i2); const g1pos = granularityOrder.indexOf(g1); @@ -233,7 +265,7 @@ export const minGranularityForIntervals = (i1, i2) => { return g2; }; -export const granularityFor = (dateStr) => { +export const granularityFor = (dateStr: string): string => { const dayjsDate = internalDayjs(dateStr); const month = dayjsDate.month(); const date = dayjsDate.date(); diff --git a/packages/cubejs-client-core/src/types.ts b/packages/cubejs-client-core/src/types.ts new file mode 100644 index 0000000000000..efbe83e829225 --- /dev/null +++ b/packages/cubejs-client-core/src/types.ts @@ -0,0 +1,514 @@ +import Meta from './Meta'; +import { TimeDimensionGranularity } from './time'; + +export type QueryOrder = 'asc' | 'desc' | 'none'; + +export type TQueryOrderObject = { [key: string]: QueryOrder }; +export type TQueryOrderArray = Array<[string, QueryOrder]>; + +export type GranularityAnnotation = { + name: string; + title: string; + interval: string; + offset?: string; + origin?: string; +}; + +export type Annotation = { + title: string; + shortTitle: string; + type: string; + meta?: any; + format?: 'currency' | 'percent' | 'number'; + drillMembers?: any[]; + drillMembersGrouped?: any; + granularity?: GranularityAnnotation; +}; + +export type QueryAnnotations = { + dimensions: Record; + measures: Record; + timeDimensions: Record; + segments: Record; +}; + +export type QueryType = 'regularQuery' | 'compareDateRangeQuery' | 'blendingQuery'; + +export type DateRange = string | [string, string]; + +export interface TimeDimensionBase { + dimension: string; + granularity?: TimeDimensionGranularity; + dateRange?: DateRange; +} + +export interface TimeDimensionComparison extends TimeDimensionBase { + compareDateRange: Array; +} + +export type TimeDimension = TimeDimensionBase | TimeDimensionComparison; + +// eslint-disable-next-line no-use-before-define +export type Filter = BinaryFilter | UnaryFilter | LogicalOrFilter | LogicalAndFilter; + +export type LogicalAndFilter = { + and: Filter[]; +}; + +export type LogicalOrFilter = { + or: Filter[]; +}; + +export type UnaryOperator = 'set' | 'notSet'; + +export type BinaryOperator = + | 'equals' + | 'notEquals' + | 'contains' + | 'notContains' + | 'startsWith' + | 'notStartsWith' + | 'endsWith' + | 'notEndsWith' + | 'gt' + | 'gte' + | 'lt' + | 'lte' + | 'inDateRange' + | 'notInDateRange' + | 'beforeDate' + | 'beforeOrOnDate' + | 'afterDate' + | 'afterOrOnDate'; + +export interface BinaryFilter { + /** + * @deprecated Use `member` instead. + */ + dimension?: string; + member?: string; + operator: BinaryOperator; + values: string[]; +} + +export interface UnaryFilter { + /** + * @deprecated Use `member` instead. + */ + dimension?: string; + member?: string; + operator: UnaryOperator; + values?: never; +} + +export interface Query { + measures?: string[]; + dimensions?: string[]; + filters?: Filter[]; + timeDimensions?: TimeDimension[]; + segments?: string[]; + limit?: null | number; + rowLimit?: null | number; + offset?: number; + order?: TQueryOrderObject | TQueryOrderArray; + timezone?: string; + renewQuery?: boolean; + ungrouped?: boolean; + responseFormat?: 'compact' | 'default'; + total?: boolean; +} + +export type PivotQuery = Query & { + queryType: QueryType; +}; + +type LeafMeasure = { + measure: string; + additive: boolean; + type: 'count' | 'countDistinct' | 'sum' | 'min' | 'max' | 'number' | 'countDistinctApprox' +}; + +export type TransformedQuery = { + allFiltersWithinSelectedDimensions: boolean; + granularityHierarchies: Record; + hasMultipliedMeasures: boolean; + hasNoTimeDimensionsWithoutGranularity: boolean; + isAdditive: boolean; + leafMeasureAdditive: boolean; + leafMeasures: string[]; + measures: string[]; + sortedDimensions: string[]; + sortedTimeDimensions: [[string, string]]; + measureToLeafMeasures?: Record; + ownedDimensions: string[]; + ownedTimeDimensionsAsIs: [[string, string | null]]; + ownedTimeDimensionsWithRollupGranularity: [[string, string]]; +}; + +export type PreAggregationType = 'rollup' | 'rollupJoin' | 'rollupLambda' | 'originalSql'; + +export type UsedPreAggregation = { + targetTableName: string; + type: PreAggregationType; +}; + +export type LoadResponseResult = { + annotation: QueryAnnotations; + lastRefreshTime: string; + query: Query; + data: T[]; + external: boolean | null; + dbType: string; + extDbType: string; + requestId?: string; + usedPreAggregations?: Record; + transformedQuery?: TransformedQuery; + total?: number; +}; + +export type LoadResponse = { + queryType: QueryType; + results: LoadResponseResult[]; + pivotQuery: PivotQuery; + slowQuery?: boolean; + [key: string]: any; +}; + +export type PivotRow = { + xValues: Array; + yValuesArray: Array<[string[], string]>; +}; + +export type Pivot = any; +// { +// xValues: any; +// yValuesArray: any[]; +// }; + +/** + * Configuration object that contains information about pivot axes and other options. + * + * Let's apply `pivotConfig` and see how it affects the axes + * ```js + * // Example query + * { + * measures: ['Orders.count'], + * dimensions: ['Users.country', 'Users.gender'] + * } + * ``` + * If we put the `Users.gender` dimension on **y** axis + * ```js + * resultSet.tablePivot({ + * x: ['Users.country'], + * y: ['Users.gender', 'measures'] + * }) + * ``` + * + * The resulting table will look the following way + * + * | Users Country | male, Orders.count | female, Orders.count | + * | ------------- | ------------------ | -------------------- | + * | Australia | 3 | 27 | + * | Germany | 10 | 12 | + * | US | 5 | 7 | + * + * Now let's put the `Users.country` dimension on **y** axis instead + * ```js + * resultSet.tablePivot({ + * x: ['Users.gender'], + * y: ['Users.country', 'measures'], + * }); + * ``` + * + * in this case the `Users.country` values will be laid out on **y** or **columns** axis + * + * | Users Gender | Australia, Orders.count | Germany, Orders.count | US, Orders.count | + * | ------------ | ----------------------- | --------------------- | ---------------- | + * | male | 3 | 10 | 5 | + * | female | 27 | 12 | 7 | + * + * It's also possible to put the `measures` on **x** axis. But in either case it should always be the last item of the array. + * ```js + * resultSet.tablePivot({ + * x: ['Users.gender', 'measures'], + * y: ['Users.country'], + * }); + * ``` + * + * | Users Gender | measures | Australia | Germany | US | + * | ------------ | ------------ | --------- | ------- | --- | + * | male | Orders.count | 3 | 10 | 5 | + * | female | Orders.count | 27 | 12 | 7 | + */ +export type PivotConfig = { + joinDateRange?: ((pivots: Pivot[], joinDateRange: any) => PivotRow[]) | false; + /** + * Dimensions to put on **x** or **rows** axis. + */ + x: string[]; + /** + * Dimensions to put on **y** or **columns** axis. + */ + y: string[]; + /** + * If `true` missing dates on the time dimensions will be filled with fillWithValue or `0` by default for all measures.Note: the `fillMissingDates` option set to `true` will override any **order** applied to the query + */ + fillMissingDates?: boolean | null; + /** + * Value to autofill all the missing date's measure. + */ + fillWithValue?: string | number | null; + /** + * Give each series a prefix alias. Should have one entry for each query:measure. See [chartPivot](#result-set-chart-pivot) + */ + aliasSeries?: string[]; +}; + +export type DrillDownLocator = { + xValues: string[]; + yValues?: string[]; +}; + +export type Series = { + key: string; + title: string; + shortTitle: string; + series: T[]; +}; + +export type Column = { + key: string; + title: string; + series: []; +}; + +export type SeriesNamesColumn = { + key: string; + title: string; + shortTitle: string; + yValues: string[]; +}; + +export type ChartPivotRow = { + x: string; + xValues: string[]; + [key: string]: any; +}; + +export type TableColumn = { + key: string; + dataIndex: string; + meta?: any; + type: string | number; + title: string; + shortTitle: string; + format?: any; + children?: TableColumn[]; +}; + +export type SerializedResult = { + loadResponse: LoadResponse; +}; + +export type ExtractTimeMember = + T extends { dimension: infer Dimension, granularity: infer Granularity } + ? Dimension | `${Dimension & string}.${Granularity & string}` + : never; + +export type ExtractTimeMembers = + T extends readonly [infer First, ...infer Rest] + ? ExtractTimeMember | ExtractTimeMembers + : never; + +export type MemberType = 'measures' | 'dimensions' | 'segments'; + +export type TOrderMember = { + id: string; + title: string; + order: QueryOrder | 'none'; +}; + +type TCubeMemberType = 'time' | 'number' | 'string' | 'boolean'; + +// @see BaseCubeMember +// @deprecated +export type TCubeMember = { + type: TCubeMemberType; + name: string; + title: string; + shortTitle: string; + description?: string; + /** + * @deprecated use `public` instead + */ + isVisible?: boolean; + public?: boolean; + meta?: any; +}; + +export type BaseCubeMember = { + type: TCubeMemberType; + name: string; + title: string; + shortTitle: string; + description?: string; + /** + * @deprecated use `public` instead + */ + isVisible?: boolean; + public?: boolean; + meta?: any; +}; + +export type TCubeMeasure = BaseCubeMember & { + aggType: 'count' | 'number'; + cumulative: boolean; + cumulativeTotal: boolean; + drillMembers: string[]; + drillMembersGrouped: { + measures: string[]; + dimensions: string[]; + }; + format?: 'currency' | 'percent'; +}; + +export type CubeTimeDimensionGranularity = { + name: string; + title: string; +}; + +export type BaseCubeDimension = BaseCubeMember & { + primaryKey?: boolean; + suggestFilterValues: boolean; +}; + +export type CubeTimeDimension = BaseCubeDimension & + { type: 'time'; granularities?: CubeTimeDimensionGranularity[] }; + +export type TCubeDimension = + (BaseCubeDimension & { type: Exclude }) | + CubeTimeDimension; + +export type TCubeSegment = Omit; + +export type NotFoundMember = { + title: string; + error: string; +}; + +export type TCubeMemberByType = T extends 'measures' + ? TCubeMeasure + : T extends 'dimensions' + ? TCubeDimension + : T extends 'segments' + ? TCubeSegment + : never; + +export type CubeMember = TCubeMeasure | TCubeDimension | TCubeSegment; + +export type TCubeFolder = { + name: string; + members: string[]; +}; + +export type TCubeHierarchy = { + name: string; + title?: string; + levels: string[]; + public?: boolean; +}; + +/** + * @deprecated use DryRunResponse + */ +export type TDryRunResponse = { + queryType: QueryType; + normalizedQueries: Query[]; + pivotQuery: PivotQuery; + queryOrder: Array<{ [k: string]: QueryOrder }>; + transformedQueries: TransformedQuery[]; +}; + +export type DryRunResponse = { + queryType: QueryType; + normalizedQueries: Query[]; + pivotQuery: PivotQuery; + queryOrder: Array<{ [k: string]: QueryOrder }>; + transformedQueries: TransformedQuery[]; +}; + +export type Cube = { + name: string; + title: string; + description?: string; + measures: TCubeMeasure[]; + dimensions: TCubeDimension[]; + segments: TCubeSegment[]; + folders: TCubeFolder[]; + hierarchies: TCubeHierarchy[]; + connectedComponent?: number; + type?: 'view' | 'cube'; + /** + * @deprecated use `public` instead + */ + isVisible?: boolean; + public?: boolean; + meta?: any; +}; + +export type CubeMap = { + measures: Record; + dimensions: Record; + segments: Record; +}; + +export type CubesMap = Record< + string, + CubeMap +>; + +export type MetaResponse = { + cubes: Cube[]; +}; + +export type FilterOperator = { + name: string; + title: string; +}; + +export type TSourceAxis = 'x' | 'y'; + +export type ChartType = 'line' | 'bar' | 'table' | 'area' | 'number' | 'pie'; + +export type TDefaultHeuristicsOptions = { + meta: Meta; + sessionGranularity?: TimeDimensionGranularity; +}; + +export type TDefaultHeuristicsResponse = { + shouldApplyHeuristicOrder: boolean; + pivotConfig: PivotConfig | null; + query: Query; + chartType?: ChartType; + sessionGranularity?: TimeDimensionGranularity | null; +}; + +export type TDefaultHeuristicsState = { + query: Query; + chartType?: ChartType; +}; + +export interface TFlatFilter { + /** + * @deprecated Use `member` instead. + */ + dimension?: string; + member?: string; + operator: BinaryOperator | UnaryOperator; + values?: string[]; +} + +export type ProgressResponse = { + stage: string; + timeElapsed: number; +}; diff --git a/packages/cubejs-client-core/src/utils.ts b/packages/cubejs-client-core/src/utils.ts index 48ef2e274a056..b419269d3f8d2 100644 --- a/packages/cubejs-client-core/src/utils.ts +++ b/packages/cubejs-client-core/src/utils.ts @@ -1,56 +1,69 @@ import { clone, equals, fromPairs, indexBy, prop, toPairs } from 'ramda'; +import { DeeplyReadonly } from 'src'; import { DEFAULT_GRANULARITY } from './time'; - -export function removeEmptyQueryFields(_query) { +import { + Filter, + PivotConfig, + Query, + QueryOrder, + TDefaultHeuristicsOptions, + TDefaultHeuristicsResponse, + TDefaultHeuristicsState, + TFlatFilter, + TOrderMember, + TQueryOrderArray, + TQueryOrderObject, + TSourceAxis +} from './types'; + +export function removeEmptyQueryFields(_query: DeeplyReadonly) { const query = _query || {}; return fromPairs( - toPairs(query) - .map(([key, value]) => { - if ( - ['measures', 'dimensions', 'segments', 'timeDimensions', 'filters'].includes(key) - ) { - if (Array.isArray(value) && value.length === 0) { - return null; - } + toPairs(query).flatMap(([key, value]) => { + if ( + ['measures', 'dimensions', 'segments', 'timeDimensions', 'filters'].includes(key) + ) { + if (Array.isArray(value) && value.length === 0) { + return []; } + } - if (key === 'order' && value) { - if (Array.isArray(value) && !value.length) { - return null; - } else if (!Object.keys(value).length) { - return null; - } + if (key === 'order' && value) { + if (Array.isArray(value) && value.length === 0) { + return []; + } else if (!Object.keys(value).length) { + return []; } + } - return [key, value]; - }) - .filter(Boolean) + return [[key, value]]; + }) ); } -export function validateQuery(_query) { +export function validateQuery(_query: DeeplyReadonly | null | undefined): Query { const query = _query || {}; return removeEmptyQueryFields({ ...query, - filters: (query.filters || []).filter((f) => f.operator), + filters: (query.filters || []).filter((f) => 'operator' in f), timeDimensions: (query.timeDimensions || []).filter( (td) => !(!td.dateRange && !td.granularity) ), }); } -export function areQueriesEqual(query1 = {}, query2 = {}) { +export function areQueriesEqual(query1: DeeplyReadonly | null, query2: DeeplyReadonly | null): boolean { return ( equals( - Object.entries((query1 && query1.order) || {}), - Object.entries((query2 && query2.order) || {}) + Object.entries(query1?.order || {}), + Object.entries(query2?.order || {}) ) && equals(query1, query2) ); } -export function defaultOrder(query) { +export function defaultOrder(query: DeeplyReadonly): { [key: string]: QueryOrder } { const granularity = (query.timeDimensions || []).find((d) => d.granularity); if (granularity) { @@ -62,23 +75,29 @@ export function defaultOrder(query) { (query.dimensions || []).length > 0 ) { return { - [query.measures[0]]: 'desc', + [query.measures![0]]: 'desc', }; } else if ((query.dimensions || []).length > 0) { return { - [query.dimensions[0]]: 'asc', + [query.dimensions![0]]: 'asc', }; } return {}; } -export function defaultHeuristics(newState, oldQuery = {}, options) { +export function defaultHeuristics( + newState: TDefaultHeuristicsState, + oldQuery: Query, + options: TDefaultHeuristicsOptions +): TDefaultHeuristicsResponse { const { query, ...props } = clone(newState); const { meta, sessionGranularity } = options; const granularity = sessionGranularity || DEFAULT_GRANULARITY; - let state = { + let state: TDefaultHeuristicsResponse = { + shouldApplyHeuristicOrder: false, + pivotConfig: null, query, ...props, }; @@ -89,20 +108,24 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { } if (Array.isArray(newQuery) || Array.isArray(oldQuery)) { - return newState; + return { + shouldApplyHeuristicOrder: false, + pivotConfig: null, + ...newState, + }; } if (newQuery) { if ( (oldQuery.timeDimensions || []).length === 1 && (newQuery.timeDimensions || []).length === 1 && - newQuery.timeDimensions[0].granularity && - oldQuery.timeDimensions[0].granularity !== - newQuery.timeDimensions[0].granularity + newQuery.timeDimensions![0].granularity && + oldQuery.timeDimensions![0].granularity !== + newQuery.timeDimensions![0].granularity ) { state = { ...state, - sessionGranularity: newQuery.timeDimensions[0].granularity, + sessionGranularity: newQuery.timeDimensions![0].granularity, }; } @@ -111,11 +134,11 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { (newQuery.measures || []).length > 0) || ((oldQuery.measures || []).length === 1 && (newQuery.measures || []).length === 1 && - oldQuery.measures[0] !== newQuery.measures[0]) + oldQuery.measures![0] !== newQuery.measures![0]) ) { const [td] = newQuery.timeDimensions || []; const defaultTimeDimension = meta.defaultTimeDimensionNameFor( - newQuery.measures[0] + newQuery.measures![0] ); newQuery = { ...newQuery, @@ -123,8 +146,8 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { ? [ { dimension: defaultTimeDimension, - granularity: (td && td.granularity) || granularity, - dateRange: td && td.dateRange, + granularity: td?.granularity || granularity, + dateRange: td?.dateRange, }, ] : [], @@ -209,9 +232,9 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { if ( (newChartType === 'line' || newChartType === 'area') && (oldQuery.timeDimensions || []).length === 1 && - !oldQuery.timeDimensions[0].granularity + !oldQuery.timeDimensions![0].granularity ) { - const [td] = oldQuery.timeDimensions; + const [td] = oldQuery.timeDimensions!; return { ...state, pivotConfig: null, @@ -227,9 +250,9 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { newChartType === 'table' || newChartType === 'number') && (oldQuery.timeDimensions || []).length === 1 && - oldQuery.timeDimensions[0].granularity + oldQuery.timeDimensions![0].granularity ) { - const [td] = oldQuery.timeDimensions; + const [td] = oldQuery.timeDimensions!; return { ...state, pivotConfig: null, @@ -245,25 +268,23 @@ export function defaultHeuristics(newState, oldQuery = {}, options) { return state; } -export function isQueryPresent(query) { +export function isQueryPresent(query: DeeplyReadonly | null | undefined): boolean { if (!query) { return false; } return (Array.isArray(query) ? query : [query]).every( - (q) => (q.measures && q.measures.length) || - (q.dimensions && q.dimensions.length) || - (q.timeDimensions && q.timeDimensions.length) + (q) => q.measures?.length || q.dimensions?.length || q.timeDimensions?.length ); } export function movePivotItem( - pivotConfig, - sourceIndex, - destinationIndex, - sourceAxis, - destinationAxis -) { + pivotConfig: PivotConfig, + sourceIndex: number, + destinationIndex: number, + sourceAxis: TSourceAxis, + destinationAxis: TSourceAxis +): PivotConfig { const nextPivotConfig = { ...pivotConfig, x: [...pivotConfig.x], @@ -294,7 +315,7 @@ export function movePivotItem( return nextPivotConfig; } -export function moveItemInArray(list, sourceIndex, destinationIndex) { +export function moveItemInArray(list: T[], sourceIndex: number, destinationIndex: number): T[] { const result = [...list]; const [removed] = result.splice(sourceIndex, 1); result.splice(destinationIndex, 0, removed); @@ -302,33 +323,43 @@ export function moveItemInArray(list, sourceIndex, destinationIndex) { return result; } -export function flattenFilters(filters = []) { - return filters.reduce((memo, filter) => { - if (filter.or || filter.and) { - return [...memo, ...flattenFilters(filter.or || filter.and)]; +export function flattenFilters(filters: Filter[] = []): TFlatFilter[] { + return filters.reduce((memo, filter) => { + if ('or' in filter) { + return [...memo, ...flattenFilters(filter.or)]; + } + + if ('and' in filter) { + return [...memo, ...flattenFilters(filter.and)]; } return [...memo, filter]; }, []); } -export function getQueryMembers(query = {}) { - const keys = ['measures', 'dimensions', 'segments']; - const members = new Set(); +export function getQueryMembers(query: DeeplyReadonly = {}): string[] { + const keys = ['measures', 'dimensions', 'segments'] as const; + const members = new Set(); keys.forEach((key) => (query[key] || []).forEach((member) => members.add(member))); (query.timeDimensions || []).forEach((td) => members.add(td.dimension)); - flattenFilters(query.filters).forEach((filter) => members.add(filter.dimension || filter.member)); + const filters = flattenFilters(query.filters as Filter[]); + filters.forEach((filter) => { + const member = filter.dimension || filter.member; + if (typeof member === 'string') { + members.add(member); + } + }); return [...members]; } -export function getOrderMembersFromOrder(orderMembers, order) { - const ids = new Set(); +export function getOrderMembersFromOrder(orderMembers: any[], order: TQueryOrderObject | TQueryOrderArray): TOrderMember[] { + const ids = new Set(); const indexedOrderMembers = indexBy(prop('id'), orderMembers); const entries = Array.isArray(order) ? order : Object.entries(order || {}); - const nextOrderMembers = []; + const nextOrderMembers: TOrderMember[] = []; entries.forEach(([memberId, currentOrder]) => { if (currentOrder !== 'none' && indexedOrderMembers[memberId]) { @@ -351,14 +382,10 @@ export function getOrderMembersFromOrder(orderMembers, order) { return nextOrderMembers; } -export function aliasSeries(values, index, pivotConfig, duplicateMeasures) { - const nonNullValues = values.filter((value) => value != null); +export function aliasSeries(values: string[], index: number, pivotConfig?: Partial, duplicateMeasures: Set = new Set()) { + const nonNullValues = values.filter((value: any) => value != null); - if ( - pivotConfig && - pivotConfig.aliasSeries && - pivotConfig.aliasSeries[index] - ) { + if (pivotConfig?.aliasSeries?.[index]) { return [pivotConfig.aliasSeries[index], ...nonNullValues]; } else if (duplicateMeasures.has(nonNullValues[0])) { return [index, ...nonNullValues]; From 7dbce72f46ec53be85dabf0861bbd431f41957b4 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 11:39:11 +0300 Subject: [PATCH 03/51] Move to TS tests --- .../{src/tests => test}/CubeApi.test.js | 0 .../HttpTransport.test.ts} | 22 +- .../ResultSet.test.ts} | 325 +++++++++--------- .../compare-date-range.test.ts} | 8 +- .../data-blending.test.ts} | 12 +- .../default-heuristics.test.ts} | 10 +- .../drill-down.test.ts} | 18 +- .../fixtures/datablending/load-responses.json | 0 .../granularity.test.ts} | 8 +- .../{src/index.test.js => test/index.test.ts} | 41 ++- .../table.test.js => test/table.test.ts} | 109 +++--- .../utils.test.js => test/utils.test.ts} | 6 +- 12 files changed, 291 insertions(+), 268 deletions(-) rename packages/cubejs-client-core/{src/tests => test}/CubeApi.test.js (100%) rename packages/cubejs-client-core/{src/HttpTransport.test.js => test/HttpTransport.test.ts} (94%) rename packages/cubejs-client-core/{src/tests/ResultSet.test.js => test/ResultSet.test.ts} (90%) rename packages/cubejs-client-core/{src/tests/compare-date-range.test.js => test/compare-date-range.test.ts} (99%) rename packages/cubejs-client-core/{src/tests/data-blending.test.js => test/data-blending.test.ts} (98%) rename packages/cubejs-client-core/{src/tests/default-heuristics.test.js => test/default-heuristics.test.ts} (89%) rename packages/cubejs-client-core/{src/tests/drill-down.test.js => test/drill-down.test.ts} (96%) rename packages/cubejs-client-core/{src/tests => test}/fixtures/datablending/load-responses.json (100%) rename packages/cubejs-client-core/{src/tests/granularity.test.js => test/granularity.test.ts} (97%) rename packages/cubejs-client-core/{src/index.test.js => test/index.test.ts} (91%) rename packages/cubejs-client-core/{src/tests/table.test.js => test/table.test.ts} (93%) rename packages/cubejs-client-core/{src/tests/utils.test.js => test/utils.test.ts} (84%) diff --git a/packages/cubejs-client-core/src/tests/CubeApi.test.js b/packages/cubejs-client-core/test/CubeApi.test.js similarity index 100% rename from packages/cubejs-client-core/src/tests/CubeApi.test.js rename to packages/cubejs-client-core/test/CubeApi.test.js diff --git a/packages/cubejs-client-core/src/HttpTransport.test.js b/packages/cubejs-client-core/test/HttpTransport.test.ts similarity index 94% rename from packages/cubejs-client-core/src/HttpTransport.test.js rename to packages/cubejs-client-core/test/HttpTransport.test.ts index 92d53bc79111b..c3e19758b76fd 100644 --- a/packages/cubejs-client-core/src/HttpTransport.test.js +++ b/packages/cubejs-client-core/test/HttpTransport.test.ts @@ -1,10 +1,12 @@ /* eslint-disable import/first */ -/* eslint-disable import/newline-after-import */ /* globals describe,test,expect,jest,afterEach,beforeAll,beforeEach */ -import '@babel/runtime/regenerator'; -jest.mock('cross-fetch'); import fetch from 'cross-fetch'; -import HttpTransport from './HttpTransport'; + +jest.mock('cross-fetch'); + +import HttpTransport from '../src/HttpTransport'; + +const mockedFetch = fetch as jest.MockedFunction; describe('HttpTransport', () => { const apiUrl = 'http://localhost:3000/cubejs-api/v1'; @@ -31,11 +33,11 @@ describe('HttpTransport', () => { const largeQueryJson = `{"query":{"measures":["Orders.count"],"dimensions":["Users.country"],"filters":[{"member":"Users.id","operator":"equals","values":${JSON.stringify(ids)}}]}}`; beforeAll(() => { - fetch.mockReturnValue(Promise.resolve({ ok: true })); + mockedFetch.mockReturnValue(Promise.resolve({ ok: true } as Response)); }); afterEach(() => { - fetch.mockClear(); + mockedFetch.mockClear(); }); test('it serializes the query object and sends it in the query string', async () => { @@ -44,7 +46,7 @@ describe('HttpTransport', () => { apiUrl, }); const req = transport.request('load', { query }); - await req.subscribe(() => { }); + await req.subscribe(() => { console.log('subscribe cb'); }); expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith(`${apiUrl}/load?query=${queryUrlEncoded}`, { method: 'GET', @@ -66,7 +68,7 @@ describe('HttpTransport', () => { } }); const req = transport.request('meta', { extraParams }); - await req.subscribe(() => { }); + await req.subscribe(() => { console.log('subscribe cb'); }); expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith(`${apiUrl}/meta?extraParams=${serializedExtraParams}`, { method: 'GET', @@ -85,7 +87,7 @@ describe('HttpTransport', () => { method: 'POST' }); const req = transport.request('load', { query }); - await req.subscribe(() => { }); + await req.subscribe(() => { console.log('subscribe cb'); }); expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith(`${apiUrl}/load`, { method: 'POST', @@ -103,7 +105,7 @@ describe('HttpTransport', () => { apiUrl }); const req = transport.request('load', { query: LargeQuery }); - await req.subscribe(() => { }); + await req.subscribe(() => { console.log('subscribe cb'); }); expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith(`${apiUrl}/load`, { method: 'POST', diff --git a/packages/cubejs-client-core/src/tests/ResultSet.test.js b/packages/cubejs-client-core/test/ResultSet.test.ts similarity index 90% rename from packages/cubejs-client-core/src/tests/ResultSet.test.js rename to packages/cubejs-client-core/test/ResultSet.test.ts index f1c997eb68119..d6e39701111d4 100644 --- a/packages/cubejs-client-core/src/tests/ResultSet.test.js +++ b/packages/cubejs-client-core/test/ResultSet.test.ts @@ -7,16 +7,17 @@ /* globals describe,test,expect */ import 'jest'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; +import { TimeDimension } from '../src'; describe('ResultSet', () => { describe('timeSeries', () => { test('it generates array of dates - granularity month', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2015-01-01', '2015-12-31'], granularity: 'month', - timeDimension: 'Events.time' + dimension: 'Events.time' }; const output = [ '2015-01-01T00:00:00.000', @@ -36,11 +37,11 @@ describe('ResultSet', () => { }); test('it generates array of dates - granularity quarter', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2015-01-01', '2015-12-31'], granularity: 'quarter', - timeDimension: 'Events.time' + dimension: 'Events.time' }; const output = [ '2015-01-01T00:00:00.000', @@ -52,11 +53,11 @@ describe('ResultSet', () => { }); test('it generates array of dates - granularity hour', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2015-01-01', '2015-01-01'], granularity: 'hour', - timeDimension: 'Events.time' + dimension: 'Events.time' }; const output = [ '2015-01-01T00:00:00.000', @@ -88,11 +89,11 @@ describe('ResultSet', () => { }); test('it generates array of dates - granularity hour - not full day', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2015-01-01T10:30:00.000', '2015-01-01T13:59:00.000'], granularity: 'hour', - timeDimension: 'Events.time' + dimension: 'Events.time' }; const output = [ '2015-01-01T10:00:00.000', @@ -104,8 +105,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 1 year, origin - 2020-01-01', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2023-12-31'], granularity: 'one_year', dimension: 'Events.time' @@ -131,8 +132,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 1 year, origin - 2025-03-01', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2022-12-31'], granularity: 'one_year', dimension: 'Events.time' @@ -158,8 +159,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 1 year, offset - 2 months', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2022-12-31'], granularity: 'one_year', dimension: 'Events.time' @@ -185,8 +186,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 2 months, origin - 2019-01-01', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-12-31'], granularity: 'two_months', dimension: 'Events.time' @@ -215,8 +216,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 2 months, no offset', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-12-31'], granularity: 'two_months', dimension: 'Events.time' @@ -244,8 +245,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 2 months, origin - 2019-03-15', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-12-31'], granularity: 'two_months', dimension: 'Events.time' @@ -275,8 +276,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 1 months 2 weeks 3 days, origin - 2021-01-25', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-12-31'], granularity: 'one_mo_two_we_three_d', dimension: 'Events.time' @@ -308,8 +309,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 3 weeks, origin - 2020-12-15', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-03-01'], granularity: 'three_weeks', dimension: 'Events.time' @@ -336,8 +337,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, origin - 2021-01-01', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-01-01', '2021-12-31'], granularity: 'two_mo_3w_4d_5h_6m_7s', dimension: 'Events.time' @@ -365,8 +366,8 @@ describe('ResultSet', () => { }); test('it generates array of dates - custom interval - 10 minutes 15 seconds, origin - 2021-02-01 09:59:45', () => { - const resultSet = new ResultSet({}); - const timeDimension = { + const resultSet = new ResultSet({} as any); + const timeDimension: TimeDimension = { dateRange: ['2021-02-01 10:00:00', '2021-02-01 12:00:00'], granularity: 'ten_min_fifteen_sec', dimension: 'Events.time' @@ -437,7 +438,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.chartPivot()).toEqual([ { @@ -486,7 +487,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.chartPivot()).toEqual([ { @@ -535,7 +536,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.chartPivot()).toEqual([ { @@ -583,7 +584,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.chartPivot()).toEqual([ { @@ -633,7 +634,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }, + } as any, { parseDateMeasures: true } ); @@ -758,7 +759,7 @@ describe('ResultSet', () => { ], dimensions: [], }, - }); + } as any); expect(resultSet.seriesNames({ aliasSeries: ['one', 'two'] })).toEqual([ { @@ -882,7 +883,7 @@ describe('ResultSet', () => { ], dimensions: [], }, - }); + } as any); expect(resultSet.seriesNames()).toEqual([ { @@ -913,7 +914,7 @@ describe('ResultSet', () => { } ] } - }); + } as any); expect(resultSet.normalizePivotConfig({ y: ['Foo.bar'] })).toEqual({ x: ['Foo.createdAt.day'], @@ -934,7 +935,7 @@ describe('ResultSet', () => { } ] } - }); + } as any); expect( resultSet.normalizePivotConfig({ x: ['Foo.createdAt'], y: ['Foo.bar'] }) @@ -960,7 +961,7 @@ describe('ResultSet', () => { filters: [], timezone: 'UTC' } - }); + } as any); expect( resultSet.normalizePivotConfig(resultSet.normalizePivotConfig({})) @@ -986,7 +987,7 @@ describe('ResultSet', () => { filters: [], timezone: 'UTC' } - }); + } as any); expect( resultSet.normalizePivotConfig(resultSet.normalizePivotConfig()) @@ -1013,7 +1014,7 @@ describe('ResultSet', () => { filters: [], timezone: 'UTC' } - }); + } as any); expect( resultSet.normalizePivotConfig(resultSet.normalizePivotConfig({})) @@ -1214,7 +1215,7 @@ describe('ResultSet', () => { } } } - }); + } as any); expect(resultSet.tablePivot()).toEqual([ { @@ -1406,7 +1407,7 @@ describe('ResultSet', () => { } } } - }); + } as any); expect(resultSet.tablePivot()).toEqual([ { @@ -1419,124 +1420,124 @@ describe('ResultSet', () => { }); test('fill missing dates with custom value', () => { - const resultSet = new ResultSet({ - query: { - measures: ['Orders.total'], - timeDimensions: [ - { - dimension: 'Orders.createdAt', - granularity: 'day', - dateRange: ['2020-01-08T00:00:00.000', '2020-01-11T23:59:59.999'] - } - ], - filters: [], - timezone: 'UTC' - }, - data: [ - { - 'Orders.createdAt': '2020-01-08T00:00:00.000', - 'Orders.total': 1 - }, - { - 'Orders.createdAt': '2020-01-10T00:00:00.000', - 'Orders.total': 10 - } - ], - annotation: { - measures: {}, - dimensions: {}, - segments: {}, - timeDimensions: { - 'Orders.createdAt': { - title: 'Orders Created at', - shortTitle: 'Created at', - type: 'time' - } - } - } - }); - - expect(resultSet.tablePivot({ - 'fillWithValue': 5 - })).toEqual([ - { - 'Orders.createdAt.day': '2020-01-08T00:00:00.000', - 'Orders.total': 1 - }, - { - 'Orders.createdAt.day': '2020-01-09T00:00:00.000', - 'Orders.total': 5 - }, - { - 'Orders.createdAt.day': '2020-01-10T00:00:00.000', - 'Orders.total': 10 - }, + const resultSet = new ResultSet({ + query: { + measures: ['Orders.total'], + timeDimensions: [ { - 'Orders.createdAt.day': '2020-01-11T00:00:00.000', - 'Orders.total': 5 + dimension: 'Orders.createdAt', + granularity: 'day', + dateRange: ['2020-01-08T00:00:00.000', '2020-01-11T23:59:59.999'] } - ]); - }); + ], + filters: [], + timezone: 'UTC' + }, + data: [ + { + 'Orders.createdAt': '2020-01-08T00:00:00.000', + 'Orders.total': 1 + }, + { + 'Orders.createdAt': '2020-01-10T00:00:00.000', + 'Orders.total': 10 + } + ], + annotation: { + measures: {}, + dimensions: {}, + segments: {}, + timeDimensions: { + 'Orders.createdAt': { + title: 'Orders Created at', + shortTitle: 'Created at', + type: 'time' + } + } + } + } as any); + + expect(resultSet.tablePivot({ + fillWithValue: 5 + })).toEqual([ + { + 'Orders.createdAt.day': '2020-01-08T00:00:00.000', + 'Orders.total': 1 + }, + { + 'Orders.createdAt.day': '2020-01-09T00:00:00.000', + 'Orders.total': 5 + }, + { + 'Orders.createdAt.day': '2020-01-10T00:00:00.000', + 'Orders.total': 10 + }, + { + 'Orders.createdAt.day': '2020-01-11T00:00:00.000', + 'Orders.total': 5 + } + ]); + }); test('fill missing dates with custom string', () => { - const resultSet = new ResultSet({ - query: { - measures: ['Orders.total'], - timeDimensions: [ - { - dimension: 'Orders.createdAt', - granularity: 'day', - dateRange: ['2020-01-08T00:00:00.000', '2020-01-11T23:59:59.999'] - } - ], - filters: [], - timezone: 'UTC' - }, - data: [ - { - 'Orders.createdAt': '2020-01-08T00:00:00.000', - 'Orders.total': 1 - }, - { - 'Orders.createdAt': '2020-01-10T00:00:00.000', - 'Orders.total': 10 - } - ], - annotation: { - measures: {}, - dimensions: {}, - segments: {}, - timeDimensions: { - 'Orders.createdAt': { - title: 'Orders Created at', - shortTitle: 'Created at', - type: 'time' - } - } - } - }); + const resultSet = new ResultSet({ + query: { + measures: ['Orders.total'], + timeDimensions: [ + { + dimension: 'Orders.createdAt', + granularity: 'day', + dateRange: ['2020-01-08T00:00:00.000', '2020-01-11T23:59:59.999'] + } + ], + filters: [], + timezone: 'UTC' + }, + data: [ + { + 'Orders.createdAt': '2020-01-08T00:00:00.000', + 'Orders.total': 1 + }, + { + 'Orders.createdAt': '2020-01-10T00:00:00.000', + 'Orders.total': 10 + } + ], + annotation: { + measures: {}, + dimensions: {}, + segments: {}, + timeDimensions: { + 'Orders.createdAt': { + title: 'Orders Created at', + shortTitle: 'Created at', + type: 'time' + } + } + } + } as any); - expect(resultSet.tablePivot({ - 'fillWithValue': 'N/A' - })).toEqual([ - { - 'Orders.createdAt.day': '2020-01-08T00:00:00.000', - 'Orders.total': 1 - }, - { - 'Orders.createdAt.day': '2020-01-09T00:00:00.000', - 'Orders.total': "N/A" - }, - { - 'Orders.createdAt.day': '2020-01-10T00:00:00.000', - 'Orders.total': 10 - }, - { - 'Orders.createdAt.day': '2020-01-11T00:00:00.000', - 'Orders.total': "N/A" - } - ]); - }); + expect(resultSet.tablePivot({ + fillWithValue: 'N/A' + })).toEqual([ + { + 'Orders.createdAt.day': '2020-01-08T00:00:00.000', + 'Orders.total': 1 + }, + { + 'Orders.createdAt.day': '2020-01-09T00:00:00.000', + 'Orders.total': 'N/A' + }, + { + 'Orders.createdAt.day': '2020-01-10T00:00:00.000', + 'Orders.total': 10 + }, + { + 'Orders.createdAt.day': '2020-01-11T00:00:00.000', + 'Orders.total': 'N/A' + } + ]); + }); test('same dimension and time dimension without granularity', () => { const resultSet = new ResultSet({ @@ -1580,7 +1581,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.tablePivot()).toEqual([ { 'Orders.createdAt': '2020-01-08T17:04:43.000' }, @@ -1640,7 +1641,7 @@ describe('ResultSet', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.pivot()).toEqual( [ diff --git a/packages/cubejs-client-core/src/tests/compare-date-range.test.js b/packages/cubejs-client-core/test/compare-date-range.test.ts similarity index 99% rename from packages/cubejs-client-core/src/tests/compare-date-range.test.js rename to packages/cubejs-client-core/test/compare-date-range.test.ts index 0ab77f1ebadeb..c00178269f597 100644 --- a/packages/cubejs-client-core/src/tests/compare-date-range.test.js +++ b/packages/cubejs-client-core/test/compare-date-range.test.ts @@ -1,5 +1,7 @@ +/* globals describe, test, expect */ + import 'jest'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; const loadResponses = [ { @@ -289,8 +291,8 @@ const loadResponses = [ ]; describe('compare date range', () => { - const resultSet1 = new ResultSet(loadResponses[0]); - const resultSet2 = new ResultSet(loadResponses[1]); + const resultSet1 = new ResultSet(loadResponses[0] as any); + const resultSet2 = new ResultSet(loadResponses[1] as any); describe('series and seriesNames', () => { test('with a single time dimension', () => { diff --git a/packages/cubejs-client-core/src/tests/data-blending.test.js b/packages/cubejs-client-core/test/data-blending.test.ts similarity index 98% rename from packages/cubejs-client-core/src/tests/data-blending.test.js rename to packages/cubejs-client-core/test/data-blending.test.ts index e3eb42c96d161..19b163e6556cb 100644 --- a/packages/cubejs-client-core/src/tests/data-blending.test.js +++ b/packages/cubejs-client-core/test/data-blending.test.ts @@ -1,10 +1,12 @@ +/* globals describe, test, expect */ + import 'jest'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; import { loadResponse, loadResponseWithoutDateRange } from './fixtures/datablending/load-responses.json'; describe('data blending', () => { - const resultSet1 = new ResultSet(loadResponse); + const resultSet1 = new ResultSet(loadResponse as any); describe('with different dimensions', () => { test('normalized pivotConfig', () => { @@ -210,7 +212,7 @@ describe('data blending', () => { ], dimensions: [], }, - }); + } as any); expect(resultSet.chartPivot()).toEqual([ { @@ -247,7 +249,7 @@ describe('data blending', () => { }); test('query without date range', () => { - const resultSet = new ResultSet(loadResponseWithoutDateRange); + const resultSet = new ResultSet(loadResponseWithoutDateRange as any); expect(resultSet.chartPivot()).toEqual([ { @@ -394,7 +396,7 @@ describe('data blending', () => { ], dimensions: [], }, - }); + } as any); expect(resultSet.chartPivot({ aliasSeries: ['one', 'two'] })).toEqual([ { diff --git a/packages/cubejs-client-core/src/tests/default-heuristics.test.js b/packages/cubejs-client-core/test/default-heuristics.test.ts similarity index 89% rename from packages/cubejs-client-core/src/tests/default-heuristics.test.js rename to packages/cubejs-client-core/test/default-heuristics.test.ts index 2c2ca063d4fe7..63c50eae85797 100644 --- a/packages/cubejs-client-core/src/tests/default-heuristics.test.js +++ b/packages/cubejs-client-core/test/default-heuristics.test.ts @@ -1,5 +1,7 @@ +/* globals jest, describe, expect, it */ + import 'jest'; -import { defaultHeuristics } from '../utils'; +import { defaultHeuristics } from '../src/utils'; jest.mock('moment-range', () => { const Moment = jest.requireActual('moment'); @@ -40,7 +42,7 @@ describe('default heuristics', () => { return 'Orders.ts'; }, }, - }) + } as any) ).toStrictEqual({ pivotConfig: null, query: { @@ -73,7 +75,7 @@ describe('default heuristics', () => { const oldQuery = {}; - expect(defaultHeuristics(newState, oldQuery, { meta })).toMatchObject({ + expect(defaultHeuristics(newState, oldQuery, { meta } as any)).toMatchObject({ query: { timeDimensions: [ { @@ -104,7 +106,7 @@ describe('default heuristics', () => { }, }; - expect(defaultHeuristics(newState, {}, { meta })).toMatchObject({ + expect(defaultHeuristics(newState, {}, { meta } as any)).toMatchObject({ query: { timeDimensions: [ { diff --git a/packages/cubejs-client-core/src/tests/drill-down.test.js b/packages/cubejs-client-core/test/drill-down.test.ts similarity index 96% rename from packages/cubejs-client-core/src/tests/drill-down.test.js rename to packages/cubejs-client-core/test/drill-down.test.ts index 1c6196787c488..672512c0a7003 100644 --- a/packages/cubejs-client-core/src/tests/drill-down.test.js +++ b/packages/cubejs-client-core/test/drill-down.test.ts @@ -1,5 +1,7 @@ +/* globals jest, describe, expect, it */ + import 'jest'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; jest.mock('moment-range', () => { const Moment = jest.requireActual('moment'); @@ -218,11 +220,11 @@ const loadResponse2 = { }; describe('drill down query', () => { - const resultSet1 = new ResultSet(loadResponse()); + const resultSet1 = new ResultSet(loadResponse() as any); const resultSet2 = new ResultSet( loadResponse({ timezone: 'America/Los_Angeles', - }) + }) as any ); const resultSet3 = new ResultSet( loadResponse({ @@ -233,13 +235,13 @@ describe('drill down query', () => { values: ['Los Angeles'], }, ], - }) + }) as any ); const resultSet4 = new ResultSet( loadResponse({ dimensions: ['Statuses.potential'], timeDimensions: [], - }) + }) as any ); const resultSet5 = new ResultSet( loadResponse({ @@ -249,7 +251,7 @@ describe('drill down query', () => { granularity: 'week', } ] - }) + }) as any ); it('handles a query with a time dimension', () => { @@ -333,7 +335,7 @@ describe('drill down query', () => { }); it('handles null values', () => { - expect(resultSet4.drillDown({ xvalues: [null] })).toEqual({ + expect(resultSet4.drillDown({ xValues: [] })).toEqual({ measures: [], segments: [], dimensions: ['Orders.id', 'Orders.title'], @@ -353,7 +355,7 @@ describe('drill down query', () => { }); it('respects the parent time dimension date range', () => { - const resultSet = new ResultSet(loadResponse2); + const resultSet = new ResultSet(loadResponse2 as any); expect( resultSet.drillDown({ xValues: ['2023-05-08T00:00:00.000'] }) diff --git a/packages/cubejs-client-core/src/tests/fixtures/datablending/load-responses.json b/packages/cubejs-client-core/test/fixtures/datablending/load-responses.json similarity index 100% rename from packages/cubejs-client-core/src/tests/fixtures/datablending/load-responses.json rename to packages/cubejs-client-core/test/fixtures/datablending/load-responses.json diff --git a/packages/cubejs-client-core/src/tests/granularity.test.js b/packages/cubejs-client-core/test/granularity.test.ts similarity index 97% rename from packages/cubejs-client-core/src/tests/granularity.test.js rename to packages/cubejs-client-core/test/granularity.test.ts index 0e975f99134f7..cdc3529947b4c 100644 --- a/packages/cubejs-client-core/src/tests/granularity.test.js +++ b/packages/cubejs-client-core/test/granularity.test.ts @@ -3,7 +3,7 @@ import 'jest'; import dayjs from 'dayjs'; import ko from 'dayjs/locale/ko'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; describe('ResultSet Granularity', () => { describe('chartPivot', () => { @@ -33,7 +33,6 @@ describe('ResultSet Granularity', () => { timezone: 'UTC', order: [], dimensions: [], - queryType: 'regularQuery', }, data: [ { @@ -98,7 +97,7 @@ describe('ResultSet Granularity', () => { queryType: 'regularQuery', }, slowQuery: false, - }); + } as any); expect(result.chartPivot()).toStrictEqual([ { @@ -141,7 +140,6 @@ describe('ResultSet Granularity', () => { timezone: 'UTC', order: [], dimensions: [], - queryType: 'regularQuery', }, data: [ { @@ -206,7 +204,7 @@ describe('ResultSet Granularity', () => { queryType: 'regularQuery', }, slowQuery: false, - }); + } as any); expect(result.chartPivot()).toStrictEqual([ { diff --git a/packages/cubejs-client-core/src/index.test.js b/packages/cubejs-client-core/test/index.test.ts similarity index 91% rename from packages/cubejs-client-core/src/index.test.js rename to packages/cubejs-client-core/test/index.test.ts index 5dbb3330e6412..ac65e4ab2c9f1 100644 --- a/packages/cubejs-client-core/src/index.test.js +++ b/packages/cubejs-client-core/test/index.test.ts @@ -6,12 +6,21 @@ /* globals describe,test,expect,beforeEach,jest */ -import ResultSet from './ResultSet'; -import { CubeApi } from './index'; +import { CubeApi, LoadMethodOptions, LoadResponse } from '../src/index'; +import ResultSet from '../src/ResultSet'; + +jest.mock('../src/ResultSet'); + +const MockedResultSet = ResultSet as jest.MockedClass; + +class CubeApiTest extends CubeApi { + public loadResponseInternal(response: LoadResponse, options: LoadMethodOptions | null = {}): ResultSet { + return super.loadResponseInternal(response, options); + } +} -jest.mock('./ResultSet'); beforeEach(() => { - ResultSet.mockClear(); + MockedResultSet.mockClear(); }); const mockData = { @@ -205,7 +214,7 @@ const mockData = { describe('CubeApi', () => { test('CubeApi#loadResponseInternal should work with the "default" resType for regular query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -228,7 +237,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { @@ -237,7 +246,7 @@ describe('CubeApi', () => { }); test('CubeApi#loadResponseInternal should work with the "default" resType for compare date range query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -274,7 +283,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { @@ -283,7 +292,7 @@ describe('CubeApi', () => { }); test('CubeApi#loadResponseInternal should work with the "default" resType for blending query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -320,7 +329,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { @@ -329,7 +338,7 @@ describe('CubeApi', () => { }); test('CubeApi#loadResponseInternal should work with the "compact" resType for regular query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -352,7 +361,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { @@ -361,7 +370,7 @@ describe('CubeApi', () => { }); test('CubeApi#loadResponseInternal should work with the "compact" resType for compare date range query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -398,7 +407,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { @@ -407,7 +416,7 @@ describe('CubeApi', () => { }); test('CubeApi#loadResponseInternal should work with the "compact" resType for blending query', () => { - const api = new CubeApi(undefined, { + const api = new CubeApiTest(undefined, { apiUrl: 'http://localhost:4000/cubejs-api/v1', }); const income = { @@ -444,7 +453,7 @@ describe('CubeApi', () => { ) }], }; - api.loadResponseInternal(income); + api.loadResponseInternal(income as LoadResponse); expect(ResultSet).toHaveBeenCalled(); expect(ResultSet).toHaveBeenCalledTimes(1); expect(ResultSet).toHaveBeenCalledWith(outcome, { diff --git a/packages/cubejs-client-core/src/tests/table.test.js b/packages/cubejs-client-core/test/table.test.ts similarity index 93% rename from packages/cubejs-client-core/src/tests/table.test.js rename to packages/cubejs-client-core/test/table.test.ts index 14240546c158b..41bf623e1a6ab 100644 --- a/packages/cubejs-client-core/src/tests/table.test.js +++ b/packages/cubejs-client-core/test/table.test.ts @@ -1,5 +1,8 @@ +/* globals describe, expect, test */ + import 'jest'; -import ResultSet from '../ResultSet'; +import ResultSet from '../src/ResultSet'; +import { PivotConfig } from '../src/types'; describe('resultSet tablePivot and tableColumns', () => { describe('it works with one measure', () => { @@ -62,10 +65,10 @@ describe('resultSet tablePivot and tableColumns', () => { segments: {}, timeDimensions: {}, }, - }); + } as any); describe('all dimensions on `x` axis', () => { - const pivotConfig = { + const pivotConfig: PivotConfig = { x: ['Users.country', 'Users.gender'], y: ['measures'], }; @@ -129,7 +132,7 @@ describe('resultSet tablePivot and tableColumns', () => { }); describe('one dimension on `x` and one one `y` axis', () => { - const pivotConfig = { + const pivotConfig: PivotConfig = { x: ['Users.country'], y: ['Users.gender', 'measures'], }; @@ -477,7 +480,7 @@ describe('resultSet tablePivot and tableColumns', () => { segments: {}, timeDimensions: {}, }, - }); + } as any); test('all dimensions on `x` axis', () => { const pivotConfig = { @@ -622,7 +625,7 @@ describe('resultSet tablePivot and tableColumns', () => { segments: {}, timeDimensions: {}, }, - }); + } as any); test('all dimensions on `x` axis', () => { const pivotConfig = { @@ -697,16 +700,16 @@ describe('resultSet tablePivot and tableColumns', () => { test('order of values is preserved', () => { const resultSet = new ResultSet({ query: { - measures: [ + measures: [ 'Branch.count' ], dimensions: [ 'Tenant.number' ], - 'order': [ + order: [ { - 'id': 'Tenant.number', - 'desc': true + id: 'Tenant.number', + desc: true } ], filters: [], @@ -738,54 +741,54 @@ describe('resultSet tablePivot and tableColumns', () => { segments: {}, timeDimensions: {} } - }); + } as any); expect(resultSet.tableColumns({ - 'x': [], - 'y': [ + x: [], + y: [ 'Tenant.number' ] })).toEqual( - [ - { - 'key': '6', - 'type': 'string', - 'title': 'Tenant Number 6', - 'shortTitle': '6', - 'format': undefined, - 'meta': undefined, - 'children': [ - { - 'key': 'Branch.count', - 'type': 'number', - 'dataIndex': '6,Branch.count', - 'title': 'Branch.count', - 'shortTitle': 'Branch.count', - 'format': undefined, - 'meta': undefined, - } - ] - }, - { - 'key': '1', - 'type': 'string', - 'title': 'Tenant Number 1', - 'shortTitle': '1', - 'format': undefined, - 'meta': undefined, - 'children': [ - { - 'key': 'Branch.count', - 'type': 'number', - 'dataIndex': '1,Branch.count', - 'title': 'Branch.count', - 'shortTitle': 'Branch.count', - 'format': undefined, - "meta": undefined, - } - ] - } - ] + [ + { + key: '6', + type: 'string', + title: 'Tenant Number 6', + shortTitle: '6', + format: undefined, + meta: undefined, + children: [ + { + key: 'Branch.count', + type: 'number', + dataIndex: '6,Branch.count', + title: 'Branch.count', + shortTitle: 'Branch.count', + format: undefined, + meta: undefined, + } + ] + }, + { + key: '1', + type: 'string', + title: 'Tenant Number 1', + shortTitle: '1', + format: undefined, + meta: undefined, + children: [ + { + key: 'Branch.count', + type: 'number', + dataIndex: '1,Branch.count', + title: 'Branch.count', + shortTitle: 'Branch.count', + format: undefined, + meta: undefined, + } + ] + } + ] ); }); }); diff --git a/packages/cubejs-client-core/src/tests/utils.test.js b/packages/cubejs-client-core/test/utils.test.ts similarity index 84% rename from packages/cubejs-client-core/src/tests/utils.test.js rename to packages/cubejs-client-core/test/utils.test.ts index 59c7bf0fb4acb..f56d0ec2a0840 100644 --- a/packages/cubejs-client-core/src/tests/utils.test.js +++ b/packages/cubejs-client-core/test/utils.test.ts @@ -1,7 +1,9 @@ +/* globals describe, expect, test */ + import 'jest'; -import { defaultOrder } from '../utils'; -import { dayRange, TIME_SERIES } from '../time'; +import { defaultOrder } from '../src/utils'; +import { dayRange, TIME_SERIES } from '../src/time'; describe('utils', () => { test('default order', () => { From fb7b4415663f2926bf418045079e148d7aa2c965 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 11:39:26 +0300 Subject: [PATCH 04/51] side fix :) --- packages/cubejs-schema-compiler/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json index 222223fa23bd4..88e50f69389ea 100644 --- a/packages/cubejs-schema-compiler/package.json +++ b/packages/cubejs-schema-compiler/package.json @@ -81,8 +81,7 @@ "source-map-support": "^0.5.19", "sqlstring": "^2.3.1", "testcontainers": "^10.13.0", - "typescript": "~5.2.2", - "uuid": "^8.3.2" + "typescript": "~5.2.2" }, "license": "Apache-2.0", "eslintConfig": { From c3f33110a1a4eba68b94aa623e0759d929d1c6d9 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 11:45:16 +0300 Subject: [PATCH 05/51] fix tools settings --- packages/cubejs-client-core/.eslintrc.js | 39 --------------------- packages/cubejs-client-core/babel.config.js | 4 --- packages/cubejs-client-core/jest.config.js | 3 -- packages/cubejs-client-core/package.json | 34 ++++++++++-------- packages/cubejs-client-core/tsconfig.json | 31 ++++++---------- tsconfig.json | 3 ++ 6 files changed, 33 insertions(+), 81 deletions(-) delete mode 100644 packages/cubejs-client-core/.eslintrc.js delete mode 100644 packages/cubejs-client-core/babel.config.js diff --git a/packages/cubejs-client-core/.eslintrc.js b/packages/cubejs-client-core/.eslintrc.js deleted file mode 100644 index 341ab49edc8e6..0000000000000 --- a/packages/cubejs-client-core/.eslintrc.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - extends: 'airbnb-base', - plugins: [ - 'import' - ], - parser: 'babel-eslint', - rules: { - 'max-classes-per-file': 0, - 'prefer-object-spread': 0, - 'import/no-unresolved': 0, - 'comma-dangle': 0, - 'no-console': 0, - 'arrow-parens': 0, - 'import/extensions': 0, - quotes: ['warn', 'single'], - 'no-prototype-builtins': 0, - 'class-methods-use-this': 0, - 'no-param-reassign': 0, - 'no-mixed-operators': 0, - 'no-else-return': 0, - 'prefer-promise-reject-errors': 0, - 'no-plusplus': 0, - 'no-await-in-loop': 0, - 'operator-linebreak': 0, - 'max-len': ['error', 120, 2, { - ignoreUrls: true, - ignoreComments: false, - ignoreRegExpLiterals: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - }], - 'no-trailing-spaces': ['warn', { skipBlankLines: true }], - 'no-unused-vars': ['warn'], - 'object-curly-newline': 0 - }, - // env: { - // 'jest/globals': true - // } -}; diff --git a/packages/cubejs-client-core/babel.config.js b/packages/cubejs-client-core/babel.config.js deleted file mode 100644 index a494575cb580c..0000000000000 --- a/packages/cubejs-client-core/babel.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - presets: [['@babel/preset-env', {}]], - plugins: ['@babel/plugin-transform-runtime'] -}; diff --git a/packages/cubejs-client-core/jest.config.js b/packages/cubejs-client-core/jest.config.js index e8878181f33d5..4611f9c2f14da 100644 --- a/packages/cubejs-client-core/jest.config.js +++ b/packages/cubejs-client-core/jest.config.js @@ -4,7 +4,4 @@ const base = require('../../jest.base.config'); module.exports = { ...base, rootDir: '.', - transform: { - '^.+\\.js$': 'babel-jest', - }, }; diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index d605066473c08..8d0301728126e 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -8,9 +8,9 @@ "directory": "packages/cubejs-client-core" }, "description": "cube.js client", - "main": "dist/cubejs-client-core.js", + "main": "dist/src/index.js", "module": "dist/cubejs-client-core.esm.js", - "types": "index.d.ts", + "typings": "dist/src/index.d.ts", "author": "Cube Dev, Inc.", "dependencies": { "@babel/runtime": "^7.1.2", @@ -22,27 +22,31 @@ "uuid": "^8.3.2" }, "scripts": { + "build": "rm -rf dist && npm run tsc", + "tsc": "tsc", + "watch": "tsc -w", "test": "npm run unit", - "unit": "jest", - "lint": "eslint src/*.js", - "lint:fix": "eslint --fix src/*.js" + "unit": "jest --coverage dist/test", + "lint": "eslint src/* test/ --ext .ts,.js", + "lint:fix": "eslint --fix src/* test/ --ext .ts,js" }, "files": [ - "src", - "dist", - "index.d.ts" + "dist/src" ], "license": "MIT", "devDependencies": { - "@babel/core": "^7.3.3", - "@babel/preset-env": "^7.3.1", + "@babel/core": "^7.24", + "@babel/preset-env": "^7.24", "@types/jest": "^29", + "@types/ramda": "^0.27.34", "@types/moment-range": "^4.0.0", + "@types/uuid": "^8.3.0", "babel-jest": "^29", - "eslint": "^7.21.0", - "eslint-config-airbnb-base": "^13.1.0", - "eslint-plugin-import": "^2.16.0", - "eslint-plugin-node": "^10.0.0", - "jest": "^29" + "@cubejs-backend/linter": "1.3.12", + "jest": "^29", + "typescript": "~5.2.2" + }, + "eslintConfig": { + "extends": "../cubejs-linter" } } diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index d7a96505a301b..a3f8b04a383cb 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -1,24 +1,15 @@ { + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*", + "test/**/*", + "test/fixtures/datablending/load-responses.json" + ], "compilerOptions": { - "lib": ["es2017"], - "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "declaration": true, - "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "declarationDir": "./dist", /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": false, /* Generates corresponding '.map' file. */ - "outDir": "./temp", /* Redirect output structure to the directory. */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - "allowSyntheticDefaultImports": true, - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "outDir": "dist", + "rootDir": ".", + "baseUrl": ".", + "resolveJsonModule": true, + "esModuleInterop": true } } diff --git a/tsconfig.json b/tsconfig.json index 083de832790a3..ebee4373ec76c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,9 @@ { "path": "packages/cubejs-cubestore-driver" }, + { + "path": "packages/cubejs-client-core" + }, { "path": "packages/cubejs-query-orchestrator" }, From 5e85dce43f6f338f76ab6ca17ccebc2e15c7101a Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 13:09:56 +0300 Subject: [PATCH 06/51] temp comment out rollup for client-core --- packages/cubejs-client-core/package.json | 1 - rollup.config.js | 32 ++++++++++++------------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 8d0301728126e..0e41cd90b4f05 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -13,7 +13,6 @@ "typings": "dist/src/index.d.ts", "author": "Cube Dev, Inc.", "dependencies": { - "@babel/runtime": "^7.1.2", "core-js": "^3.6.5", "cross-fetch": "^3.0.2", "dayjs": "^1.10.4", diff --git a/rollup.config.js b/rollup.config.js index 8300ea079c49f..95ae1ef968e4d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -44,9 +44,9 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => ], }), alias({ - entries: { - '@cubejs-client/core': '../cubejs-client-core/src/index.js', - }, + // entries: { + // '@cubejs-client/core': '../cubejs-client-core/src/index.js', + // }, }), ], }; @@ -133,21 +133,21 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => ]; }; -export default bundle( - 'cubejs-client-core', - 'cubejs', - { - input: 'packages/cubejs-client-core/src/index.js', - }, - { - input: 'packages/cubejs-client-core/src/index.umd.js', - } -) - .concat( - bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { +// export default bundle( +// 'cubejs-client-core', +// 'cubejs', +// { +// input: 'packages/cubejs-client-core/src/index.js', +// }, +// { +// input: 'packages/cubejs-client-core/src/index.umd.js', +// } +// ) +// .concat( + +export default bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { input: 'packages/cubejs-client-ws-transport/src/index.ts', }) - ) .concat( bundle('cubejs-client-react', 'cubejsReact', { input: 'packages/cubejs-client-react/src/index.js', From 492da15a9ec5fd84ab67a4127b676d8b0d9ef128 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 13:20:25 +0300 Subject: [PATCH 07/51] update ts config and remove babel --- packages/cubejs-client-core/package.json | 2 -- packages/cubejs-client-core/tsconfig.json | 21 +++++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 0e41cd90b4f05..8b2fbbbe3bd1a 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -34,8 +34,6 @@ ], "license": "MIT", "devDependencies": { - "@babel/core": "^7.24", - "@babel/preset-env": "^7.24", "@types/jest": "^29", "@types/ramda": "^0.27.34", "@types/moment-range": "^4.0.0", diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index a3f8b04a383cb..4dc22f1c8e153 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -1,15 +1,32 @@ { - "extends": "../../tsconfig.base.json", "include": [ "src/**/*", "test/**/*", "test/fixtures/datablending/load-responses.json" ], "compilerOptions": { + "lib": ["es2017"], + "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "declaration": true, + "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "declarationDir": "./dist", /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": false, /* Generates corresponding '.map' file. */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ "outDir": "dist", "rootDir": ".", "baseUrl": ".", "resolveJsonModule": true, - "esModuleInterop": true } } From 7f232e45817dc2bc9b131bcfd15bff8e8e85772a Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 13:32:27 +0300 Subject: [PATCH 08/51] a few more type fixes --- packages/cubejs-client-core/src/index.ts | 2 +- packages/cubejs-client-ngx/src/client.ts | 6 +++--- .../cubejs-client-ngx/src/query-builder/builder-meta.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index bb80a34a938e1..d9b79e4980219 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -658,7 +658,7 @@ class CubeApi { export default (apiToken: string | (() => Promise), options: CubeApiOptions) => new CubeApi(apiToken, options); -export { CubeApi }; +export { CubeApi, Meta, ProgressResult, HttpTransport, SqlQuery }; export * from './Meta'; export * from './SqlQuery'; export * from './RequestError'; diff --git a/packages/cubejs-client-ngx/src/client.ts b/packages/cubejs-client-ngx/src/client.ts index 11a8d83b2f29c..0c86ea53bd411 100644 --- a/packages/cubejs-client-ngx/src/client.ts +++ b/packages/cubejs-client-ngx/src/client.ts @@ -55,8 +55,8 @@ export class CubeClient { public load( query: Query | Query[], options?: LoadMethodOptions - ): Observable { - return from(>this.apiInstance().load(query, options)); + ): Observable> { + return from(>>this.apiInstance().load(query, options)); } public sql( @@ -77,7 +77,7 @@ export class CubeClient { return from(this.apiInstance().meta(options)); } - public watch(query, params = {}): Observable { + public watch(query, params = {}): Observable> { return new Observable((observer) => query.subscribe({ next: async (query) => { diff --git a/packages/cubejs-client-ngx/src/query-builder/builder-meta.ts b/packages/cubejs-client-ngx/src/query-builder/builder-meta.ts index 139bcbb2067da..b57dba6bd4f96 100644 --- a/packages/cubejs-client-ngx/src/query-builder/builder-meta.ts +++ b/packages/cubejs-client-ngx/src/query-builder/builder-meta.ts @@ -2,13 +2,13 @@ import { Meta, TCubeDimension, TCubeMeasure, - TCubeMember, + TCubeSegment, } from '@cubejs-client/core'; export class BuilderMeta { measures: TCubeMeasure[]; dimensions: TCubeDimension[]; - segments: TCubeMember[]; + segments: TCubeSegment[]; timeDimensions: TCubeDimension[]; filters: Array; From a9736c158b435ed0418f37b4ceb14dff21adb70b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 13:38:02 +0300 Subject: [PATCH 09/51] versions sync --- packages/cubejs-client-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 8b2fbbbe3bd1a..0ac77b9bc7791 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -39,7 +39,7 @@ "@types/moment-range": "^4.0.0", "@types/uuid": "^8.3.0", "babel-jest": "^29", - "@cubejs-backend/linter": "1.3.12", + "@cubejs-backend/linter": "1.3.13", "jest": "^29", "typescript": "~5.2.2" }, From f4c23abd9be5a8e8ceea87b98fb3395cf0d46f00 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 13:55:51 +0300 Subject: [PATCH 10/51] return back rollup --- packages/cubejs-client-core/package.json | 2 +- rollup.config.js | 32 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 0ac77b9bc7791..24ecd96b1c4c9 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -30,7 +30,7 @@ "lint:fix": "eslint --fix src/* test/ --ext .ts,js" }, "files": [ - "dist/src" + "dist" ], "license": "MIT", "devDependencies": { diff --git a/rollup.config.js b/rollup.config.js index 95ae1ef968e4d..c41b18410edad 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -44,9 +44,9 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => ], }), alias({ - // entries: { - // '@cubejs-client/core': '../cubejs-client-core/src/index.js', - // }, + entries: { + '@cubejs-client/core': '../cubejs-client-core/dist/index.js', + }, }), ], }; @@ -133,21 +133,21 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => ]; }; -// export default bundle( -// 'cubejs-client-core', -// 'cubejs', -// { -// input: 'packages/cubejs-client-core/src/index.js', -// }, -// { -// input: 'packages/cubejs-client-core/src/index.umd.js', -// } -// ) -// .concat( - -export default bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { +export default bundle( + 'cubejs-client-core', + 'cubejs', + { + input: 'packages/cubejs-client-core/dist/src/index.js', + }, + { + input: 'packages/cubejs-client-core/dist/src/index.umd.js', + } +) + .concat( + bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { input: 'packages/cubejs-client-ws-transport/src/index.ts', }) + ) .concat( bundle('cubejs-client-react', 'cubejsReact', { input: 'packages/cubejs-client-react/src/index.js', From b6c66899d1e40b8b96d3dad4391f59a0f3b55a6d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 13 May 2025 15:59:53 +0300 Subject: [PATCH 11/51] another try to fix rollup --- packages/cubejs-client-core/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index d9b79e4980219..fdb22967dd441 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -658,11 +658,12 @@ class CubeApi { export default (apiToken: string | (() => Promise), options: CubeApiOptions) => new CubeApi(apiToken, options); -export { CubeApi, Meta, ProgressResult, HttpTransport, SqlQuery }; +export { CubeApi }; export * from './Meta'; export * from './SqlQuery'; export * from './RequestError'; export * from './ProgressResult'; +export * from './ProgressResult'; export { default as ResultSet } from './ResultSet'; export * from './HttpTransport'; export * from './utils'; From 1db3737900a4a1d26bab66fdcca2221733545691 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Tue, 13 May 2025 21:24:08 -0700 Subject: [PATCH 12/51] build fixes --- package.json | 2 + packages/cubejs-client-core/package.json | 4 +- packages/cubejs-client-core/src/index.ts | 1 - rollup.config.js | 144 +++++++++++++---------- yarn.lock | 43 +++---- 5 files changed, 102 insertions(+), 92 deletions(-) diff --git a/package.json b/package.json index 7cc44276cfddb..fa93b047fefab 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@rollup/plugin-alias": "^3.1.2", "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^17.1.0", + "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^11.2.0", "@types/fs-extra": "^9.0.1", "@types/jest": "^27", @@ -57,6 +58,7 @@ "rimraf": "^3.0.2", "rollup": "2.53.1", "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-tsconfig-paths": "^1.5.2", "typescript": "~5.2.2" }, "repository": { diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 24ecd96b1c4c9..039c2cd872886 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -34,12 +34,12 @@ ], "license": "MIT", "devDependencies": { + "@cubejs-backend/linter": "1.3.13", "@types/jest": "^29", - "@types/ramda": "^0.27.34", "@types/moment-range": "^4.0.0", + "@types/ramda": "^0.27.34", "@types/uuid": "^8.3.0", "babel-jest": "^29", - "@cubejs-backend/linter": "1.3.13", "jest": "^29", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index fdb22967dd441..bb80a34a938e1 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -663,7 +663,6 @@ export * from './Meta'; export * from './SqlQuery'; export * from './RequestError'; export * from './ProgressResult'; -export * from './ProgressResult'; export { default as ResultSet } from './ResultSet'; export * from './HttpTransport'; export * from './utils'; diff --git a/rollup.config.js b/rollup.config.js index c41b18410edad..86a64846d38a6 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,39 +1,46 @@ -import babel from '@rollup/plugin-babel'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import alias from '@rollup/plugin-alias'; -import peerDepsExternal from 'rollup-plugin-peer-deps-external'; +import babel from "@rollup/plugin-babel"; +import resolve from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import alias from "@rollup/plugin-alias"; +import tsconfigPaths from "rollup-plugin-tsconfig-paths"; +import peerDepsExternal from "rollup-plugin-peer-deps-external"; +import json from '@rollup/plugin-json'; -const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => { +const bundle = ( + name, + globalName, + { globals = {}, ...baseConfig }, + umdConfig +) => { const baseUmdConfig = { ...(umdConfig || baseConfig), plugins: [ commonjs({ - extensions: ['.js'], + extensions: [".js"], }), resolve({ - extensions: ['.ts', '.js', '.json'], - mainFields: ['browser', 'module', 'main'], + extensions: [".ts", ".js", ".json"], + mainFields: ["browser", "module", "main"], }), babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - exclude: ['node_modules/**', /\/core-js\//], - babelHelpers: 'runtime', + extensions: [".js", ".jsx", ".ts", ".tsx"], + exclude: ["node_modules/**", /\/core-js\//], + babelHelpers: "runtime", presets: [ - '@babel/preset-react', - '@babel/preset-typescript', + "@babel/preset-react", + "@babel/preset-typescript", [ - '@babel/preset-env', + "@babel/preset-env", { shippedProposals: true, - useBuiltIns: 'usage', + useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [ [ - '@babel/plugin-transform-runtime', + "@babel/plugin-transform-runtime", { corejs: false, helpers: true, @@ -45,7 +52,7 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => }), alias({ entries: { - '@cubejs-client/core': '../cubejs-client-core/dist/index.js', + "@cubejs-client/core": "../cubejs-client-core/src/index.ts", }, }), ], @@ -69,26 +76,34 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => { ...baseConfig, plugins: [ + json(), + tsconfigPaths(), + resolve({ + extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + browser: false, + preferBuiltins: true, + }), + commonjs(), peerDepsExternal(), babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - exclude: 'node_modules/**', - babelHelpers: 'runtime', + extensions: [".js", ".jsx", ".ts", ".tsx"], + exclude: "node_modules/**", + babelHelpers: "runtime", presets: [ - '@babel/preset-react', - '@babel/preset-typescript', + "@babel/preset-react", + "@babel/preset-typescript", [ - '@babel/preset-env', + "@babel/preset-env", { shippedProposals: true, - useBuiltIns: 'usage', + useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [ [ - '@babel/plugin-transform-runtime', + "@babel/plugin-transform-runtime", { corejs: false, helpers: true, @@ -102,15 +117,22 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => output: [ { file: `packages/${name}/dist/${name}.js`, - format: 'cjs', + format: "cjs", sourcemap: true, - } + }, ], }, // ES module (for bundlers) build. { ...baseConfig, plugins: [ + tsconfigPaths(), + resolve({ + extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + browser: true, + preferBuiltins: false, + }), + commonjs(), peerDepsExternal(), babel({ extensions: ['.js', '.jsx', '.ts', '.tsx'], @@ -134,41 +156,41 @@ const bundle = (name, globalName, { globals = {}, ...baseConfig }, umdConfig) => }; export default bundle( - 'cubejs-client-core', - 'cubejs', + "cubejs-client-core", + "cubejs", { - input: 'packages/cubejs-client-core/dist/src/index.js', + input: "packages/cubejs-client-core/src/index.ts", }, { - input: 'packages/cubejs-client-core/dist/src/index.umd.js', + input: 'packages/cubejs-client-core/src/index.umd.ts', } ) - .concat( - bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { - input: 'packages/cubejs-client-ws-transport/src/index.ts', - }) - ) - .concat( - bundle('cubejs-client-react', 'cubejsReact', { - input: 'packages/cubejs-client-react/src/index.js', - external: ['react', 'prop-types'], - }) - ) - .concat( - bundle('cubejs-client-vue', 'cubejsVue', { - input: 'packages/cubejs-client-vue/src/index.js', - external: ['vue'], - globals: { - vue: 'Vue', - }, - }) - ) - .concat( - bundle('cubejs-client-vue3', 'cubejsVue3', { - input: 'packages/cubejs-client-vue3/src/index.js', - external: ['vue'], - globals: { - vue: 'Vue', - }, - }) - ); +.concat( + bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { + input: 'packages/cubejs-client-ws-transport/src/index.ts', + }) +) +.concat( + bundle('cubejs-client-react', 'cubejsReact', { + input: 'packages/cubejs-client-react/src/index.js', + external: ['react', 'prop-types'], + }) +) +.concat( + bundle('cubejs-client-vue', 'cubejsVue', { + input: 'packages/cubejs-client-vue/src/index.js', + external: ['vue'], + globals: { + vue: 'Vue', + }, + }) +) +.concat( + bundle('cubejs-client-vue3', 'cubejsVue3', { + input: 'packages/cubejs-client-vue3/src/index.js', + external: ['vue'], + globals: { + vue: 'Vue', + }, + }) +); diff --git a/yarn.lock b/yarn.lock index 55a6ad94fd477..cb9e8ac87c278 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22924,6 +22924,13 @@ rollup-plugin-peer-deps-external@^2.2.4: resolved "https://registry.yarnpkg.com/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz#8a420bbfd6dccc30aeb68c9bf57011f2f109570d" integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g== +rollup-plugin-tsconfig-paths@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-tsconfig-paths/-/rollup-plugin-tsconfig-paths-1.5.2.tgz#753bf970b14594b9ea1e93f3237eda380635f5b5" + integrity sha512-tyS7u2Md0eXKwbDfTuDDa1izciwqhOZsHzX7zYc5gKC1L7q5ozdSt+q1jjtD1dDqWyjrt8lZoiLtOQGhMHh1OQ== + dependencies: + typescript-paths "^1.5.1" + rollup@2.53.1: version "2.53.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.1.tgz#b60439efd1eb41bdb56630509bd99aae78b575d3" @@ -24082,16 +24089,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -24182,7 +24180,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -24210,13 +24208,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -25241,6 +25232,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript-paths@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/typescript-paths/-/typescript-paths-1.5.1.tgz#975cf5883915b24f9287315a8fa70b3b3451e32e" + integrity sha512-lYErSLCON2MSplVV5V/LBgD4UNjMgY3guATdFCZY2q1Nr6OZEu4q6zX/rYMsG1TaWqqQSszg6C9EU7AGWMDrIw== + "typescript@>=3 < 6": version "5.7.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" @@ -26313,7 +26309,7 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -26339,15 +26335,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 4e36c080931389199c7643fdd20da82a3bcca2aa Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 11:44:39 +0300 Subject: [PATCH 13/51] fix quotes --- rollup.config.js | 122 +++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 86a64846d38a6..e909e92a5f392 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,9 +1,9 @@ -import babel from "@rollup/plugin-babel"; -import resolve from "@rollup/plugin-node-resolve"; -import commonjs from "@rollup/plugin-commonjs"; -import alias from "@rollup/plugin-alias"; -import tsconfigPaths from "rollup-plugin-tsconfig-paths"; -import peerDepsExternal from "rollup-plugin-peer-deps-external"; +import babel from '@rollup/plugin-babel'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import alias from '@rollup/plugin-alias'; +import tsconfigPaths from 'rollup-plugin-tsconfig-paths'; +import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import json from '@rollup/plugin-json'; const bundle = ( @@ -16,31 +16,31 @@ const bundle = ( ...(umdConfig || baseConfig), plugins: [ commonjs({ - extensions: [".js"], + extensions: ['.js'], }), resolve({ - extensions: [".ts", ".js", ".json"], - mainFields: ["browser", "module", "main"], + extensions: ['.ts', '.js', '.json'], + mainFields: ['browser', 'module', 'main'], }), babel({ - extensions: [".js", ".jsx", ".ts", ".tsx"], - exclude: ["node_modules/**", /\/core-js\//], - babelHelpers: "runtime", + extensions: ['.js', '.jsx', '.ts', '.tsx'], + exclude: ['node_modules/**', /\/core-js\//], + babelHelpers: 'runtime', presets: [ - "@babel/preset-react", - "@babel/preset-typescript", + '@babel/preset-react', + '@babel/preset-typescript', [ - "@babel/preset-env", + '@babel/preset-env', { shippedProposals: true, - useBuiltIns: "usage", + useBuiltIns: 'usage', corejs: 3, }, ], ], plugins: [ [ - "@babel/plugin-transform-runtime", + '@babel/plugin-transform-runtime', { corejs: false, helpers: true, @@ -52,7 +52,7 @@ const bundle = ( }), alias({ entries: { - "@cubejs-client/core": "../cubejs-client-core/src/index.ts", + '@cubejs-client/core': '../cubejs-client-core/src/index.ts', }, }), ], @@ -79,31 +79,31 @@ const bundle = ( json(), tsconfigPaths(), resolve({ - extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], browser: false, preferBuiltins: true, }), commonjs(), peerDepsExternal(), babel({ - extensions: [".js", ".jsx", ".ts", ".tsx"], - exclude: "node_modules/**", - babelHelpers: "runtime", + extensions: ['.js', '.jsx', '.ts', '.tsx'], + exclude: 'node_modules/**', + babelHelpers: 'runtime', presets: [ - "@babel/preset-react", - "@babel/preset-typescript", + '@babel/preset-react', + '@babel/preset-typescript', [ - "@babel/preset-env", + '@babel/preset-env', { shippedProposals: true, - useBuiltIns: "usage", + useBuiltIns: 'usage', corejs: 3, }, ], ], plugins: [ [ - "@babel/plugin-transform-runtime", + '@babel/plugin-transform-runtime', { corejs: false, helpers: true, @@ -117,7 +117,7 @@ const bundle = ( output: [ { file: `packages/${name}/dist/${name}.js`, - format: "cjs", + format: 'cjs', sourcemap: true, }, ], @@ -128,7 +128,7 @@ const bundle = ( plugins: [ tsconfigPaths(), resolve({ - extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], browser: true, preferBuiltins: false, }), @@ -156,41 +156,41 @@ const bundle = ( }; export default bundle( - "cubejs-client-core", - "cubejs", + 'cubejs-client-core', + 'cubejs', { - input: "packages/cubejs-client-core/src/index.ts", + input: 'packages/cubejs-client-core/src/index.ts', }, { input: 'packages/cubejs-client-core/src/index.umd.ts', } ) -.concat( - bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { - input: 'packages/cubejs-client-ws-transport/src/index.ts', - }) -) -.concat( - bundle('cubejs-client-react', 'cubejsReact', { - input: 'packages/cubejs-client-react/src/index.js', - external: ['react', 'prop-types'], - }) -) -.concat( - bundle('cubejs-client-vue', 'cubejsVue', { - input: 'packages/cubejs-client-vue/src/index.js', - external: ['vue'], - globals: { - vue: 'Vue', - }, - }) -) -.concat( - bundle('cubejs-client-vue3', 'cubejsVue3', { - input: 'packages/cubejs-client-vue3/src/index.js', - external: ['vue'], - globals: { - vue: 'Vue', - }, - }) -); + .concat( + bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { + input: 'packages/cubejs-client-ws-transport/src/index.ts', + }) + ) + .concat( + bundle('cubejs-client-react', 'cubejsReact', { + input: 'packages/cubejs-client-react/src/index.js', + external: ['react', 'prop-types'], + }) + ) + .concat( + bundle('cubejs-client-vue', 'cubejsVue', { + input: 'packages/cubejs-client-vue/src/index.js', + external: ['vue'], + globals: { + vue: 'Vue', + }, + }) + ) + .concat( + bundle('cubejs-client-vue3', 'cubejsVue3', { + input: 'packages/cubejs-client-vue3/src/index.js', + external: ['vue'], + globals: { + vue: 'Vue', + }, + }) + ); From 92ec374f1e5e4f839ad7b9bbc21d7011de889aaa Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 11:45:52 +0300 Subject: [PATCH 14/51] fix ts config --- package.json | 1 - tsconfig.json | 6 +-- yarn.lock | 111 ++++++++++++++------------------------------------ 3 files changed, 34 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index fa93b047fefab..e767433b90b3a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ }, "author": "Cube Dev, Inc.", "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.17.0", "core-js": "^3.34.0", "lerna": "^8.2.1" }, diff --git a/tsconfig.json b/tsconfig.json index ebee4373ec76c..aa6ec37cefcb6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.base.json", "references": [ + { + "path": "packages/cubejs-client-core" + }, { "path": "packages/cubejs-client-ws-transport" }, @@ -28,9 +31,6 @@ { "path": "packages/cubejs-cubestore-driver" }, - { - "path": "packages/cubejs-client-core" - }, { "path": "packages/cubejs-query-orchestrator" }, diff --git a/yarn.lock b/yarn.lock index cb9e8ac87c278..39592e72eabfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7837,7 +7837,7 @@ resolved "https://registry.yarnpkg.com/@types/json-bigint/-/json-bigint-1.0.1.tgz#201062a6990119a8cc18023cfe1fed12fc2fc8a7" integrity sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw== -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -8306,20 +8306,6 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^4.17.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== - dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.1.0" - semver "^7.3.5" - tsutils "^3.21.0" - "@typescript-eslint/eslint-plugin@^6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz#2a647d278bb48bf397fef07ba0507612ff9dd812" @@ -8337,18 +8323,6 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== - dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - "@typescript-eslint/parser@^6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.12.0.tgz#9fb21ed7d88065a4a2ee21eb80b8578debb8217c" @@ -8360,14 +8334,6 @@ "@typescript-eslint/visitor-keys" "6.12.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - "@typescript-eslint/scope-manager@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz#5833a16dbe19cfbad639d4d33bcca5e755c7044b" @@ -8386,29 +8352,11 @@ debug "^4.3.4" ts-api-utils "^1.0.1" -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== - "@typescript-eslint/types@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.12.0.tgz#ffc5297bcfe77003c8b7b545b51c2505748314ac" integrity sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q== -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" - "@typescript-eslint/typescript-estree@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz#764ccc32598549e5b48ec99e3b85f89b1385310c" @@ -8435,14 +8383,6 @@ "@typescript-eslint/typescript-estree" "6.12.0" semver "^7.5.4" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== - dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" - "@typescript-eslint/visitor-keys@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz#5877950de42a0f3344261b7a1eee15417306d7e9" @@ -13622,13 +13562,6 @@ eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -15687,7 +15620,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -24089,7 +24022,16 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -24180,7 +24122,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -24208,6 +24150,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -25056,7 +25005,7 @@ tslib@2.6.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== -tslib@^1, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1, tslib@^1.10.0, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -25071,13 +25020,6 @@ tslib@~2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - tuf-js@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-2.2.1.tgz#fdd8794b644af1a75c7aaa2b197ddffeb2911b56" @@ -26309,7 +26251,7 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -26335,6 +26277,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From cc9c3f4642b1925a8827c1131246e49c7fbd96cf Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 12:32:43 +0300 Subject: [PATCH 15/51] return back lint plugin --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e767433b90b3a..0ef866947b44f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@rollup/plugin-commonjs": "^17.1.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^11.2.0", + "@typescript-eslint/eslint-plugin": "^6.12.0", "@types/fs-extra": "^9.0.1", "@types/jest": "^27", "flush-promises": "^1.0.2", From 79e6883767f37b50f42b41782a63c2968f2301dd Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 12:48:11 +0300 Subject: [PATCH 16/51] delete removed rule --- packages/cubejs-linter/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cubejs-linter/index.js b/packages/cubejs-linter/index.js index 159d6f99aceb7..9d2c470987ab1 100644 --- a/packages/cubejs-linter/index.js +++ b/packages/cubejs-linter/index.js @@ -105,9 +105,7 @@ module.exports = { '@typescript-eslint/explicit-member-accessibility': 'error', 'no-shadow': 'off', '@typescript-eslint/no-shadow': ['error', { ignoreTypeValueShadow: true }], - // 'no-duplicate-imports': 'off', - '@typescript-eslint/no-duplicate-imports': 'error', semi: 'off', '@typescript-eslint/semi': 'error', }, From 3ce5045f6e4e4d8351744000c8bc9240e9fb9901 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 12:55:48 +0300 Subject: [PATCH 17/51] fix lint errors --- .../cubejs-client-core/src/ProgressResult.ts | 2 +- .../cubejs-client-core/src/RequestError.ts | 4 ++-- packages/cubejs-client-core/src/ResultSet.ts | 12 +++++----- packages/cubejs-client-core/src/index.ts | 22 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/cubejs-client-core/src/ProgressResult.ts b/packages/cubejs-client-core/src/ProgressResult.ts index a8ecad1cfb47f..ee05976d8278d 100644 --- a/packages/cubejs-client-core/src/ProgressResult.ts +++ b/packages/cubejs-client-core/src/ProgressResult.ts @@ -1,7 +1,7 @@ import { ProgressResponse } from './types'; export default class ProgressResult { - progressResponse: ProgressResponse; + private progressResponse: ProgressResponse; public constructor(progressResponse: ProgressResponse) { this.progressResponse = progressResponse; diff --git a/packages/cubejs-client-core/src/RequestError.ts b/packages/cubejs-client-core/src/RequestError.ts index a0c86d59de78c..acd57c515af70 100644 --- a/packages/cubejs-client-core/src/RequestError.ts +++ b/packages/cubejs-client-core/src/RequestError.ts @@ -1,7 +1,7 @@ export default class RequestError extends Error { - response: any; + public response: any; - status: number; + public status: number; public constructor(message: string, response: any, status: number) { super(message); diff --git a/packages/cubejs-client-core/src/ResultSet.ts b/packages/cubejs-client-core/src/ResultSet.ts index a00ea2900390a..54b734d3265cc 100644 --- a/packages/cubejs-client-core/src/ResultSet.ts +++ b/packages/cubejs-client-core/src/ResultSet.ts @@ -73,17 +73,17 @@ export type ResultSetOptions = { * Provides a convenient interface for data manipulation. */ export default class ResultSet> { - loadResponse: LoadResponse; + private readonly loadResponse: LoadResponse; - loadResponses: LoadResponseResult[]; + private readonly loadResponses: LoadResponseResult[]; - queryType: QueryType; + private readonly queryType: QueryType; - parseDateMeasures: boolean | undefined; + private readonly parseDateMeasures: boolean | undefined; - options: {}; + private readonly options: {}; - backwardCompatibleData: Record[][]; + private readonly backwardCompatibleData: Record[][]; public static measureFromAxis(axisValues: string[]): string { return axisValues[axisValues.length - 1]; diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index bb80a34a938e1..80517041ce579 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -145,27 +145,27 @@ export type CubeApiOptions = { * Main class for accessing Cube API */ class CubeApi { - apiToken: string | (() => Promise) | (CubeApiOptions & any[]) | undefined; + private readonly apiToken: string | (() => Promise) | (CubeApiOptions & any[]) | undefined; - apiUrl: string; + private readonly apiUrl: string; - method: TransportOptions['method']; + private readonly method: TransportOptions['method']; - headers: TransportOptions['headers']; + private readonly headers: TransportOptions['headers']; - credentials: TransportOptions['credentials']; + private readonly credentials: TransportOptions['credentials']; - transport: ITransport; + private readonly transport: ITransport; - pollInterval: number; + private readonly pollInterval: number; - parseDateMeasures: boolean | undefined; + private readonly parseDateMeasures: boolean | undefined; - castNumerics: boolean; + private readonly castNumerics: boolean; - networkErrorRetries: number; + private readonly networkErrorRetries: number; - updateAuthorizationPromise: Promise | null; + private updateAuthorizationPromise: Promise | null; public constructor(apiToken: string | (() => Promise) | undefined, options: CubeApiOptions); From 19c5c275a89dffe0c4b537f8858ba2bbd9398fd4 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:05:15 +0300 Subject: [PATCH 18/51] fix lint error in BaseDriver --- packages/cubejs-base-driver/src/BaseDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-base-driver/src/BaseDriver.ts b/packages/cubejs-base-driver/src/BaseDriver.ts index d9b5123604c8d..3b7fc4cd83784 100644 --- a/packages/cubejs-base-driver/src/BaseDriver.ts +++ b/packages/cubejs-base-driver/src/BaseDriver.ts @@ -375,9 +375,9 @@ export abstract class BaseDriver implements DriverInterface { return undefined; } - abstract testConnection(): Promise; + public abstract testConnection(): Promise; - abstract query(_query: string, _values?: unknown[], _options?: QueryOptions): Promise; + public abstract query(_query: string, _values?: unknown[], _options?: QueryOptions): Promise; // eslint-disable-next-line @typescript-eslint/no-unused-vars public async streamQuery(sql: string, values: string[]): Promise { From dda3ca14d23457af1324f26d11c885be026f1609 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:06:43 +0300 Subject: [PATCH 19/51] fix linting in api-gateway --- .../cubejs-api-gateway/test/helpers/prepareAnnotation.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cubejs-api-gateway/test/helpers/prepareAnnotation.test.ts b/packages/cubejs-api-gateway/test/helpers/prepareAnnotation.test.ts index 83156c52c4933..2977fdce0c74c 100644 --- a/packages/cubejs-api-gateway/test/helpers/prepareAnnotation.test.ts +++ b/packages/cubejs-api-gateway/test/helpers/prepareAnnotation.test.ts @@ -6,7 +6,6 @@ /* globals describe,test,expect */ /* eslint-disable import/no-duplicates */ -/* eslint-disable @typescript-eslint/no-duplicate-imports */ import { MemberType } from '../../src/types/enums'; import prepareAnnotationDef From 6acf830c1178102579ff9a17e04c96db4bf7224f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:08:06 +0300 Subject: [PATCH 20/51] fix linting in BaseQueueDriver --- .../src/orchestrator/BaseQueueDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-query-orchestrator/src/orchestrator/BaseQueueDriver.ts b/packages/cubejs-query-orchestrator/src/orchestrator/BaseQueueDriver.ts index 9d838553c0337..dea283349bd43 100644 --- a/packages/cubejs-query-orchestrator/src/orchestrator/BaseQueueDriver.ts +++ b/packages/cubejs-query-orchestrator/src/orchestrator/BaseQueueDriver.ts @@ -14,7 +14,7 @@ export abstract class BaseQueueDriver implements QueueDriverInterface { return getCacheHash(queryKey, this.processUid); } - abstract createConnection(): Promise; + public abstract createConnection(): Promise; - abstract release(connection: QueueDriverConnectionInterface): void; + public abstract release(connection: QueueDriverConnectionInterface): void; } From b2fcd6f826da238fc248d3f93c02d3519d15bb1b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:08:59 +0300 Subject: [PATCH 21/51] fix linting in QueryTestAbstract --- packages/cubejs-testing-shared/src/query-test.abstract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-testing-shared/src/query-test.abstract.ts b/packages/cubejs-testing-shared/src/query-test.abstract.ts index bd9a929e6872f..cb6cf9a624991 100644 --- a/packages/cubejs-testing-shared/src/query-test.abstract.ts +++ b/packages/cubejs-testing-shared/src/query-test.abstract.ts @@ -13,7 +13,7 @@ export const prepareCompiler = (content: any, options?: any) => originalPrepareC }, { adapter: 'postgres', ...options }); export abstract class QueryTestAbstract { - abstract getQueryClass(): any; + public abstract getQueryClass(): any; protected getQuery(compilers: any, options: any): BaseQuery { const QueryClass = this.getQueryClass(); From 31cdf1a9be0bc2542bc69959d2fff3ed48ed9ba8 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:12:34 +0300 Subject: [PATCH 22/51] more fixes --- packages/cubejs-client-core/src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index 80517041ce579..9a84e6033edfc 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -659,12 +659,12 @@ class CubeApi { export default (apiToken: string | (() => Promise), options: CubeApiOptions) => new CubeApi(apiToken, options); export { CubeApi }; -export * from './Meta'; -export * from './SqlQuery'; -export * from './RequestError'; -export * from './ProgressResult'; +export { default as Meta } from './Meta'; +export { default as SqlQuery } from './SqlQuery'; +export { default as RequestError } from './RequestError'; +export { default as ProgressResult } from './ProgressResult'; export { default as ResultSet } from './ResultSet'; -export * from './HttpTransport'; +export { default as HttpTransport } from './HttpTransport'; export * from './utils'; export * from './time'; export * from './types'; From 5c5280ca63f65f949a89505ca3c3fb07ab737d11 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:19:40 +0300 Subject: [PATCH 23/51] more type fixes --- packages/cubejs-client-core/src/HttpTransport.ts | 2 +- packages/cubejs-client-core/src/index.ts | 2 +- packages/cubejs-client-core/src/types.ts | 2 +- packages/cubejs-client-core/src/utils.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-client-core/src/HttpTransport.ts b/packages/cubejs-client-core/src/HttpTransport.ts index bcb9b6a602810..466eac22373d9 100644 --- a/packages/cubejs-client-core/src/HttpTransport.ts +++ b/packages/cubejs-client-core/src/HttpTransport.ts @@ -40,7 +40,7 @@ export interface ITransport { /** * Default transport implementation. */ -class HttpTransport implements ITransport { +export class HttpTransport implements ITransport { public authorization: TransportOptions['authorization']; protected apiUrl: TransportOptions['apiUrl']; diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index 9a84e6033edfc..e0a67516479b6 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -664,7 +664,7 @@ export { default as SqlQuery } from './SqlQuery'; export { default as RequestError } from './RequestError'; export { default as ProgressResult } from './ProgressResult'; export { default as ResultSet } from './ResultSet'; -export { default as HttpTransport } from './HttpTransport'; +export * from './HttpTransport'; export * from './utils'; export * from './time'; export * from './types'; diff --git a/packages/cubejs-client-core/src/types.ts b/packages/cubejs-client-core/src/types.ts index efbe83e829225..fe6ccb94a124e 100644 --- a/packages/cubejs-client-core/src/types.ts +++ b/packages/cubejs-client-core/src/types.ts @@ -328,7 +328,7 @@ export type TOrderMember = { order: QueryOrder | 'none'; }; -type TCubeMemberType = 'time' | 'number' | 'string' | 'boolean'; +export type TCubeMemberType = 'time' | 'number' | 'string' | 'boolean'; // @see BaseCubeMember // @deprecated diff --git a/packages/cubejs-client-core/src/utils.ts b/packages/cubejs-client-core/src/utils.ts index b419269d3f8d2..d2cb0e3ef98be 100644 --- a/packages/cubejs-client-core/src/utils.ts +++ b/packages/cubejs-client-core/src/utils.ts @@ -1,5 +1,5 @@ import { clone, equals, fromPairs, indexBy, prop, toPairs } from 'ramda'; -import { DeeplyReadonly } from 'src'; +import { DeeplyReadonly } from './index'; import { DEFAULT_GRANULARITY } from './time'; import { Filter, From 3e8c556ac5cdd89d4f6f18b24198526d817f9585 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 14 May 2025 17:57:25 +0300 Subject: [PATCH 24/51] Fix more types --- packages/cubejs-client-core/src/Meta.ts | 22 +++++--------------- packages/cubejs-client-core/src/ResultSet.ts | 18 ++++++++-------- packages/cubejs-client-core/src/types.ts | 10 +++++++-- packages/cubejs-client-core/src/utils.ts | 6 +++--- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/packages/cubejs-client-core/src/Meta.ts b/packages/cubejs-client-core/src/Meta.ts index b22438ced8567..f440e4afc4aae 100644 --- a/packages/cubejs-client-core/src/Meta.ts +++ b/packages/cubejs-client-core/src/Meta.ts @@ -10,15 +10,16 @@ import { TCubeMemberByType, Query, FilterOperator, - TCubeSegment, NotFoundMember + TCubeSegment, + NotFoundMember, } from './types'; import { DeeplyReadonly } from './index'; export interface CubeMemberWrapper { cubeName: string; cubeTitle: string; - type?: 'view' | 'cube'; - public?: boolean; + type: 'view' | 'cube'; + public: boolean; members: T[]; } @@ -149,19 +150,6 @@ export default class Meta { }); }); - const timeDimensionMembers = cube.dimensions.filter((m) => m.type === 'time'); - - memo.timeDimensions = [ - ...memo.timeDimensions, - { - cubeName: cube.name, - cubeTitle: cube.title, - type: cube.type, - public: cube.public, - members: timeDimensionMembers, - }, - ]; - return memo; }, { @@ -169,7 +157,7 @@ export default class Meta { dimensions: [], segments: [], timeDimensions: [], - } + } as AggregatedMembers ); } diff --git a/packages/cubejs-client-core/src/ResultSet.ts b/packages/cubejs-client-core/src/ResultSet.ts index 54b734d3265cc..f67be48f2545e 100644 --- a/packages/cubejs-client-core/src/ResultSet.ts +++ b/packages/cubejs-client-core/src/ResultSet.ts @@ -20,7 +20,7 @@ import { DrillDownLocator, LoadResponse, LoadResponseResult, Pivot, - PivotConfig, + PivotConfig, PivotConfigFull, PivotQuery, PivotRow, Query, @@ -72,7 +72,7 @@ export type ResultSetOptions = { /** * Provides a convenient interface for data manipulation. */ -export default class ResultSet> { +export default class ResultSet = any> { private readonly loadResponse: LoadResponse; private readonly loadResponses: LoadResponseResult[]; @@ -365,7 +365,7 @@ export default class ResultSet> { return axisValues.map(formatValue).join(delimiter); } - public static getNormalizedPivotConfig(query: PivotQuery, pivotConfig?: Partial): PivotConfig { + public static getNormalizedPivotConfig(query: PivotQuery, pivotConfig?: PivotConfig): PivotConfigFull { const defaultPivotConfig: PivotConfig = { x: [], y: [], @@ -388,7 +388,7 @@ export default class ResultSet> { y: [] }); - const normalizedPivotConfig = mergeDeepLeft(pivotConfig, defaultPivotConfig) as PivotConfig; + const normalizedPivotConfig = mergeDeepLeft(pivotConfig, defaultPivotConfig) as PivotConfigFull; const substituteTimeDimensionMembers = (axis: string[]) => axis.map( subDim => ( @@ -431,7 +431,7 @@ export default class ResultSet> { return normalizedPivotConfig; } - public normalizePivotConfig(pivotConfig?: Partial): PivotConfig { + public normalizePivotConfig(pivotConfig?: PivotConfig): PivotConfigFull { return ResultSet.getNormalizedPivotConfig(this.loadResponse.pivotQuery, pivotConfig); } @@ -527,7 +527,7 @@ export default class ResultSet> { * ``` * @returns An array of pivoted rows. */ - public pivot(pivotConfig?: Partial): PivotRow[] { + public pivot(pivotConfig?: PivotConfig): PivotRow[] { const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig); const { pivotQuery: query } = this.loadResponse; @@ -716,7 +716,7 @@ export default class ResultSet> { * ] * ``` */ - public chartPivot(pivotConfig?: Partial): ChartPivotRow[] { + public chartPivot(pivotConfig?: PivotConfig): ChartPivotRow[] { const validate = (value: string) => { if (this.parseDateMeasures && LocalDateRegex.test(value)) { return new Date(value); @@ -776,7 +776,7 @@ export default class ResultSet> { * ``` * @returns An array of pivoted rows */ - public tablePivot(pivotConfig?: Partial): Array<{ [key: string]: string | number | boolean }> { + public tablePivot(pivotConfig?: PivotConfig): Array<{ [key: string]: string | number | boolean }> { const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig || {}); const isMeasuresPresent = normalizedPivotConfig.x.concat(normalizedPivotConfig.y).includes('measures'); @@ -1043,7 +1043,7 @@ export default class ResultSet> { * ``` * @returns An array of series names */ - public seriesNames(pivotConfig?: Partial): SeriesNamesColumn[] { + public seriesNames(pivotConfig?: PivotConfig): SeriesNamesColumn[] { const normalizedPivotConfig = this.normalizePivotConfig(pivotConfig); const measures = this.loadResponses .map(r => r.annotation.measures) diff --git a/packages/cubejs-client-core/src/types.ts b/packages/cubejs-client-core/src/types.ts index fe6ccb94a124e..1d408fd4aabb9 100644 --- a/packages/cubejs-client-core/src/types.ts +++ b/packages/cubejs-client-core/src/types.ts @@ -1,5 +1,6 @@ import Meta from './Meta'; import { TimeDimensionGranularity } from './time'; +import { TransportOptions } from './HttpTransport'; export type QueryOrder = 'asc' | 'desc' | 'none'; @@ -245,11 +246,11 @@ export type PivotConfig = { /** * Dimensions to put on **x** or **rows** axis. */ - x: string[]; + x?: string[]; /** * Dimensions to put on **y** or **columns** axis. */ - y: string[]; + y?: string[]; /** * If `true` missing dates on the time dimensions will be filled with fillWithValue or `0` by default for all measures.Note: the `fillMissingDates` option set to `true` will override any **order** applied to the query */ @@ -264,6 +265,11 @@ export type PivotConfig = { aliasSeries?: string[]; }; +export type PivotConfigFull = Omit & { + x: string[]; + y: string[]; +}; + export type DrillDownLocator = { xValues: string[]; yValues?: string[]; diff --git a/packages/cubejs-client-core/src/utils.ts b/packages/cubejs-client-core/src/utils.ts index d2cb0e3ef98be..790bfe6f0a6ed 100644 --- a/packages/cubejs-client-core/src/utils.ts +++ b/packages/cubejs-client-core/src/utils.ts @@ -287,10 +287,10 @@ export function movePivotItem( ): PivotConfig { const nextPivotConfig = { ...pivotConfig, - x: [...pivotConfig.x], - y: [...pivotConfig.y], + x: [...(pivotConfig.x || [])], + y: [...(pivotConfig.y || [])], }; - const id = pivotConfig[sourceAxis][sourceIndex]; + const id = pivotConfig[sourceAxis]![sourceIndex]; const lastIndex = nextPivotConfig[destinationAxis].length - 1; if (id === 'measures') { From 243a92e5dcd14ec76b4881d82852f4cf9f771b40 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 16:53:26 +0300 Subject: [PATCH 25/51] fix load() --- packages/cubejs-client-core/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index e0a67516479b6..be9f00cbccb80 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -260,7 +260,10 @@ class CubeApi { const checkMutex = async () => { const requestInstance = await requestPromise; - if (options?.mutexObj?.[mutexKey] !== mutexValue) { + if (options && + options.mutexObj && + options.mutexObj[mutexKey] !== mutexValue + ) { unsubscribed = true; if (requestInstance.unsubscribe) { await requestInstance.unsubscribe(); From 223e7d1263b781c965fc42165e0a3395dea4395b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 17:21:55 +0300 Subject: [PATCH 26/51] fix vue clients --- packages/cubejs-client-vue/src/QueryBuilder.js | 4 ++-- packages/cubejs-client-vue3/src/QueryBuilder.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-client-vue/src/QueryBuilder.js b/packages/cubejs-client-vue/src/QueryBuilder.js index 3f3a998073439..5fd15fdb3abed 100644 --- a/packages/cubejs-client-vue/src/QueryBuilder.js +++ b/packages/cubejs-client-vue/src/QueryBuilder.js @@ -658,8 +658,8 @@ export default { .dryRun(query, { mutexObj: this.mutex, }) - .then(({ pivotQuery }) => { - const pivotConfig = ResultSet.getNormalizedPivotConfig(pivotQuery, this.pivotConfig); + .then((result) => { + const pivotConfig = ResultSet.getNormalizedPivotConfig(result?.pivotQuery, this.pivotConfig); if (!equals(pivotConfig, this.pivotConfig)) { this.pivotConfig = pivotConfig; diff --git a/packages/cubejs-client-vue3/src/QueryBuilder.js b/packages/cubejs-client-vue3/src/QueryBuilder.js index 3877e75ed9819..d0efd07884bc1 100644 --- a/packages/cubejs-client-vue3/src/QueryBuilder.js +++ b/packages/cubejs-client-vue3/src/QueryBuilder.js @@ -648,8 +648,8 @@ export default { .dryRun(query, { mutexObj: this.mutex, }) - .then(({ pivotQuery }) => { - const pivotConfig = ResultSet.getNormalizedPivotConfig(pivotQuery, this.pivotConfig); + .then((result) => { + const pivotConfig = ResultSet.getNormalizedPivotConfig(result?.pivotQuery, this.pivotConfig); if (!equals(pivotConfig, this.pivotConfig)) { this.pivotConfig = pivotConfig; From 8b2f6753e2290cbf2c8dfc40e0b5952eb81ff33e Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 17:22:06 +0300 Subject: [PATCH 27/51] fix getNormalizedPivotConfig() --- packages/cubejs-client-core/src/ResultSet.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-client-core/src/ResultSet.ts b/packages/cubejs-client-core/src/ResultSet.ts index f67be48f2545e..a6586b06c1a66 100644 --- a/packages/cubejs-client-core/src/ResultSet.ts +++ b/packages/cubejs-client-core/src/ResultSet.ts @@ -365,7 +365,7 @@ export default class ResultSet = any> { return axisValues.map(formatValue).join(delimiter); } - public static getNormalizedPivotConfig(query: PivotQuery, pivotConfig?: PivotConfig): PivotConfigFull { + public static getNormalizedPivotConfig(query?: PivotQuery, pivotConfig?: PivotConfig): PivotConfigFull { const defaultPivotConfig: PivotConfig = { x: [], y: [], @@ -376,9 +376,9 @@ export default class ResultSet = any> { const { measures = [], dimensions = [] - } = query; + } = query || {}; - const timeDimensions = (query.timeDimensions || []).filter(td => !!td.granularity); + const timeDimensions = (query?.timeDimensions || []).filter(td => !!td.granularity); pivotConfig = pivotConfig || (timeDimensions.length ? { x: timeDimensions.map(td => ResultSet.timeDimensionMember(td)), @@ -396,7 +396,7 @@ export default class ResultSet = any> { timeDimensions.find(td => td.dimension === subDim) && !dimensions.find(d => d === subDim) ) ? - ResultSet.timeDimensionMember((query.timeDimensions || []).find(td => td.dimension === subDim)!) : + ResultSet.timeDimensionMember((query?.timeDimensions || []).find(td => td.dimension === subDim)!) : subDim ) ); From 66814253845a2e65e6786388e85ab91d82ef2364 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 17:30:25 +0300 Subject: [PATCH 28/51] trying to fix dev docker img --- packages/cubejs-docker/dev.Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-docker/dev.Dockerfile b/packages/cubejs-docker/dev.Dockerfile index 35bb0ca527d7b..753fc0ffae690 100644 --- a/packages/cubejs-docker/dev.Dockerfile +++ b/packages/cubejs-docker/dev.Dockerfile @@ -166,7 +166,8 @@ COPY packages/cubejs-client-ngx/ packages/cubejs-client-ngx/ COPY packages/cubejs-client-ws-transport/ packages/cubejs-client-ws-transport/ COPY packages/cubejs-playground/ packages/cubejs-playground/ -RUN yarn build +RUN yarn run build +RUN yarn run tsc RUN yarn lerna run build RUN find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + From af47ed6a4c4eb1ee82659a5d1839721b0aa4b1b6 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 17:39:03 +0300 Subject: [PATCH 29/51] exclude index.umd.js from coverage --- packages/cubejs-client-core/jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cubejs-client-core/jest.config.js b/packages/cubejs-client-core/jest.config.js index 4611f9c2f14da..69ec3dc057afa 100644 --- a/packages/cubejs-client-core/jest.config.js +++ b/packages/cubejs-client-core/jest.config.js @@ -4,4 +4,5 @@ const base = require('../../jest.base.config'); module.exports = { ...base, rootDir: '.', + collectCoverageFrom: [...base.collectCoverageFrom, '!dist/src/index.umd.js'] }; From e79d3906dcd0d2f9b0b5bd21dcc19516594c82b8 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 17:52:49 +0300 Subject: [PATCH 30/51] trying to fix dev docker img --- .dockerignore | 1 + packages/cubejs-docker/dev.Dockerfile | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.dockerignore b/.dockerignore index bfc7f4fbc857e..ad3e938a1db19 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ ** !package.json !tsconfig.base.json +!tsconfig.json !rollup.config.js !yarn.lock !lerna.json diff --git a/packages/cubejs-docker/dev.Dockerfile b/packages/cubejs-docker/dev.Dockerfile index 753fc0ffae690..b2c11e5575b60 100644 --- a/packages/cubejs-docker/dev.Dockerfile +++ b/packages/cubejs-docker/dev.Dockerfile @@ -29,6 +29,7 @@ COPY package.json . COPY lerna.json . COPY yarn.lock . COPY tsconfig.base.json . +COPY tsconfig.json . COPY rollup.config.js . COPY packages/cubejs-linter packages/cubejs-linter @@ -41,6 +42,7 @@ COPY packages/cubejs-base-driver/package.json packages/cubejs-base-driver/packag COPY packages/cubejs-backend-native/package.json packages/cubejs-backend-native/package.json COPY packages/cubejs-testing-shared/package.json packages/cubejs-testing-shared/package.json COPY packages/cubejs-backend-cloud/package.json packages/cubejs-backend-cloud/package.json +COPY packages/cubejs-backend-maven/package.json packages/cubejs-backend-maven/package.json COPY packages/cubejs-api-gateway/package.json packages/cubejs-api-gateway/package.json COPY packages/cubejs-athena-driver/package.json packages/cubejs-athena-driver/package.json COPY packages/cubejs-bigquery-driver/package.json packages/cubejs-bigquery-driver/package.json @@ -118,6 +120,7 @@ COPY packages/cubejs-base-driver/ packages/cubejs-base-driver/ COPY packages/cubejs-backend-native/ packages/cubejs-backend-native/ COPY packages/cubejs-testing-shared/ packages/cubejs-testing-shared/ COPY packages/cubejs-backend-cloud/ packages/cubejs-backend-cloud/ +COPY packages/cubejs-backend-maven/ packages/cubejs-backend-maven/ COPY packages/cubejs-api-gateway/ packages/cubejs-api-gateway/ COPY packages/cubejs-athena-driver/ packages/cubejs-athena-driver/ COPY packages/cubejs-bigquery-driver/ packages/cubejs-bigquery-driver/ From 8fbb37a825a7abd20caa63a930e80532cd27f29a Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 19:21:00 +0300 Subject: [PATCH 31/51] revert all docker changes in docker file --- packages/cubejs-docker/dev.Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/cubejs-docker/dev.Dockerfile b/packages/cubejs-docker/dev.Dockerfile index b2c11e5575b60..35bb0ca527d7b 100644 --- a/packages/cubejs-docker/dev.Dockerfile +++ b/packages/cubejs-docker/dev.Dockerfile @@ -29,7 +29,6 @@ COPY package.json . COPY lerna.json . COPY yarn.lock . COPY tsconfig.base.json . -COPY tsconfig.json . COPY rollup.config.js . COPY packages/cubejs-linter packages/cubejs-linter @@ -42,7 +41,6 @@ COPY packages/cubejs-base-driver/package.json packages/cubejs-base-driver/packag COPY packages/cubejs-backend-native/package.json packages/cubejs-backend-native/package.json COPY packages/cubejs-testing-shared/package.json packages/cubejs-testing-shared/package.json COPY packages/cubejs-backend-cloud/package.json packages/cubejs-backend-cloud/package.json -COPY packages/cubejs-backend-maven/package.json packages/cubejs-backend-maven/package.json COPY packages/cubejs-api-gateway/package.json packages/cubejs-api-gateway/package.json COPY packages/cubejs-athena-driver/package.json packages/cubejs-athena-driver/package.json COPY packages/cubejs-bigquery-driver/package.json packages/cubejs-bigquery-driver/package.json @@ -120,7 +118,6 @@ COPY packages/cubejs-base-driver/ packages/cubejs-base-driver/ COPY packages/cubejs-backend-native/ packages/cubejs-backend-native/ COPY packages/cubejs-testing-shared/ packages/cubejs-testing-shared/ COPY packages/cubejs-backend-cloud/ packages/cubejs-backend-cloud/ -COPY packages/cubejs-backend-maven/ packages/cubejs-backend-maven/ COPY packages/cubejs-api-gateway/ packages/cubejs-api-gateway/ COPY packages/cubejs-athena-driver/ packages/cubejs-athena-driver/ COPY packages/cubejs-bigquery-driver/ packages/cubejs-bigquery-driver/ @@ -169,8 +166,7 @@ COPY packages/cubejs-client-ngx/ packages/cubejs-client-ngx/ COPY packages/cubejs-client-ws-transport/ packages/cubejs-client-ws-transport/ COPY packages/cubejs-playground/ packages/cubejs-playground/ -RUN yarn run build -RUN yarn run tsc +RUN yarn build RUN yarn lerna run build RUN find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + From 23eb2d5d664d917e1d64451e47e245a487c4d350 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 19:31:59 +0300 Subject: [PATCH 32/51] fix client builds --- package.json | 2 +- packages/cubejs-client-core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0ef866947b44f..9b9ac3a3580ea 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ ] }, "scripts": { - "build": "rollup -c", + "build": "yarn lerna run build:client-core && rollup -c", "watch": "rollup -c -w", "watch-local": "CUBEJS_API_URL=http://localhost:6020/cubejs-api/v1 rollup -c -w", "lint:npm": "yarn npmPkgJsonLint packages/*/package.json rust/package.json", diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 039c2cd872886..4de41ba30a35d 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -21,7 +21,7 @@ "uuid": "^8.3.2" }, "scripts": { - "build": "rm -rf dist && npm run tsc", + "build:client-core": "rm -rf dist && npm run tsc", "tsc": "tsc", "watch": "tsc -w", "test": "npm run unit", From 4ba64686a0e1a689b84796d86b125abca0bde032 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 10:21:12 -0700 Subject: [PATCH 33/51] entry point fixes --- packages/cubejs-client-core/package.json | 11 +- rollup.config.js | 127 ++++++++++++----------- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 4de41ba30a35d..48920aae3f56b 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -7,11 +7,16 @@ "url": "https://github.com/cube-js/cube.git", "directory": "packages/cubejs-client-core" }, - "description": "cube.js client", - "main": "dist/src/index.js", - "module": "dist/cubejs-client-core.esm.js", + "description": "Cube client", + "main": "dist/cubejs-client-core.cjs.js", + "module": "dist/src/index.js", + "browser": "dist/cubejs-client-core.umd.js", "typings": "dist/src/index.d.ts", "author": "Cube Dev, Inc.", + "exports": { + "import": "./dist/src/index.js", + "require": "./dist/cubejs-client-core.cjs.js" + }, "dependencies": { "core-js": "^3.6.5", "cross-fetch": "^3.0.2", diff --git a/rollup.config.js b/rollup.config.js index e909e92a5f392..113eef30ec53d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,10 +1,10 @@ -import babel from '@rollup/plugin-babel'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import alias from '@rollup/plugin-alias'; -import tsconfigPaths from 'rollup-plugin-tsconfig-paths'; -import peerDepsExternal from 'rollup-plugin-peer-deps-external'; -import json from '@rollup/plugin-json'; +import babel from "@rollup/plugin-babel"; +import resolve from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import alias from "@rollup/plugin-alias"; +import tsconfigPaths from "rollup-plugin-tsconfig-paths"; +import peerDepsExternal from "rollup-plugin-peer-deps-external"; +import json from "@rollup/plugin-json"; const bundle = ( name, @@ -16,31 +16,31 @@ const bundle = ( ...(umdConfig || baseConfig), plugins: [ commonjs({ - extensions: ['.js'], + extensions: [".js"], }), resolve({ - extensions: ['.ts', '.js', '.json'], - mainFields: ['browser', 'module', 'main'], + extensions: [".ts", ".js", ".json"], + mainFields: ["browser", "module", "main"], }), babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - exclude: ['node_modules/**', /\/core-js\//], - babelHelpers: 'runtime', + extensions: [".js", ".jsx", ".ts", ".tsx"], + exclude: ["node_modules/**", /\/core-js\//], + babelHelpers: "runtime", presets: [ - '@babel/preset-react', - '@babel/preset-typescript', + "@babel/preset-react", + "@babel/preset-typescript", [ - '@babel/preset-env', + "@babel/preset-env", { shippedProposals: true, - useBuiltIns: 'usage', + useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [ [ - '@babel/plugin-transform-runtime', + "@babel/plugin-transform-runtime", { corejs: false, helpers: true, @@ -52,22 +52,25 @@ const bundle = ( }), alias({ entries: { - '@cubejs-client/core': '../cubejs-client-core/src/index.ts', + "@cubejs-client/core": "../cubejs-client-core/src/index.ts", }, }), ], }; - return [ + // Will be built with typescript + const skipEsModule = name === "cubejs-client-core"; + + const config = [ // browser-friendly UMD build { ...baseUmdConfig, output: [ { file: `packages/${name}/dist/${name}.umd.js`, - format: 'umd', + format: "umd", name: globalName, - exports: 'auto', + exports: "auto", sourcemap: true, }, ], @@ -79,31 +82,31 @@ const bundle = ( json(), tsconfigPaths(), resolve({ - extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], + extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], browser: false, preferBuiltins: true, }), commonjs(), peerDepsExternal(), babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - exclude: 'node_modules/**', - babelHelpers: 'runtime', + extensions: [".js", ".jsx", ".ts", ".tsx"], + exclude: "node_modules/**", + babelHelpers: "runtime", presets: [ - '@babel/preset-react', - '@babel/preset-typescript', + "@babel/preset-react", + "@babel/preset-typescript", [ - '@babel/preset-env', + "@babel/preset-env", { shippedProposals: true, - useBuiltIns: 'usage', + useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [ [ - '@babel/plugin-transform-runtime', + "@babel/plugin-transform-runtime", { corejs: false, helpers: true, @@ -116,81 +119,83 @@ const bundle = ( ], output: [ { - file: `packages/${name}/dist/${name}.js`, - format: 'cjs', + file: `packages/${name}/dist/${name}.cjs.js`, + format: "cjs", sourcemap: true, }, ], }, + ]; + + if (!skipEsModule) { // ES module (for bundlers) build. - { + config.push({ ...baseConfig, plugins: [ tsconfigPaths(), resolve({ - extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], + extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], browser: true, preferBuiltins: false, }), commonjs(), peerDepsExternal(), babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - exclude: 'node_modules/**', - presets: [ - '@babel/preset-react', - '@babel/preset-typescript', - ], + extensions: [".js", ".jsx", ".ts", ".tsx"], + exclude: "node_modules/**", + presets: ["@babel/preset-react", "@babel/preset-typescript"], }), ], output: [ { file: `packages/${name}/dist/${name}.esm.js`, - format: 'es', + format: "es", sourcemap: true, globals, }, ], - }, - ]; + }); + } + + return config; }; export default bundle( - 'cubejs-client-core', - 'cubejs', + "cubejs-client-core", + "cubejs", { - input: 'packages/cubejs-client-core/src/index.ts', + input: "packages/cubejs-client-core/src/index.ts", }, { - input: 'packages/cubejs-client-core/src/index.umd.ts', + input: "packages/cubejs-client-core/src/index.umd.ts", } ) .concat( - bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { - input: 'packages/cubejs-client-ws-transport/src/index.ts', + bundle("cubejs-client-ws-transport", "CubejsWebSocketTransport", { + input: "packages/cubejs-client-ws-transport/src/index.ts", }) ) .concat( - bundle('cubejs-client-react', 'cubejsReact', { - input: 'packages/cubejs-client-react/src/index.js', - external: ['react', 'prop-types'], + bundle("cubejs-client-react", "cubejsReact", { + input: "packages/cubejs-client-react/src/index.js", + external: ["react", "prop-types"], }) ) .concat( - bundle('cubejs-client-vue', 'cubejsVue', { - input: 'packages/cubejs-client-vue/src/index.js', - external: ['vue'], + bundle("cubejs-client-vue", "cubejsVue", { + input: "packages/cubejs-client-vue/src/index.js", + external: ["vue"], globals: { - vue: 'Vue', + vue: "Vue", }, }) ) .concat( - bundle('cubejs-client-vue3', 'cubejsVue3', { - input: 'packages/cubejs-client-vue3/src/index.js', - external: ['vue'], + bundle("cubejs-client-vue3", "cubejsVue3", { + input: "packages/cubejs-client-vue3/src/index.js", + external: ["vue"], globals: { - vue: 'Vue', + vue: "Vue", }, }) ); From ccc77c82183801de9311a6d0f441004e3b3321d8 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 10:36:47 -0700 Subject: [PATCH 34/51] fix quotes --- rollup.config.js | 108 +++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 113eef30ec53d..e4482e802c688 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,10 +1,10 @@ -import babel from "@rollup/plugin-babel"; -import resolve from "@rollup/plugin-node-resolve"; -import commonjs from "@rollup/plugin-commonjs"; -import alias from "@rollup/plugin-alias"; -import tsconfigPaths from "rollup-plugin-tsconfig-paths"; -import peerDepsExternal from "rollup-plugin-peer-deps-external"; -import json from "@rollup/plugin-json"; +import babel from '@rollup/plugin-babel'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import alias from '@rollup/plugin-alias'; +import tsconfigPaths from 'rollup-plugin-tsconfig-paths'; +import peerDepsExternal from 'rollup-plugin-peer-deps-external'; +import json from '@rollup/plugin-json'; const bundle = ( name, @@ -16,31 +16,31 @@ const bundle = ( ...(umdConfig || baseConfig), plugins: [ commonjs({ - extensions: [".js"], + extensions: ['.js'], }), resolve({ - extensions: [".ts", ".js", ".json"], - mainFields: ["browser", "module", "main"], + extensions: ['.ts', '.js', '.json'], + mainFields: ['browser', 'module', 'main'], }), babel({ - extensions: [".js", ".jsx", ".ts", ".tsx"], - exclude: ["node_modules/**", /\/core-js\//], - babelHelpers: "runtime", + extensions: ['.js', '.jsx', '.ts', '.tsx'], + exclude: ['node_modules/**', /\/core-js\//], + babelHelpers: 'runtime', presets: [ - "@babel/preset-react", - "@babel/preset-typescript", + '@babel/preset-react', + '@babel/preset-typescript', [ - "@babel/preset-env", + '@babel/preset-env', { shippedProposals: true, - useBuiltIns: "usage", + useBuiltIns: 'usage', corejs: 3, }, ], ], plugins: [ [ - "@babel/plugin-transform-runtime", + '@babel/plugin-transform-runtime', { corejs: false, helpers: true, @@ -52,14 +52,14 @@ const bundle = ( }), alias({ entries: { - "@cubejs-client/core": "../cubejs-client-core/src/index.ts", + '@cubejs-client/core': '../cubejs-client-core/src/index.ts', }, }), ], }; // Will be built with typescript - const skipEsModule = name === "cubejs-client-core"; + const skipEsModule = name === 'cubejs-client-core'; const config = [ // browser-friendly UMD build @@ -68,9 +68,9 @@ const bundle = ( output: [ { file: `packages/${name}/dist/${name}.umd.js`, - format: "umd", + format: 'umd', name: globalName, - exports: "auto", + exports: 'auto', sourcemap: true, }, ], @@ -82,31 +82,31 @@ const bundle = ( json(), tsconfigPaths(), resolve({ - extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], browser: false, preferBuiltins: true, }), commonjs(), peerDepsExternal(), babel({ - extensions: [".js", ".jsx", ".ts", ".tsx"], - exclude: "node_modules/**", - babelHelpers: "runtime", + extensions: ['.js', '.jsx', '.ts', '.tsx'], + exclude: 'node_modules/**', + babelHelpers: 'runtime', presets: [ - "@babel/preset-react", - "@babel/preset-typescript", + '@babel/preset-react', + '@babel/preset-typescript', [ - "@babel/preset-env", + '@babel/preset-env', { shippedProposals: true, - useBuiltIns: "usage", + useBuiltIns: 'usage', corejs: 3, }, ], ], plugins: [ [ - "@babel/plugin-transform-runtime", + '@babel/plugin-transform-runtime', { corejs: false, helpers: true, @@ -120,7 +120,7 @@ const bundle = ( output: [ { file: `packages/${name}/dist/${name}.cjs.js`, - format: "cjs", + format: 'cjs', sourcemap: true, }, ], @@ -134,22 +134,22 @@ const bundle = ( plugins: [ tsconfigPaths(), resolve({ - extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx", ".json"], + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], browser: true, preferBuiltins: false, }), commonjs(), peerDepsExternal(), babel({ - extensions: [".js", ".jsx", ".ts", ".tsx"], - exclude: "node_modules/**", - presets: ["@babel/preset-react", "@babel/preset-typescript"], + extensions: ['.js', '.jsx', '.ts', '.tsx'], + exclude: 'node_modules/**', + presets: ['@babel/preset-react', '@babel/preset-typescript'], }), ], output: [ { file: `packages/${name}/dist/${name}.esm.js`, - format: "es", + format: 'es', sourcemap: true, globals, }, @@ -161,41 +161,41 @@ const bundle = ( }; export default bundle( - "cubejs-client-core", - "cubejs", + 'cubejs-client-core', + 'cubejs', { - input: "packages/cubejs-client-core/src/index.ts", + input: 'packages/cubejs-client-core/src/index.ts', }, { - input: "packages/cubejs-client-core/src/index.umd.ts", + input: 'packages/cubejs-client-core/src/index.umd.ts', } ) .concat( - bundle("cubejs-client-ws-transport", "CubejsWebSocketTransport", { - input: "packages/cubejs-client-ws-transport/src/index.ts", + bundle('cubejs-client-ws-transport', 'CubejsWebSocketTransport', { + input: 'packages/cubejs-client-ws-transport/src/index.ts', }) ) .concat( - bundle("cubejs-client-react", "cubejsReact", { - input: "packages/cubejs-client-react/src/index.js", - external: ["react", "prop-types"], + bundle('cubejs-client-react', 'cubejsReact', { + input: 'packages/cubejs-client-react/src/index.js', + external: ['react', 'prop-types'], }) ) .concat( - bundle("cubejs-client-vue", "cubejsVue", { - input: "packages/cubejs-client-vue/src/index.js", - external: ["vue"], + bundle('cubejs-client-vue', 'cubejsVue', { + input: 'packages/cubejs-client-vue/src/index.js', + external: ['vue'], globals: { - vue: "Vue", + vue: 'Vue', }, }) ) .concat( - bundle("cubejs-client-vue3", "cubejsVue3", { - input: "packages/cubejs-client-vue3/src/index.js", - external: ["vue"], + bundle('cubejs-client-vue3', 'cubejsVue3', { + input: 'packages/cubejs-client-vue3/src/index.js', + external: ['vue'], globals: { - vue: "Vue", + vue: 'Vue', }, }) ); From 40d119bfc553ce62cbac7d7c4dcba4c76e023fec Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 10:49:16 -0700 Subject: [PATCH 35/51] module fix --- packages/cubejs-client-core/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index 4dc22f1c8e153..973c69bf8444f 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -7,7 +7,8 @@ "compilerOptions": { "lib": ["es2017"], "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "module": "ES2020", + "moduleResolution": "node", "declaration": true, "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "declarationDir": "./dist", /* Generates a sourcemap for each corresponding '.d.ts' file. */ From 564a31f03d97f656b7d7a1664e6965655084715e Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 11:28:10 -0700 Subject: [PATCH 36/51] updated uuid --- packages/cubejs-client-core/package.json | 3 +-- packages/cubejs-client-core/tsconfig.json | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 48920aae3f56b..f271116880246 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -23,7 +23,7 @@ "dayjs": "^1.10.4", "ramda": "^0.27.2", "url-search-params-polyfill": "^7.0.0", - "uuid": "^8.3.2" + "uuid": "^11.1.0" }, "scripts": { "build:client-core": "rm -rf dist && npm run tsc", @@ -43,7 +43,6 @@ "@types/jest": "^29", "@types/moment-range": "^4.0.0", "@types/ramda": "^0.27.34", - "@types/uuid": "^8.3.0", "babel-jest": "^29", "jest": "^29", "typescript": "~5.2.2" diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index 973c69bf8444f..738c9b2f65a31 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -5,8 +5,8 @@ "test/fixtures/datablending/load-responses.json" ], "compilerOptions": { - "lib": ["es2017"], - "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "lib": ["ES2020"], + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "ES2020", "moduleResolution": "node", "declaration": true, From f7341ba5f82bdf73386cbd5bc7fc6829c9adf36d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 22:19:32 +0300 Subject: [PATCH 37/51] trying to fix jest tests + client core build --- jest.base.config.js | 1 + jest.setup.js | 5 +++++ packages/cubejs-client-core/tsconfig.json | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 jest.setup.js diff --git a/jest.base.config.js b/jest.base.config.js index 915c54622bb45..7c0fafae64cf6 100644 --- a/jest.base.config.js +++ b/jest.base.config.js @@ -16,6 +16,7 @@ module.exports = { '^uuid$': require.resolve('uuid'), '^yaml$': require.resolve('yaml'), }, + setupFiles: ['../../jest.setup.js'], snapshotFormat: { escapeString: true, // To keep existing variant of snapshots printBasicPrototype: true diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000000000..e9d1eac214228 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,5 @@ +const { webcrypto } = require('node:crypto'); + +if (!globalThis.crypto) { + globalThis.crypto = webcrypto; +} diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index 738c9b2f65a31..e8e2e6fc884aa 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -7,8 +7,7 @@ "compilerOptions": { "lib": ["ES2020"], "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "ES2020", - "moduleResolution": "node", + "module": "commonjs", "declaration": true, "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "declarationDir": "./dist", /* Generates a sourcemap for each corresponding '.d.ts' file. */ From 520c642b86983eef102eac244b14b2fcff444db7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 May 2025 22:30:11 +0300 Subject: [PATCH 38/51] remove module --- packages/cubejs-client-core/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index f271116880246..9dc77e4405564 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -9,7 +9,6 @@ }, "description": "Cube client", "main": "dist/cubejs-client-core.cjs.js", - "module": "dist/src/index.js", "browser": "dist/cubejs-client-core.umd.js", "typings": "dist/src/index.d.ts", "author": "Cube Dev, Inc.", From d024ad73f0c61f46ccc4e012ff06c0bf6162c198 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 20:14:12 -0700 Subject: [PATCH 39/51] unit tests --- packages/cubejs-client-core/jest.config.js | 12 +- packages/cubejs-client-core/package.json | 3 +- .../cubejs-client-core/tsconfig.jest.json | 6 + packages/cubejs-client-core/tsconfig.json | 7 +- yarn.lock | 199 ++++++++++++++---- 5 files changed, 182 insertions(+), 45 deletions(-) create mode 100644 packages/cubejs-client-core/tsconfig.jest.json diff --git a/packages/cubejs-client-core/jest.config.js b/packages/cubejs-client-core/jest.config.js index 69ec3dc057afa..d33c392b4a249 100644 --- a/packages/cubejs-client-core/jest.config.js +++ b/packages/cubejs-client-core/jest.config.js @@ -3,6 +3,16 @@ const base = require('../../jest.base.config'); /** @type {import('jest').Config} */ module.exports = { ...base, + preset: 'ts-jest', rootDir: '.', - collectCoverageFrom: [...base.collectCoverageFrom, '!dist/src/index.umd.js'] + testMatch: ['/test/**/*.test.ts'], + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.jest.json' }], + }, + collectCoverageFrom: [ + ...base.collectCoverageFrom, + '!/dist/index.umd.ts', + '!/dist/index.cjs.ts', + ], }; diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 9dc77e4405564..a36c05f32875a 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -29,7 +29,7 @@ "tsc": "tsc", "watch": "tsc -w", "test": "npm run unit", - "unit": "jest --coverage dist/test", + "unit": "jest --coverage", "lint": "eslint src/* test/ --ext .ts,.js", "lint:fix": "eslint --fix src/* test/ --ext .ts,js" }, @@ -44,6 +44,7 @@ "@types/ramda": "^0.27.34", "babel-jest": "^29", "jest": "^29", + "ts-jest": "^29.3.3", "typescript": "~5.2.2" }, "eslintConfig": { diff --git a/packages/cubejs-client-core/tsconfig.jest.json b/packages/cubejs-client-core/tsconfig.jest.json new file mode 100644 index 0000000000000..7048c297c8f6c --- /dev/null +++ b/packages/cubejs-client-core/tsconfig.jest.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS" + } +} diff --git a/packages/cubejs-client-core/tsconfig.json b/packages/cubejs-client-core/tsconfig.json index e8e2e6fc884aa..6637475d03a1e 100644 --- a/packages/cubejs-client-core/tsconfig.json +++ b/packages/cubejs-client-core/tsconfig.json @@ -5,9 +5,10 @@ "test/fixtures/datablending/load-responses.json" ], "compilerOptions": { - "lib": ["ES2020"], - "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", + "target": "ES2020", + "lib": ["dom", "dom.iterable", "ES2022"], + "module": "ES2020", + "moduleResolution": "node", "declaration": true, "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "declarationDir": "./dist", /* Generates a sourcemap for each corresponding '.d.ts' file. */ diff --git a/yarn.lock b/yarn.lock index 39592e72eabfd..d9d51a5e36d9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7919,13 +7919,27 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>=18", "@types/node@^18", "@types/node@^20", "@types/node@^22.5.5": +"@types/node@*", "@types/node@>=18", "@types/node@^20": version "20.17.28" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.28.tgz#c10436f3a3c996f535919a9b082e2c47f19c40a1" integrity sha512-DHlH/fNL6Mho38jTy7/JT7sn2wnXI+wULR6PV4gy4VHLVvnrV/d3pHAMQHhc4gjdLmK2ZiPoMxzp6B3yRajLSQ== dependencies: undici-types "~6.19.2" +"@types/node@^18": + version "18.19.100" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.100.tgz#7f3aefbb6911099ab7e0902a1f373b1a4d2c1947" + integrity sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA== + dependencies: + undici-types "~5.26.4" + +"@types/node@^22.5.5": + version "22.15.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.18.tgz#2f8240f7e932f571c2d45f555ba0b6c3f7a75963" + integrity sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg== + dependencies: + undici-types "~6.21.0" + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -7968,7 +7982,7 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/ramda@0.27.40", "@types/ramda@^0.27.32", "@types/ramda@^0.27.34", "@types/ramda@^0.27.40": +"@types/ramda@^0.27.32", "@types/ramda@^0.27.34", "@types/ramda@^0.27.40": version "0.27.40" resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.27.40.tgz#99f307356fe553095ee4d3c2af2b0eb3af7a8413" integrity sha512-V99ZfTH2tqVYdLDAlgh2uT+N074HPgqnAsMjALKSBqogYd0HbuuGMqNukJ6fk9Ml/Htaus76fsc4Yh3p7q1VdQ== @@ -10370,6 +10384,13 @@ browserslist@^4.0.0, browserslist@^4.16.0, browserslist@^4.16.3, browserslist@^4 node-releases "^2.0.19" update-browserslist-db "^1.1.1" +bs-logger@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -11179,6 +11200,11 @@ commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + integrity sha512-J2wnb6TKniXNOtoHS8TSrG9IOQluPrsmyAJ8oCUJOBmv+uLBCyPYAZkD2jFvw2DCzIXNnISIM01NIvr35TkBMQ== + common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -12124,7 +12150,7 @@ d3-timer@^3.0.1: resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== -d@1, d@^1.0.2: +d@1, d@^1.0.1, d@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== @@ -12893,7 +12919,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^3.1.7: +ejs@^3.1.10, ejs@^3.1.7: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -13222,7 +13248,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@0.10.53, es5-ext@^0.10.35, es5-ext@^0.10.64: +es5-ext@^0.10.35: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== @@ -13231,7 +13257,17 @@ es5-ext@0.10.53, es5-ext@^0.10.35, es5-ext@^0.10.64: es6-symbol "~3.1.3" next-tick "~1.0.0" -es6-iterator@~2.0.3: +es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== @@ -13240,7 +13276,7 @@ es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-symbol@^3.1.0, es6-symbol@^3.1.1, es6-symbol@~3.1.3: +es6-symbol@^3.1.0, es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.3: version "3.1.4" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== @@ -13683,6 +13719,16 @@ eslint@^8.54.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -13766,6 +13812,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + event-pubsub@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" @@ -14083,7 +14137,7 @@ fast-glob@^3.3.1, fast-glob@^3.3.2, fast-glob@^3.3.3: merge2 "^1.3.0" micromatch "^4.0.8" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -17172,7 +17226,7 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.7.0: +jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== @@ -18284,7 +18338,12 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= -lodash@^3.10.1, lodash@^4.16.5, lodash@^4.17.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + integrity sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ== + +lodash@^4.16.5, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -18472,6 +18531,11 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-error@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^10.0.3: version "10.2.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" @@ -19173,6 +19237,11 @@ nan@^2.18.0, nan@^2.19.0, nan@^2.20.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== +nan@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" + integrity sha512-Wm2/nFOm2y9HtJfgOLnctGbfvF23FcQZeyUZqDD8JQG3zO5kXh3MkQKiUaA68mJiVWrOzLFkAV1u6bC8P52DJA== + nanoid@^3.3.7, nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -19245,6 +19314,11 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -19444,7 +19518,7 @@ node-gyp@^9.3.0: tar "^6.1.2" which "^2.0.2" -node-int64@^0.3.3: +node-int64@^0.3.3, node-int64@~0.3.0: version "0.3.3" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.3.3.tgz#2d6e6b2ece5de8588b43d88d1bc41b26cd1fa84d" integrity sha1-LW5rLs5d6FiLQ9iNG8QbJs0fqE0= @@ -19938,6 +20012,11 @@ optionator@^0.9.1, optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + integrity sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg== + ora@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" @@ -21612,6 +21691,11 @@ python-struct@^1.1.3: dependencies: long "^4.0.0" +q@1.0.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.0.1.tgz#11872aeedee89268110b10a718448ffb10112a14" + integrity sha512-18MnBaCeBX9sLRUdtxz/6onlb7wLzFxCylklyO8n27y5JxJYaGLPu4ccyc5zih58SpEzY8QmfwaWqguqXU6Y+A== + q@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -23211,6 +23295,11 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + send@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" @@ -24022,16 +24111,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -24122,7 +24202,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -24150,13 +24230,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -24712,7 +24785,7 @@ thread-loader@^3.0.0: neo-async "^2.6.2" schema-utils "^3.0.0" -thrift@0.20.0, thrift@^0.20.0, thrift@^0.9.3: +thrift@^0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.20.0.tgz#b2dbeaaf7c2385aed2eba59d442c87f033fa8e5e" integrity sha512-oSmJTaoIAGolpupVHFfsWcmdEKX81fcDI6ty0hhezzdgZvp0XyXgMe9+1YusI8Ahy0HK4n8jlNrkPjOPeHZjdQ== @@ -24723,6 +24796,15 @@ thrift@0.20.0, thrift@^0.20.0, thrift@^0.9.3: q "^1.5.0" ws "^5.2.3" +thrift@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.9.3.tgz#99021336403d42e08b8efadb1c37a06d7998b2a6" + integrity sha512-CLjTMnk31Yhcteeyfhxv1UU8yv0jqlc5kNDGogbpWG2mUU2sJtj33sQzA+qml8Kj2y8WUdVKY7kkZxiczKoATw== + dependencies: + node-int64 "~0.3.0" + q "1.0.x" + ws "~0.4.32" + throat@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" @@ -24793,6 +24875,11 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== +tinycolor@0.x: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" + integrity sha512-+CorETse1kl98xg0WAzii8DTT4ABF4R3nquhrkIbVGcw1T8JYs5Gfx9xEfGINPUZGDj9C4BmOtuKeaTtuuRolg== + tinypool@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.7.0.tgz#88053cc99b4a594382af23190c609d93fddf8021" @@ -24961,6 +25048,22 @@ ts-invariant@^0.10.3: dependencies: tslib "^2.1.0" +ts-jest@^29.3.3: + version "29.3.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.3.tgz#c24c31a9d12268f88899e3eeb05912cab42c574c" + integrity sha512-y6jLm19SL4GroiBmHwFK4dSHUfDNmOrJbRfp6QmDIlI9p5tT5Q8ItccB4pTIslCIqOZuQnBwpTR0bQ5eUMYwkw== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.2" + type-fest "^4.41.0" + yargs-parser "^21.1.1" + ts-toolbelt@^6.15.1: version "6.15.5" resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz#cb3b43ed725cb63644782c64fbcad7d8f28c0a83" @@ -25100,6 +25203,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -25232,11 +25340,21 @@ unbzip2-stream@^1.0.9: buffer "^5.2.1" through "^2.3.8" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + undici@^5.28.4, undici@^5.28.5: version "5.28.5" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" @@ -26251,7 +26369,7 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -26277,15 +26395,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -26394,6 +26503,16 @@ ws@^8.13.0, ws@^8.16.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@~0.4.32: + version "0.4.32" + resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" + integrity sha512-htqsS0U9Z9lb3ITjidQkRvkLdVhQePrMeu475yEfOWkAYvJ6dSjQp1tOH6ugaddzX5b7sQjMPNtY71eTzrV/kA== + dependencies: + commander "~2.1.0" + nan "~1.0.0" + options ">=0.0.5" + tinycolor "0.x" + xhr@^2.0.1: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" From 3134708aa41faf784620a5aedc78e0b862cf397b Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 20:17:56 -0700 Subject: [PATCH 40/51] fix coverage --- packages/cubejs-client-core/jest.config.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cubejs-client-core/jest.config.js b/packages/cubejs-client-core/jest.config.js index d33c392b4a249..0f4c0bd20a075 100644 --- a/packages/cubejs-client-core/jest.config.js +++ b/packages/cubejs-client-core/jest.config.js @@ -11,8 +11,7 @@ module.exports = { '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.jest.json' }], }, collectCoverageFrom: [ - ...base.collectCoverageFrom, - '!/dist/index.umd.ts', - '!/dist/index.cjs.ts', + '/src/**/*.{ts,tsx}', + '!/src/**/*.d.ts', ], }; From 63d7f841c5f03ab9a6ec473127d549cf2b98f60d Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 20:53:28 -0700 Subject: [PATCH 41/51] fix entry point --- packages/cubejs-client-ws-transport/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-client-ws-transport/package.json b/packages/cubejs-client-ws-transport/package.json index 2a06d70fa88f7..d0b14ece87108 100644 --- a/packages/cubejs-client-ws-transport/package.json +++ b/packages/cubejs-client-ws-transport/package.json @@ -8,7 +8,7 @@ "directory": "packages/cubejs-client-ws-transport" }, "description": "Cube.js WebSocket transport", - "main": "dist/cubejs-client-ws-transport.js", + "main": "dist/cubejs-client-ws-transport.cjs.js", "typings": "dist/index.d.ts", "author": "Cube Dev, Inc.", "scripts": { From ee77138d44d4163c11165fb7f8108dc26f40818f Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 21:45:27 -0700 Subject: [PATCH 42/51] ws fix --- rollup.config.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rollup.config.js b/rollup.config.js index e4482e802c688..5f720bdae10fc 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,6 +5,15 @@ import alias from '@rollup/plugin-alias'; import tsconfigPaths from 'rollup-plugin-tsconfig-paths'; import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import json from '@rollup/plugin-json'; +import { builtinModules } from 'module'; + +const external = [ + 'ws', + 'isomorphic-ws', + 'bufferutil', + 'utf-8-validate', + ...builtinModules, +]; const bundle = ( name, @@ -78,12 +87,13 @@ const bundle = ( { ...baseConfig, + external, plugins: [ json(), tsconfigPaths(), resolve({ extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], - browser: false, + browser: true, preferBuiltins: true, }), commonjs(), From 200a2b9c923b4078e5b9c66d78d939193c51d88f Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 21:52:38 -0700 Subject: [PATCH 43/51] resolve fix --- rollup.config.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 5f720bdae10fc..1a1b429eb8d8e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -30,6 +30,7 @@ const bundle = ( resolve({ extensions: ['.ts', '.js', '.json'], mainFields: ['browser', 'module', 'main'], + resolveOnly: [/^\.\.?/], }), babel({ extensions: ['.js', '.jsx', '.ts', '.tsx'], @@ -93,8 +94,7 @@ const bundle = ( tsconfigPaths(), resolve({ extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], - browser: true, - preferBuiltins: true, + resolveOnly: [/^\.\.?/], }), commonjs(), peerDepsExternal(), @@ -145,8 +145,7 @@ const bundle = ( tsconfigPaths(), resolve({ extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], - browser: true, - preferBuiltins: false, + resolveOnly: [/^\.\.?/], }), commonjs(), peerDepsExternal(), From 289ea9470165d0f386e713195ab6ebb92f11efd2 Mon Sep 17 00:00:00 2001 From: Alex Vasilev Date: Thu, 15 May 2025 21:54:22 -0700 Subject: [PATCH 44/51] cleanup --- rollup.config.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 1a1b429eb8d8e..d976825c4901e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,14 +7,6 @@ import peerDepsExternal from 'rollup-plugin-peer-deps-external'; import json from '@rollup/plugin-json'; import { builtinModules } from 'module'; -const external = [ - 'ws', - 'isomorphic-ws', - 'bufferutil', - 'utf-8-validate', - ...builtinModules, -]; - const bundle = ( name, globalName, @@ -88,7 +80,6 @@ const bundle = ( { ...baseConfig, - external, plugins: [ json(), tsconfigPaths(), From 457a5b580edc0e4c037063ea69100a663e8ed292 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 11:36:40 +0300 Subject: [PATCH 45/51] fix yarn.lock --- yarn.lock | 157 +++++++++++++----------------------------------------- 1 file changed, 38 insertions(+), 119 deletions(-) diff --git a/yarn.lock b/yarn.lock index d9d51a5e36d9f..70ae277646c5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7919,27 +7919,13 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>=18", "@types/node@^20": +"@types/node@*", "@types/node@>=18", "@types/node@^18", "@types/node@^20", "@types/node@^22.5.5": version "20.17.28" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.28.tgz#c10436f3a3c996f535919a9b082e2c47f19c40a1" integrity sha512-DHlH/fNL6Mho38jTy7/JT7sn2wnXI+wULR6PV4gy4VHLVvnrV/d3pHAMQHhc4gjdLmK2ZiPoMxzp6B3yRajLSQ== dependencies: undici-types "~6.19.2" -"@types/node@^18": - version "18.19.100" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.100.tgz#7f3aefbb6911099ab7e0902a1f373b1a4d2c1947" - integrity sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA== - dependencies: - undici-types "~5.26.4" - -"@types/node@^22.5.5": - version "22.15.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.18.tgz#2f8240f7e932f571c2d45f555ba0b6c3f7a75963" - integrity sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg== - dependencies: - undici-types "~6.21.0" - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -7982,7 +7968,7 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/ramda@^0.27.32", "@types/ramda@^0.27.34", "@types/ramda@^0.27.40": +"@types/ramda@0.27.40", "@types/ramda@^0.27.32", "@types/ramda@^0.27.34", "@types/ramda@^0.27.40": version "0.27.40" resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.27.40.tgz#99f307356fe553095ee4d3c2af2b0eb3af7a8413" integrity sha512-V99ZfTH2tqVYdLDAlgh2uT+N074HPgqnAsMjALKSBqogYd0HbuuGMqNukJ6fk9Ml/Htaus76fsc4Yh3p7q1VdQ== @@ -11200,11 +11186,6 @@ commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" - integrity sha512-J2wnb6TKniXNOtoHS8TSrG9IOQluPrsmyAJ8oCUJOBmv+uLBCyPYAZkD2jFvw2DCzIXNnISIM01NIvr35TkBMQ== - common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -12150,7 +12131,7 @@ d3-timer@^3.0.1: resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== -d@1, d@^1.0.1, d@^1.0.2: +d@1, d@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== @@ -13248,7 +13229,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35: +es5-ext@0.10.53, es5-ext@^0.10.35, es5-ext@^0.10.64: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== @@ -13257,17 +13238,7 @@ es5-ext@^0.10.35: es6-symbol "~3.1.3" next-tick "~1.0.0" -es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14: - version "0.10.64" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" - integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - esniff "^2.0.1" - next-tick "^1.1.0" - -es6-iterator@^2.0.3, es6-iterator@~2.0.3: +es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== @@ -13276,7 +13247,7 @@ es6-iterator@^2.0.3, es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-symbol@^3.1.0, es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.3: +es6-symbol@^3.1.0, es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.4" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== @@ -13719,16 +13690,6 @@ eslint@^8.54.0: strip-ansi "^6.0.1" text-table "^0.2.0" -esniff@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" - integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - event-emitter "^0.3.5" - type "^2.7.2" - espree@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -13812,14 +13773,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - event-pubsub@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" @@ -18338,12 +18291,7 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= -lodash@^3.10.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - integrity sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ== - -lodash@^4.16.5, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^3.10.1, lodash@^4.16.5, lodash@^4.17.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -19237,11 +19185,6 @@ nan@^2.18.0, nan@^2.19.0, nan@^2.20.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== -nan@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" - integrity sha512-Wm2/nFOm2y9HtJfgOLnctGbfvF23FcQZeyUZqDD8JQG3zO5kXh3MkQKiUaA68mJiVWrOzLFkAV1u6bC8P52DJA== - nanoid@^3.3.7, nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -19314,11 +19257,6 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -19518,7 +19456,7 @@ node-gyp@^9.3.0: tar "^6.1.2" which "^2.0.2" -node-int64@^0.3.3, node-int64@~0.3.0: +node-int64@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.3.3.tgz#2d6e6b2ece5de8588b43d88d1bc41b26cd1fa84d" integrity sha1-LW5rLs5d6FiLQ9iNG8QbJs0fqE0= @@ -20012,11 +19950,6 @@ optionator@^0.9.1, optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg== - ora@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" @@ -21691,11 +21624,6 @@ python-struct@^1.1.3: dependencies: long "^4.0.0" -q@1.0.x: - version "1.0.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.0.1.tgz#11872aeedee89268110b10a718448ffb10112a14" - integrity sha512-18MnBaCeBX9sLRUdtxz/6onlb7wLzFxCylklyO8n27y5JxJYaGLPu4ccyc5zih58SpEzY8QmfwaWqguqXU6Y+A== - q@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -24111,7 +24039,16 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -24202,7 +24139,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -24230,6 +24167,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -24785,7 +24729,7 @@ thread-loader@^3.0.0: neo-async "^2.6.2" schema-utils "^3.0.0" -thrift@^0.20.0: +thrift@0.20.0, thrift@^0.20.0, thrift@^0.9.3: version "0.20.0" resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.20.0.tgz#b2dbeaaf7c2385aed2eba59d442c87f033fa8e5e" integrity sha512-oSmJTaoIAGolpupVHFfsWcmdEKX81fcDI6ty0hhezzdgZvp0XyXgMe9+1YusI8Ahy0HK4n8jlNrkPjOPeHZjdQ== @@ -24796,15 +24740,6 @@ thrift@^0.20.0: q "^1.5.0" ws "^5.2.3" -thrift@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.9.3.tgz#99021336403d42e08b8efadb1c37a06d7998b2a6" - integrity sha512-CLjTMnk31Yhcteeyfhxv1UU8yv0jqlc5kNDGogbpWG2mUU2sJtj33sQzA+qml8Kj2y8WUdVKY7kkZxiczKoATw== - dependencies: - node-int64 "~0.3.0" - q "1.0.x" - ws "~0.4.32" - throat@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" @@ -24875,11 +24810,6 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== -tinycolor@0.x: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" - integrity sha512-+CorETse1kl98xg0WAzii8DTT4ABF4R3nquhrkIbVGcw1T8JYs5Gfx9xEfGINPUZGDj9C4BmOtuKeaTtuuRolg== - tinypool@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.7.0.tgz#88053cc99b4a594382af23190c609d93fddf8021" @@ -25048,7 +24978,7 @@ ts-invariant@^0.10.3: dependencies: tslib "^2.1.0" -ts-jest@^29.3.3: +ts-jest@^29: version "29.3.3" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.3.tgz#c24c31a9d12268f88899e3eeb05912cab42c574c" integrity sha512-y6jLm19SL4GroiBmHwFK4dSHUfDNmOrJbRfp6QmDIlI9p5tT5Q8ItccB4pTIslCIqOZuQnBwpTR0bQ5eUMYwkw== @@ -25340,21 +25270,11 @@ unbzip2-stream@^1.0.9: buffer "^5.2.1" through "^2.3.8" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -undici-types@~6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" - integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== - undici@^5.28.4, undici@^5.28.5: version "5.28.5" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" @@ -26369,7 +26289,7 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -26395,6 +26315,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -26503,16 +26432,6 @@ ws@^8.13.0, ws@^8.16.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== -ws@~0.4.32: - version "0.4.32" - resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" - integrity sha512-htqsS0U9Z9lb3ITjidQkRvkLdVhQePrMeu475yEfOWkAYvJ6dSjQp1tOH6ugaddzX5b7sQjMPNtY71eTzrV/kA== - dependencies: - commander "~2.1.0" - nan "~1.0.0" - options ">=0.0.5" - tinycolor "0.x" - xhr@^2.0.1: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" From 36d5c2cc26712d74d83112a343b95353dd3fb013 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 11:41:25 +0300 Subject: [PATCH 46/51] some configs polishment --- jest.base-ts.config.js | 16 ++++++++++++++++ packages/cubejs-client-core/jest.config.js | 12 +++--------- packages/cubejs-client-core/package.json | 2 +- packages/cubejs-client-core/tsconfig.jest.json | 6 ------ 4 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 jest.base-ts.config.js delete mode 100644 packages/cubejs-client-core/tsconfig.jest.json diff --git a/jest.base-ts.config.js b/jest.base-ts.config.js new file mode 100644 index 0000000000000..2914b09c97967 --- /dev/null +++ b/jest.base-ts.config.js @@ -0,0 +1,16 @@ +const base = require('./jest.base.config'); + +/** @type {import('jest').Config} */ +module.exports = { + ...base, + preset: 'ts-jest', + testMatch: ['/test/**/*.test.ts'], + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: [ + '/src/**/*.{ts,tsx}', + '!/src/**/*.d.ts', + ] +}; diff --git a/packages/cubejs-client-core/jest.config.js b/packages/cubejs-client-core/jest.config.js index 0f4c0bd20a075..d2dfbb31e44eb 100644 --- a/packages/cubejs-client-core/jest.config.js +++ b/packages/cubejs-client-core/jest.config.js @@ -1,17 +1,11 @@ -const base = require('../../jest.base.config'); +const base = require('../../jest.base-ts.config'); /** @type {import('jest').Config} */ module.exports = { ...base, - preset: 'ts-jest', rootDir: '.', - testMatch: ['/test/**/*.test.ts'], - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.jest.json' }], - }, collectCoverageFrom: [ - '/src/**/*.{ts,tsx}', - '!/src/**/*.d.ts', + ...base.collectCoverageFrom, + '!/src/index.umd.ts', ], }; diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index a36c05f32875a..ae4157729a8dc 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -44,7 +44,7 @@ "@types/ramda": "^0.27.34", "babel-jest": "^29", "jest": "^29", - "ts-jest": "^29.3.3", + "ts-jest": "^29", "typescript": "~5.2.2" }, "eslintConfig": { diff --git a/packages/cubejs-client-core/tsconfig.jest.json b/packages/cubejs-client-core/tsconfig.jest.json deleted file mode 100644 index 7048c297c8f6c..0000000000000 --- a/packages/cubejs-client-core/tsconfig.jest.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS" - } -} From fac9b3be56f80cbcef7d46df9b1d8a75f8c40156 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 14:02:19 +0300 Subject: [PATCH 47/51] fix after rebasing --- packages/cubejs-client-core/package.json | 2 +- .../cubejs-client-core/src/HttpTransport.ts | 8 +++- packages/cubejs-client-core/src/index.ts | 16 +++++++- .../test/HttpTransport.test.ts | 37 ++++++++++--------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index ae4157729a8dc..e9267419aa519 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -38,7 +38,7 @@ ], "license": "MIT", "devDependencies": { - "@cubejs-backend/linter": "1.3.13", + "@cubejs-backend/linter": "1.3.15", "@types/jest": "^29", "@types/moment-range": "^4.0.0", "@types/ramda": "^0.27.34", diff --git a/packages/cubejs-client-core/src/HttpTransport.ts b/packages/cubejs-client-core/src/HttpTransport.ts index 466eac22373d9..decb7bdfd7731 100644 --- a/packages/cubejs-client-core/src/HttpTransport.ts +++ b/packages/cubejs-client-core/src/HttpTransport.ts @@ -24,6 +24,10 @@ export type TransportOptions = { * Fetch timeout in milliseconds. Would be passed as AbortSignal.timeout() */ fetchTimeout?: number; + /** + * AbortSignal to cancel requests + */ + signal?: AbortSignal; }; export interface ITransportResponse { @@ -52,8 +56,8 @@ export class HttpTransport implements ITransport { protected credentials: TransportOptions['credentials']; protected fetchTimeout: number | undefined; - - private signal: any; + + private readonly signal: AbortSignal | undefined; public constructor({ authorization, apiUrl, method, headers = {}, credentials, fetchTimeout, signal }: Omit & { headers?: TransportOptions['headers'] }) { this.authorization = authorization; diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index be9f00cbccb80..b436fbe076ae9 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -45,6 +45,10 @@ export type LoadMethodOptions = { * Function that receives `ProgressResult` on each `Continue wait` message. */ progressCallback?(result: ProgressResult): void; + /** + * AbortSignal to cancel requests + */ + signal?: AbortSignal; }; export type DeeplyReadonly = { @@ -139,6 +143,14 @@ export type CubeApiOptions = { * How many network errors would be retried before returning to users. Default to 0. */ networkErrorRetries?: number; + /** + * AbortSignal to cancel requests + */ + signal?: AbortSignal; + /** + * Fetch timeout in milliseconds. Would be passed as AbortSignal.timeout() + */ + fetchTimeout?: number; }; /** @@ -528,7 +540,7 @@ class CubeApi { () => this.request('load', { query, queryType: 'multi', - signal: options.signal + signal: options?.signal }), (response: any) => this.loadResponseInternal(response, options), options, @@ -594,7 +606,7 @@ class CubeApi { () => this.request('subscribe', { query, queryType: 'multi', - signal: options.signal + signal: options?.signal }), (response: any) => this.loadResponseInternal(response, options), { ...options, subscribe: true }, diff --git a/packages/cubejs-client-core/test/HttpTransport.test.ts b/packages/cubejs-client-core/test/HttpTransport.test.ts index c3e19758b76fd..ec72c0c0704bf 100644 --- a/packages/cubejs-client-core/test/HttpTransport.test.ts +++ b/packages/cubejs-client-core/test/HttpTransport.test.ts @@ -120,13 +120,16 @@ describe('HttpTransport', () => { // Signal tests from src/tests/HttpTransport.test.js describe('Signal functionality', () => { beforeEach(() => { - fetch.mockClear(); // Default mock implementation for signal tests - fetch.mockImplementation(() => Promise.resolve({ + mockedFetch.mockImplementation(() => Promise.resolve({ json: () => Promise.resolve({ data: 'test data' }), ok: true, status: 200 - })); + }) as any); + }); + + afterEach(() => { + mockedFetch.mockClear(); }); test('should pass the signal to fetch when provided in constructor', async () => { @@ -148,8 +151,8 @@ describe('HttpTransport', () => { await Promise.resolve(); // Ensure fetch was called with the signal - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch.mock.calls[0][1].signal).toBe(signal); + expect(mockedFetch).toHaveBeenCalledTimes(1); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).toBe(signal); await promise; }); @@ -175,8 +178,8 @@ describe('HttpTransport', () => { await Promise.resolve(); // Ensure fetch was called with the signal - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch.mock.calls[0][1].signal).toBe(signal); + expect(mockedFetch).toHaveBeenCalledTimes(1); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).toBe(signal); await promise; }); @@ -203,9 +206,9 @@ describe('HttpTransport', () => { await Promise.resolve(); // Ensure fetch was called with the request signal, not the constructor signal - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch.mock.calls[0][1].signal).toBe(controller2.signal); - expect(fetch.mock.calls[0][1].signal).not.toBe(controller1.signal); + expect(mockedFetch).toHaveBeenCalledTimes(1); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).toBe(controller2.signal); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).not.toBe(controller1.signal); await promise; }); @@ -233,8 +236,8 @@ describe('HttpTransport', () => { await Promise.resolve(); // Ensure fetch was called with the timeout signal - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch.mock.calls[0][1].signal).toBe(mockTimeoutSignal); + expect(mockedFetch).toHaveBeenCalledTimes(1); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).toBe(mockTimeoutSignal); expect(AbortSignal.timeout).toHaveBeenCalledWith(5000); // Restore original implementation @@ -246,12 +249,12 @@ describe('HttpTransport', () => { test('should handle request abortion', async () => { // Create a mock Promise and resolver function to control Promise completion let resolveFetch; - const fetchPromise = new Promise(resolve => { + const fetchPromise = new Promise(resolve => { resolveFetch = resolve; }); // Mock fetch to return our controlled Promise - fetch.mockImplementationOnce(() => fetchPromise); + mockedFetch.mockImplementationOnce(() => fetchPromise); const controller = new AbortController(); const { signal } = controller; @@ -273,14 +276,14 @@ describe('HttpTransport', () => { await Promise.resolve(); // Ensure fetch was called with the signal - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch.mock.calls[0][1].signal).toBe(signal); + expect(mockedFetch).toHaveBeenCalledTimes(1); + expect(mockedFetch.mock.calls[0]?.[1]?.signal).toBe(signal); // Abort the request controller.abort(); // Resolve the fetch Promise, simulating request completion - resolveFetch({ + resolveFetch!({ json: () => Promise.resolve({ data: 'aborted data' }), ok: true, status: 200 From 2d18f9c1458dcd9746b7562512814a57ac3f978c Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 16:40:35 +0300 Subject: [PATCH 48/51] improve ts config for jest --- jest.base-ts.config.js | 2 +- tsconfig.jest.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tsconfig.jest.json diff --git a/jest.base-ts.config.js b/jest.base-ts.config.js index 2914b09c97967..337e832baa952 100644 --- a/jest.base-ts.config.js +++ b/jest.base-ts.config.js @@ -7,7 +7,7 @@ module.exports = { testMatch: ['/test/**/*.test.ts'], moduleFileExtensions: ['ts', 'js', 'json'], transform: { - '^.+\\.ts$': 'ts-jest', + '^.+\\.ts$': ['ts-jest', { tsconfig: '../../tsconfig.jest.json' }], }, collectCoverageFrom: [ '/src/**/*.{ts,tsx}', diff --git a/tsconfig.jest.json b/tsconfig.jest.json new file mode 100644 index 0000000000000..2cd2e6642cdae --- /dev/null +++ b/tsconfig.jest.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "sourceMap": true, + "inlineSourceMap": true, + "inlineSources": true, + "resolveJsonModule": true + } +} From 7ef44fd01c2261d729ffd753a5659ddb72080692 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 16:40:46 +0300 Subject: [PATCH 49/51] more tests for resultset --- .../cubejs-client-core/test/ResultSet.test.ts | 155 +++ packages/cubejs-client-core/test/helpers.ts | 909 ++++++++++++++++++ 2 files changed, 1064 insertions(+) create mode 100644 packages/cubejs-client-core/test/helpers.ts diff --git a/packages/cubejs-client-core/test/ResultSet.test.ts b/packages/cubejs-client-core/test/ResultSet.test.ts index d6e39701111d4..b090df65a667f 100644 --- a/packages/cubejs-client-core/test/ResultSet.test.ts +++ b/packages/cubejs-client-core/test/ResultSet.test.ts @@ -9,6 +9,7 @@ import 'jest'; import ResultSet from '../src/ResultSet'; import { TimeDimension } from '../src'; +import { DescriptiveQueryResponse } from './helpers'; describe('ResultSet', () => { describe('timeSeries', () => { @@ -651,6 +652,160 @@ describe('ResultSet', () => { }); }); + test('tableColumns', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.tableColumns()).toEqual([ + { + dataIndex: 'base_orders.created_at.month', + format: undefined, + key: 'base_orders.created_at.month', + meta: undefined, + shortTitle: 'Created at', + title: 'Base Orders Created at', + type: 'time', + }, + { + dataIndex: 'base_orders.status', + format: undefined, + key: 'base_orders.status', + meta: { + addDesc: 'The status of order', + moreNum: 42, + }, + shortTitle: 'Status', + title: 'Base Orders Status', + type: 'string', + }, + { + dataIndex: 'base_orders.count', + format: undefined, + key: 'base_orders.count', + meta: undefined, + shortTitle: 'Count', + title: 'Base Orders Count', + type: 'number', + }, + ]); + }); + + test('totalRow', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.totalRow()).toEqual({ + 'completed,base_orders.count': 2, + 'processing,base_orders.count': 0, + 'shipped,base_orders.count': 0, + x: '2023-04-01T00:00:00.000', + xValues: [ + '2023-04-01T00:00:00.000', + ], + }); + }); + + test('pivotQuery', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.pivotQuery()).toEqual(DescriptiveQueryResponse.pivotQuery); + }); + + test('totalRows', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.totalRows()).toEqual(19); + }); + + test('rawData', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.rawData()).toEqual(DescriptiveQueryResponse.results[0].data); + }); + + test('annotation', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.annotation()).toEqual(DescriptiveQueryResponse.results[0].annotation); + }); + + test('categories', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + expect(resultSet.categories()).toEqual([ + { + 'completed,base_orders.count': 2, + 'processing,base_orders.count': 0, + 'shipped,base_orders.count': 0, + x: '2023-04-01T00:00:00.000', + xValues: [ + '2023-04-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 6, + 'processing,base_orders.count': 6, + 'shipped,base_orders.count': 9, + x: '2023-05-01T00:00:00.000', + xValues: [ + '2023-05-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 5, + 'processing,base_orders.count': 5, + 'shipped,base_orders.count': 13, + x: '2023-06-01T00:00:00.000', + xValues: [ + '2023-06-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 5, + 'processing,base_orders.count': 7, + 'shipped,base_orders.count': 5, + x: '2023-07-01T00:00:00.000', + xValues: [ + '2023-07-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 11, + 'processing,base_orders.count': 3, + 'shipped,base_orders.count': 4, + x: '2023-08-01T00:00:00.000', + xValues: [ + '2023-08-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 5, + 'processing,base_orders.count': 10, + 'shipped,base_orders.count': 9, + x: '2023-09-01T00:00:00.000', + xValues: [ + '2023-09-01T00:00:00.000', + ], + }, + { + 'completed,base_orders.count': 4, + 'processing,base_orders.count': 5, + 'shipped,base_orders.count': 9, + x: '2023-10-01T00:00:00.000', + xValues: [ + '2023-10-01T00:00:00.000', + ], + }, + ]); + }); + + test('serialize/deserialize', () => { + const resultSet = new ResultSet(DescriptiveQueryResponse as any); + + const serialized = resultSet.serialize(); + const restoredResultSet = ResultSet.deserialize(serialized); + + expect(restoredResultSet).toEqual(resultSet); + }); + describe('seriesNames', () => { test('Multiple series with custom alias', () => { const resultSet = new ResultSet({ diff --git a/packages/cubejs-client-core/test/helpers.ts b/packages/cubejs-client-core/test/helpers.ts new file mode 100644 index 0000000000000..5d4447f8627a8 --- /dev/null +++ b/packages/cubejs-client-core/test/helpers.ts @@ -0,0 +1,909 @@ +export const DescriptiveQueryResponse = { + queryType: 'regularQuery', + results: [ + { + query: { + measures: [ + 'base_orders.count' + ], + dimensions: [ + 'base_orders.status' + ], + timeDimensions: [ + { + dimension: 'base_orders.created_at', + granularity: 'month' + }, + { + dimension: 'base_orders.completed_at', + dateRange: [ + '2023-05-16T00:00:00.000', + '2025-05-16T23:59:59.999' + ] + } + ], + segments: [ + 'users.sf_users' + ], + limit: 10000, + total: true, + timezone: 'UTC', + filters: [ + { + member: 'base_orders.fiscal_event_date_label', + operator: 'set' + } + ], + rowLimit: 10000 + }, + lastRefreshTime: '2025-05-16T13:34:38.144Z', + refreshKeyValues: [ + [ + { + refresh_key: '174740245' + } + ], + [ + { + refresh_key: '174740245' + } + ] + ], + usedPreAggregations: {}, + transformedQuery: { + sortedDimensions: [ + 'base_orders.fiscal_event_date_label', + 'base_orders.status', + 'users.sf_users' + ], + sortedTimeDimensions: [ + [ + 'base_orders.completed_at', + 'day' + ], + [ + 'base_orders.created_at', + 'month' + ] + ], + timeDimensions: [ + [ + 'base_orders.completed_at', + null + ], + [ + 'base_orders.created_at', + 'month' + ] + ], + measures: [ + 'base_orders.count' + ], + leafMeasureAdditive: true, + leafMeasures: [ + 'base_orders.count' + ], + measureToLeafMeasures: { + 'base_orders.count': [ + { + measure: 'base_orders.count', + additive: true, + type: 'count' + } + ] + }, + hasNoTimeDimensionsWithoutGranularity: false, + allFiltersWithinSelectedDimensions: false, + isAdditive: true, + granularityHierarchies: { + 'line_items_to_orders.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.minute': [ + 'minute', + 'second' + ], + 'line_items_to_orders.created_at.second': [ + 'second' + ], + 'orders_to_line_items.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.minute': [ + 'minute', + 'second' + ], + 'orders_to_line_items.created_at.second': [ + 'second' + ], + 'orders_to_line_items.completed_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.minute': [ + 'minute', + 'second' + ], + 'orders_to_line_items.completed_at.second': [ + 'second' + ], + 'products.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'products.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'products.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'products.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'products.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'products.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'products.created_at.minute': [ + 'minute', + 'second' + ], + 'products.created_at.second': [ + 'second' + ], + 'simple_orders.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'simple_orders.created_at.minute': [ + 'minute', + 'second' + ], + 'simple_orders.created_at.second': [ + 'second' + ], + 'simple_orders_sql_ext.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.minute': [ + 'minute', + 'second' + ], + 'simple_orders_sql_ext.created_at.second': [ + 'second' + ], + 'users.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'users.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'users.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'users.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'users.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'users.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'users.created_at.minute': [ + 'minute', + 'second' + ], + 'users.created_at.second': [ + 'second' + ], + 'base_orders.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'base_orders.created_at.minute': [ + 'minute', + 'second' + ], + 'base_orders.created_at.second': [ + 'second' + ], + 'base_orders.completed_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'base_orders.completed_at.minute': [ + 'minute', + 'second' + ], + 'base_orders.completed_at.second': [ + 'second' + ], + 'base_orders.event_date.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.hour': [ + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.minute': [ + 'minute', + 'second' + ], + 'base_orders.event_date.second': [ + 'second' + ], + 'base_orders.event_date.fiscal_year': [ + 'fiscal_year', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'base_orders.event_date.fiscal_quarter': [ + 'fiscal_quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.year': [ + 'year', + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.quarter': [ + 'quarter', + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.month': [ + 'month', + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.week': [ + 'week', + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.day': [ + 'day', + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.hour': [ + 'hour', + 'minute', + 'second' + ], + 'check_dup_names.created_at.minute': [ + 'minute', + 'second' + ], + 'check_dup_names.created_at.second': [ + 'second' + ] + }, + hasMultipliedMeasures: false, + hasCumulativeMeasures: false, + windowGranularity: null, + filterDimensionsSingleValueEqual: null, + ownedDimensions: [ + 'base_orders.event_date', + 'base_orders.status', + 'users.sf_users' + ], + ownedTimeDimensionsWithRollupGranularity: [ + [ + 'base_orders.completed_at', + 'day' + ], + [ + 'base_orders.created_at', + 'month' + ] + ], + ownedTimeDimensionsAsIs: [ + [ + 'base_orders.completed_at', + null + ], + [ + 'base_orders.created_at', + 'month' + ] + ], + allBackAliasMembers: {}, + ungrouped: null, + sortedUsedCubePrimaryKeys: null, + sortedAllCubeNames: null, + hasMultiStage: false + }, + requestId: '2ac2a7b1-008b-41ec-be93-691f79a55348-span-1', + annotation: { + measures: { + 'base_orders.count': { + title: 'Base Orders Count', + shortTitle: 'Count', + type: 'number', + drillMembers: [ + 'base_orders.id', + 'base_orders.status', + 'users.city', + 'users.gender' + ], + drillMembersGrouped: { + measures: [], + dimensions: [ + 'base_orders.id', + 'base_orders.status', + 'users.city', + 'users.gender' + ] + } + } + }, + dimensions: { + 'base_orders.status': { + title: 'Base Orders Status', + shortTitle: 'Status', + type: 'string', + meta: { + addDesc: 'The status of order', + moreNum: 42 + } + } + }, + timeDimensions: { + 'base_orders.created_at': { + title: 'Base Orders Created at', + shortTitle: 'Created at', + type: 'time' + }, + 'base_orders.created_at.month': { + title: 'Base Orders Created at', + shortTitle: 'Created at', + type: 'time', + granularity: { + name: 'month', + title: 'month', + interval: '1 month' + } + } + }, + segments: { + 'users.sf_users': { + title: 'Users Sf Users', + shortTitle: 'Sf Users' + } + } + }, + dataSource: 'default', + dbType: 'postgres', + extDbType: 'cubestore', + external: false, + slowQuery: false, + total: 19, + data: [ + { + 'base_orders.created_at.month': '2023-04-01T00:00:00.000', + 'base_orders.created_at': '2023-04-01T00:00:00.000', + 'base_orders.count': '2', + 'base_orders.status': 'completed' + }, + { + 'base_orders.count': '6', + 'base_orders.created_at': '2023-05-01T00:00:00.000', + 'base_orders.created_at.month': '2023-05-01T00:00:00.000', + 'base_orders.status': 'completed' + }, + { + 'base_orders.count': '6', + 'base_orders.status': 'processing', + 'base_orders.created_at': '2023-05-01T00:00:00.000', + 'base_orders.created_at.month': '2023-05-01T00:00:00.000' + }, + { + 'base_orders.count': '9', + 'base_orders.created_at.month': '2023-05-01T00:00:00.000', + 'base_orders.status': 'shipped', + 'base_orders.created_at': '2023-05-01T00:00:00.000' + }, + { + 'base_orders.created_at': '2023-06-01T00:00:00.000', + 'base_orders.status': 'completed', + 'base_orders.created_at.month': '2023-06-01T00:00:00.000', + 'base_orders.count': '5' + }, + { + 'base_orders.count': '5', + 'base_orders.status': 'processing', + 'base_orders.created_at': '2023-06-01T00:00:00.000', + 'base_orders.created_at.month': '2023-06-01T00:00:00.000' + }, + { + 'base_orders.count': '13', + 'base_orders.created_at': '2023-06-01T00:00:00.000', + 'base_orders.status': 'shipped', + 'base_orders.created_at.month': '2023-06-01T00:00:00.000' + }, + { + 'base_orders.status': 'completed', + 'base_orders.created_at.month': '2023-07-01T00:00:00.000', + 'base_orders.created_at': '2023-07-01T00:00:00.000', + 'base_orders.count': '5' + }, + { + 'base_orders.created_at.month': '2023-07-01T00:00:00.000', + 'base_orders.status': 'processing', + 'base_orders.created_at': '2023-07-01T00:00:00.000', + 'base_orders.count': '7' + }, + { + 'base_orders.count': '5', + 'base_orders.status': 'shipped', + 'base_orders.created_at': '2023-07-01T00:00:00.000', + 'base_orders.created_at.month': '2023-07-01T00:00:00.000' + }, + { + 'base_orders.created_at': '2023-08-01T00:00:00.000', + 'base_orders.status': 'completed', + 'base_orders.count': '11', + 'base_orders.created_at.month': '2023-08-01T00:00:00.000' + }, + { + 'base_orders.count': '3', + 'base_orders.created_at.month': '2023-08-01T00:00:00.000', + 'base_orders.created_at': '2023-08-01T00:00:00.000', + 'base_orders.status': 'processing' + }, + { + 'base_orders.status': 'shipped', + 'base_orders.count': '4', + 'base_orders.created_at.month': '2023-08-01T00:00:00.000', + 'base_orders.created_at': '2023-08-01T00:00:00.000' + }, + { + 'base_orders.created_at.month': '2023-09-01T00:00:00.000', + 'base_orders.status': 'completed', + 'base_orders.count': '5', + 'base_orders.created_at': '2023-09-01T00:00:00.000' + }, + { + 'base_orders.count': '10', + 'base_orders.created_at.month': '2023-09-01T00:00:00.000', + 'base_orders.status': 'processing', + 'base_orders.created_at': '2023-09-01T00:00:00.000' + }, + { + 'base_orders.created_at': '2023-09-01T00:00:00.000', + 'base_orders.count': '9', + 'base_orders.created_at.month': '2023-09-01T00:00:00.000', + 'base_orders.status': 'shipped' + }, + { + 'base_orders.count': '4', + 'base_orders.created_at.month': '2023-10-01T00:00:00.000', + 'base_orders.created_at': '2023-10-01T00:00:00.000', + 'base_orders.status': 'completed' + }, + { + 'base_orders.count': '5', + 'base_orders.created_at': '2023-10-01T00:00:00.000', + 'base_orders.status': 'processing', + 'base_orders.created_at.month': '2023-10-01T00:00:00.000' + }, + { + 'base_orders.status': 'shipped', + 'base_orders.created_at.month': '2023-10-01T00:00:00.000', + 'base_orders.count': '9', + 'base_orders.created_at': '2023-10-01T00:00:00.000' + } + ] + } + ], + pivotQuery: { + measures: [ + 'base_orders.count' + ], + dimensions: [ + 'base_orders.status' + ], + timeDimensions: [ + { + dimension: 'base_orders.created_at', + granularity: 'month' + }, + { + dimension: 'base_orders.completed_at', + dateRange: [ + '2023-05-16T00:00:00.000', + '2025-05-16T23:59:59.999' + ] + } + ], + segments: [ + 'users.sf_users' + ], + limit: 10000, + total: true, + timezone: 'UTC', + filters: [ + { + member: 'base_orders.fiscal_event_date_label', + operator: 'set' + } + ], + rowLimit: 10000, + queryType: 'regularQuery' + }, + slowQuery: false +}; From 5fa98a290b70f14bbba4e6177c2e6b48fb8ff9c4 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 17:02:53 +0300 Subject: [PATCH 50/51] fix test for cubeApi --- packages/cubejs-client-core/src/index.ts | 4 +- .../test/{CubeApi.test.js => CubeApi.test.ts} | 70 ++++++++++++------- 2 files changed, 45 insertions(+), 29 deletions(-) rename packages/cubejs-client-core/test/{CubeApi.test.js => CubeApi.test.ts} (73%) diff --git a/packages/cubejs-client-core/src/index.ts b/packages/cubejs-client-core/src/index.ts index b436fbe076ae9..c2f32bb9cd063 100644 --- a/packages/cubejs-client-core/src/index.ts +++ b/packages/cubejs-client-core/src/index.ts @@ -167,7 +167,7 @@ class CubeApi { private readonly credentials: TransportOptions['credentials']; - private readonly transport: ITransport; + protected readonly transport: ITransport; private readonly pollInterval: number; @@ -241,7 +241,7 @@ class CubeApi { this.updateAuthorizationPromise = null; } - private request(method: string, params?: any) { + protected request(method: string, params?: any) { return this.transport.request(method, { baseRequestId: uuidv4(), ...params diff --git a/packages/cubejs-client-core/test/CubeApi.test.js b/packages/cubejs-client-core/test/CubeApi.test.ts similarity index 73% rename from packages/cubejs-client-core/test/CubeApi.test.js rename to packages/cubejs-client-core/test/CubeApi.test.ts index 9940999e4252c..fd63a79776139 100644 --- a/packages/cubejs-client-core/test/CubeApi.test.js +++ b/packages/cubejs-client-core/test/CubeApi.test.ts @@ -5,10 +5,20 @@ */ /* globals describe,test,expect,jest,beforeEach */ +/* eslint-disable import/first */ -import 'jest'; -import { CubeApi } from '../index'; -import HttpTransport from '../HttpTransport'; +import { CubeApi as CubeApiOriginal } from '../src'; +import HttpTransport from '../src/HttpTransport'; + +class CubeApi extends CubeApiOriginal { + public getTransport(): any { + return this.transport; + } + + public makeRequest(method: string, params?: any): any { + return this.request(method, params); + } +} describe('CubeApi with Signal', () => { beforeEach(() => { @@ -26,7 +36,8 @@ describe('CubeApi with Signal', () => { status: 200, text: () => Promise.resolve('{"results":[]}'), json: () => Promise.resolve({ results: [] }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -45,13 +56,13 @@ describe('CubeApi with Signal', () => { // The request method should receive the signal in the call // Create a request in the same way as CubeApi.load does - cubeApi.request('load', { + cubeApi.makeRequest('load', { query: { measures: ['Orders.count'] }, queryType: 'multi' }); // Verify the transport is using the signal - expect(cubeApi.transport.signal).toBe(signal); + expect(cubeApi.getTransport().signal).toBe(signal); }); test('should pass signal from options to request', async () => { @@ -59,12 +70,13 @@ describe('CubeApi with Signal', () => { const { signal } = controller; // Mock for this specific test - jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ subscribe: (cb) => Promise.resolve(cb({ status: 200, text: () => Promise.resolve('{"results":[]}'), json: () => Promise.resolve({ results: [] }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -76,8 +88,8 @@ describe('CubeApi with Signal', () => { { signal } ); - expect(HttpTransport.prototype.request).toHaveBeenCalled(); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).toBe(signal); + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal); }); test('options signal should override constructor signal', async () => { @@ -85,12 +97,13 @@ describe('CubeApi with Signal', () => { const optionsController = new AbortController(); // Mock for this specific test - jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ subscribe: (cb) => Promise.resolve(cb({ status: 200, text: () => Promise.resolve('{"results":[]}'), json: () => Promise.resolve({ results: [] }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -103,9 +116,9 @@ describe('CubeApi with Signal', () => { { signal: optionsController.signal } ); - expect(HttpTransport.prototype.request).toHaveBeenCalled(); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).toBe(optionsController.signal); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).not.toBe(constructorController.signal); + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(optionsController.signal); + expect(requestSpy.mock.calls[0]?.[1]?.signal).not.toBe(constructorController.signal); }); test('should pass signal to meta request', async () => { @@ -113,7 +126,7 @@ describe('CubeApi with Signal', () => { const { signal } = controller; // Mock for meta with proper format - include dimensions, segments, and measures with required properties - jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ subscribe: (cb) => Promise.resolve(cb({ status: 200, text: () => Promise.resolve(JSON.stringify({ @@ -152,7 +165,8 @@ describe('CubeApi with Signal', () => { segments: [] }] }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -161,8 +175,8 @@ describe('CubeApi with Signal', () => { await cubeApi.meta({ signal }); - expect(HttpTransport.prototype.request).toHaveBeenCalled(); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).toBe(signal); + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal); }); test('should pass signal to sql request', async () => { @@ -170,12 +184,13 @@ describe('CubeApi with Signal', () => { const { signal } = controller; // Mock for SQL response - jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ subscribe: (cb) => Promise.resolve(cb({ status: 200, text: () => Promise.resolve('{"sql":{"sql":"SELECT * FROM orders"}}'), json: () => Promise.resolve({ sql: { sql: 'SELECT * FROM orders' } }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -187,8 +202,8 @@ describe('CubeApi with Signal', () => { { signal } ); - expect(HttpTransport.prototype.request).toHaveBeenCalled(); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).toBe(signal); + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal); }); test('should pass signal to dryRun request', async () => { @@ -196,12 +211,13 @@ describe('CubeApi with Signal', () => { const { signal } = controller; // Mock for dryRun response - jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ subscribe: (cb) => Promise.resolve(cb({ status: 200, text: () => Promise.resolve('{"queryType":"regular"}'), json: () => Promise.resolve({ queryType: 'regular' }) - })) + } as any, + async () => undefined as any)) })); const cubeApi = new CubeApi('token', { @@ -213,7 +229,7 @@ describe('CubeApi with Signal', () => { { signal } ); - expect(HttpTransport.prototype.request).toHaveBeenCalled(); - expect(HttpTransport.prototype.request.mock.calls[0][1].signal).toBe(signal); + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal); }); }); From 2824207ee5f96c9fd92fe7ad871c0b06f1bdb0ca Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 May 2025 18:04:13 +0300 Subject: [PATCH 51/51] few more tests --- .../cubejs-client-core/test/CubeApi.test.ts | 131 +++++++++++++++++- packages/cubejs-client-core/test/helpers.ts | 68 +++++++++ 2 files changed, 196 insertions(+), 3 deletions(-) diff --git a/packages/cubejs-client-core/test/CubeApi.test.ts b/packages/cubejs-client-core/test/CubeApi.test.ts index fd63a79776139..d1097fe206259 100644 --- a/packages/cubejs-client-core/test/CubeApi.test.ts +++ b/packages/cubejs-client-core/test/CubeApi.test.ts @@ -7,8 +7,15 @@ /* globals describe,test,expect,jest,beforeEach */ /* eslint-disable import/first */ -import { CubeApi as CubeApiOriginal } from '../src'; +import { CubeApi as CubeApiOriginal, Query } from '../src'; import HttpTransport from '../src/HttpTransport'; +import { + DescriptiveQueryRequest, + DescriptiveQueryRequestCompact, + DescriptiveQueryResponse, + NumericCastedData +} from './helpers'; +import ResultSet from '../src/ResultSet'; class CubeApi extends CubeApiOriginal { public getTransport(): any { @@ -20,8 +27,126 @@ class CubeApi extends CubeApiOriginal { } } -describe('CubeApi with Signal', () => { - beforeEach(() => { +describe('CubeApi Constructor', () => { + test('throw error if no api url', async () => { + try { + const _cubeApi = new CubeApi('token', {} as any); + throw new Error('Should not get here'); + } catch (e: any) { + expect(e.message).toBe('The `apiUrl` option is required'); + } + }); +}); + +describe('CubeApi Load', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + test('simple query, no options', async () => { + // Create a spy on the request method + jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + subscribe: (cb) => Promise.resolve(cb({ + status: 200, + text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)), + json: () => Promise.resolve(DescriptiveQueryResponse) + } as any, + async () => undefined as any)) + })); + + const cubeApi = new CubeApi('token', { + apiUrl: 'http://localhost:4000/cubejs-api/v1', + }); + + const res = await cubeApi.load(DescriptiveQueryRequest as Query); + expect(res).toBeInstanceOf(ResultSet); + expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data); + }); + + test('simple query + { mutexKey, castNumerics }', async () => { + // Create a spy on the request method + jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + subscribe: (cb) => Promise.resolve(cb({ + status: 200, + text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)), + json: () => Promise.resolve(DescriptiveQueryResponse) + } as any, + async () => undefined as any)) + })); + + const cubeApi = new CubeApi({ + apiUrl: 'http://localhost:4000/cubejs-api/v1', + }); + + const res = await cubeApi.load(DescriptiveQueryRequest as Query, { mutexKey: 'mutexKey', castNumerics: true }); + expect(res).toBeInstanceOf(ResultSet); + expect(res.rawData()).toEqual(NumericCastedData); + }); + + test('simple query + compact response format', async () => { + // Create a spy on the request method + jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + subscribe: (cb) => Promise.resolve(cb({ + status: 200, + text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)), + json: () => Promise.resolve(DescriptiveQueryResponse) + } as any, + async () => undefined as any)) + })); + + const cubeApi = new CubeApi('token', { + apiUrl: 'http://localhost:4000/cubejs-api/v1', + }); + + const res = await cubeApi.load(DescriptiveQueryRequestCompact as Query, undefined, undefined, 'compact'); + expect(res).toBeInstanceOf(ResultSet); + expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data); + }); + + test('2 queries', async () => { + // Create a spy on the request method + jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + subscribe: (cb) => Promise.resolve(cb({ + status: 200, + text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)), + json: () => Promise.resolve(DescriptiveQueryResponse) + } as any, + async () => undefined as any)) + })); + + const cubeApi = new CubeApi('token', { + apiUrl: 'http://localhost:4000/cubejs-api/v1', + }); + + const res = await cubeApi.load([DescriptiveQueryRequest as Query, DescriptiveQueryRequest as Query]); + expect(res).toBeInstanceOf(ResultSet); + expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data); + }); + + test('2 queries + compact response format', async () => { + // Create a spy on the request method + jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({ + subscribe: (cb) => Promise.resolve(cb({ + status: 200, + text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)), + json: () => Promise.resolve(DescriptiveQueryResponse) + } as any, + async () => undefined as any)) + })); + + const cubeApi = new CubeApi('token', { + apiUrl: 'http://localhost:4000/cubejs-api/v1', + }); + + const res = await cubeApi.load([DescriptiveQueryRequestCompact as Query, DescriptiveQueryRequestCompact as Query], undefined, undefined, 'compact'); + expect(res).toBeInstanceOf(ResultSet); + expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data); + }); +}); + +describe('CubeApi with Abort Signal', () => { + afterEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); diff --git a/packages/cubejs-client-core/test/helpers.ts b/packages/cubejs-client-core/test/helpers.ts index 5d4447f8627a8..cdb2868496f6f 100644 --- a/packages/cubejs-client-core/test/helpers.ts +++ b/packages/cubejs-client-core/test/helpers.ts @@ -1,3 +1,66 @@ +export const DescriptiveQueryRequest = { + timeDimensions: [ + { + dimension: 'base_orders.created_at', + granularity: 'month' + }, + { + dimension: 'base_orders.completed_at', + dateRange: [ + '2023-05-16', + '2025-05-16' + ] + } + ], + filters: [ + { + member: 'base_orders.fiscal_event_date_label', + operator: 'set' + } + ], + dimensions: [ + 'base_orders.status' + ], + measures: [ + 'base_orders.count' + ], + segments: [ + 'users.sf_users' + ] +}; + +export const DescriptiveQueryRequestCompact = { + timeDimensions: [ + { + dimension: 'base_orders.created_at', + granularity: 'month' + }, + { + dimension: 'base_orders.completed_at', + dateRange: [ + '2023-05-16', + '2025-05-16' + ] + } + ], + filters: [ + { + member: 'base_orders.fiscal_event_date_label', + operator: 'set' + } + ], + dimensions: [ + 'base_orders.status' + ], + measures: [ + 'base_orders.count' + ], + segments: [ + 'users.sf_users' + ], + responseFormat: 'compact', +}; + export const DescriptiveQueryResponse = { queryType: 'regularQuery', results: [ @@ -907,3 +970,8 @@ export const DescriptiveQueryResponse = { }, slowQuery: false }; + +export const NumericCastedData = DescriptiveQueryResponse.results[0].data.map(r => ({ + ...r, + 'base_orders.count': Number(r['base_orders.count']) +}));