Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,14 @@ callback? | [LoadMethodCallback](#types-load-method-callback)‹[ResultSet](#res

### `meta`

> **meta**(**options?**: [LoadMethodOptions](#types-load-method-options)): *Promise‹[Meta](#meta)›*
> **meta**(**options?**: [MetaMethodOptions](#types-meta-method-options)): *Promise‹[Meta](#meta)›*

> **meta**(**options?**: [LoadMethodOptions](#types-load-method-options), **callback?**: [LoadMethodCallback](#types-load-method-callback)‹[Meta](#meta)›): *void*
> **meta**(**options?**: [MetaMethodOptions](#types-meta-method-options), **callback?**: [LoadMethodCallback](#types-load-method-callback)‹[Meta](#meta)›): *void*

Get meta description of cubes available for querying.

Pass **`extended: true`** to request [extended `/v1/meta`](/product/apis-integrations/rest-api/reference#base_pathv1meta) (joins, SQL snippets, pre-aggregations, and related fields). The client only adds the `extended` query parameter when this option is `true`; omit it otherwise. Do not rely on `extended: false` on the wire—the API treats the presence of any `extended` parameter as extended mode.

### `sql`

> **sql**(**query**: [Query](#types-query) | [Query](#types-query)[], **options?**: [LoadMethodOptions](#types-load-method-options)): *Promise‹[SqlQuery](#sql-query)›*
Expand Down Expand Up @@ -817,6 +819,14 @@ Name | Type | Optional? | Description |
`signal` | `AbortSignal` | ✅ Yes | AbortSignal to cancel the request. This allows you to manually abort requests using AbortController. |
`baseRequestId` | `string` | ✅ Yes | Base request ID ([`spanId`](https://cube.dev/docs/product/apis-integrations/core-data-apis/rest-api#request-span-annotation)) to be used for the request. If not provided a random request ID will be generated. |

### `MetaMethodOptions`

Extends [LoadMethodOptions](#types-load-method-options).

Name | Type | Optional? | Description |
------ | ------ | ------ | --- |
`extended` | `boolean` | ✅ Yes | Pass **`true`** to load extended meta. Omit when not needed (the client does not send `extended=false`). |

### `LoadResponse`

Name | Type |
Expand Down
53 changes: 31 additions & 22 deletions packages/cubejs-client-core/src/Meta.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { unnest, fromPairs } from 'ramda';
import {
Cube,
CubesMap,
CubeExtended,
CubePlain,
CubesMapFromMeta,
MemberType,
MetaCubeOf,
MetaResponse,
MetaResponseExtended,
TCubeMeasure,
TCubeDimension,
TCubeMember,
TCubeMemberByType,
TCubeMemberByTypeForMeta,
Query,
FilterOperator,
TCubeSegment,
Expand All @@ -23,13 +25,16 @@ export interface CubeMemberWrapper<T> {
members: T[];
}

export type AggregatedMembers = {
measures: CubeMemberWrapper<TCubeMeasure>[];
dimensions: CubeMemberWrapper<TCubeDimension>[];
segments: CubeMemberWrapper<TCubeSegment>[];
timeDimensions: CubeMemberWrapper<TCubeDimension>[];
export type AggregatedMembersFor<C extends CubePlain | CubeExtended> = {
measures: CubeMemberWrapper<C['measures'][number]>[];
dimensions: CubeMemberWrapper<C['dimensions'][number]>[];
segments: CubeMemberWrapper<C['segments'][number]>[];
timeDimensions: CubeMemberWrapper<C['dimensions'][number]>[];
};

/** Plain-meta variant of {@link AggregatedMembersFor} (alias for `AggregatedMembersFor<CubePlain>`). */
export type AggregatedMembers = AggregatedMembersFor<CubePlain>;

const memberMap = (memberArray: any[]) => fromPairs(
memberArray.map((m) => [m.name, m])
);
Expand Down Expand Up @@ -72,23 +77,23 @@ const operators = {
/**
* Contains information about available cubes and it's members.
*/
export default class Meta {
export default class Meta<T extends MetaResponse | MetaResponseExtended = MetaResponse> {
/**
* Raw meta response
*/
public readonly meta: MetaResponse;
public readonly meta: T;

/**
* An array of all available cubes with their members
*/
public readonly cubes: Cube[];
public readonly cubes: T['cubes'];

/**
* A map of all cubes where the key is a cube name
*/
public readonly cubesMap: CubesMap;
public readonly cubesMap: CubesMapFromMeta<T>;

public constructor(metaResponse: MetaResponse) {
public constructor(metaResponse: T) {
this.meta = metaResponse;
const { cubes } = this.meta;
this.cubes = cubes;
Expand All @@ -110,15 +115,19 @@ export default class Meta {
* @param _query - context query to provide filtering of members available to add to this query
* @param memberType
*/
public membersForQuery(_query: DeeplyReadonly<Query> | null, memberType: MemberType): (TCubeMeasure | TCubeDimension | TCubeMember | TCubeSegment)[] {
public membersForQuery(_query: DeeplyReadonly<Query> | null, memberType: MemberType): (
TCubeMemberByTypeForMeta<MetaCubeOf<T>, 'measures'>
| TCubeMemberByTypeForMeta<MetaCubeOf<T>, 'dimensions'>
| TCubeMemberByTypeForMeta<MetaCubeOf<T>, 'segments'>
)[] {
return unnest(this.cubes.map((c) => c[memberType]))
.sort((a, b) => (a.title > b.title ? 1 : -1));
}

public membersGroupedByCube() {
public membersGroupedByCube(): AggregatedMembersFor<MetaCubeOf<T>> {
const memberKeys = ['measures', 'dimensions', 'segments', 'timeDimensions'];

return this.cubes.reduce<AggregatedMembers>(
return this.cubes.reduce<AggregatedMembersFor<MetaCubeOf<T>>>(
(memo, cube) => {
memberKeys.forEach((key) => {
let members: TCubeMeasure[] | TCubeDimension[] | TCubeSegment[] = [];
Expand Down Expand Up @@ -157,7 +166,7 @@ export default class Meta {
dimensions: [],
segments: [],
timeDimensions: [],
} as AggregatedMembers
} as AggregatedMembersFor<MetaCubeOf<T>>
);
}

Expand All @@ -178,10 +187,10 @@ export default class Meta {
* @param memberType
* @return An object containing meta information about member
*/
public resolveMember<T extends MemberType>(
public resolveMember<M extends MemberType>(
memberName: string,
memberType: T | T[]
): NotFoundMember | TCubeMemberByType<T> {
memberType: M | M[]
): NotFoundMember | TCubeMemberByTypeForMeta<MetaCubeOf<T>, M> {
const [cube] = memberName.split('.');

if (!this.cubesMap[cube]) {
Expand All @@ -200,7 +209,7 @@ export default class Meta {
};
}

return member as TCubeMemberByType<T>;
return member as TCubeMemberByTypeForMeta<MetaCubeOf<T>, M>;
}

public defaultTimeDimensionNameFor(memberName: string): string | null | undefined {
Expand Down
31 changes: 27 additions & 4 deletions packages/cubejs-client-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LoadResponse,
MeasureFormat,
MetaResponse,
MetaResponseExtended,
PivotQuery,
ProgressResponse,
Query,
Expand Down Expand Up @@ -63,6 +64,16 @@ export type LoadMethodOptions = {
baseRequestId?: string;
};

export type MetaMethodOptions = LoadMethodOptions & {
/**
* When `true`, requests extended meta from the API (joins, pre-aggregations, SQL snippets, etc.).
* Only pass this property when the value is `true`. Omit it when extended meta is not needed —
* the gateway treats the presence of any `extended` query parameter as extended mode, so
* sending `extended=false` would still select extended meta.
*/
extended?: boolean;
};

export type DeeplyReadonly<T> = {
readonly [K in keyof T]: DeeplyReadonly<T[K]>;
};
Expand Down Expand Up @@ -690,20 +701,32 @@ class CubeApi {
);
}

public meta(options?: LoadMethodOptions): Promise<Meta>;
public meta(options?: MetaMethodOptions & { extended?: false }): Promise<Meta<MetaResponse>>;

public meta(options: MetaMethodOptions & { extended: true }): Promise<Meta<MetaResponseExtended>>;

public meta(options?: MetaMethodOptions & { extended?: false }, callback?: LoadMethodCallback<Meta<MetaResponse>>): UnsubscribeObj;

public meta(options?: LoadMethodOptions, callback?: LoadMethodCallback<Meta>): UnsubscribeObj;
public meta(options: MetaMethodOptions & { extended: true }, callback?: LoadMethodCallback<Meta<MetaResponseExtended>>): UnsubscribeObj;

/**
* Get meta description of cubes available for querying.
*/
public meta(options?: LoadMethodOptions, callback?: LoadMethodCallback<Meta>): Promise<Meta> | UnsubscribeObj {
public meta(
options?: MetaMethodOptions,
callback?: LoadMethodCallback<Meta<MetaResponse>> | LoadMethodCallback<Meta<MetaResponseExtended>>
): Promise<Meta<MetaResponse>> | Promise<Meta<MetaResponseExtended>> | UnsubscribeObj {
return this.loadMethod(
() => this.request('meta', {
signal: options?.signal,
baseRequestId: options?.baseRequestId,
...(options?.extended === true ? { extended: true } : {}),
}),
(body: MetaResponse) => new Meta(body),
(body: MetaResponse | MetaResponseExtended) => (
options?.extended === true
? new Meta<MetaResponseExtended>(body as MetaResponseExtended)
: new Meta<MetaResponse>(body as MetaResponse)
),
options,
callback
);
Expand Down
Loading
Loading