Skip to content

Commit 4875b8e

Browse files
authored
feat(schema-compiler): expose custom granularities via meta API (#8703)
* Add DimensionGranularity to Meta OpenAPI Spec * feat(schema-compiler): expose custom granularities via meta API * add unit test for custom granularities in meta * add an optional title to custom granularity * Add tests for granularities with and w/o title in meta
1 parent 148e4cf commit 4875b8e

File tree

7 files changed

+61
-3
lines changed

7 files changed

+61
-3
lines changed

packages/cubejs-api-gateway/openspec.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ components:
9898
type: "string"
9999
shortTitle:
100100
type: "string"
101+
V1CubeMetaDimensionGranularity:
102+
type: "object"
103+
required:
104+
- name
105+
- title
106+
properties:
107+
name:
108+
type: "string"
109+
title:
110+
type: "string"
101111
V1CubeMetaDimension:
102112
type: "object"
103113
required:
@@ -110,6 +120,10 @@ components:
110120
type: "string"
111121
type:
112122
type: "string"
123+
granularities:
124+
type: array
125+
items:
126+
$ref: "#/components/schemas/V1CubeMetaDimensionGranularity"
113127
V1CubeMetaMeasure:
114128
type: "object"
115129
required:

packages/cubejs-schema-compiler/src/compiler/CubeToMetaTransformer.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class CubeToMetaTransformer {
3939
*/
4040
transform(cube) {
4141
const cubeTitle = cube.title || this.titleize(cube.name);
42-
42+
4343
const isCubeVisible = this.isVisible(cube, true);
4444

4545
return {
@@ -80,6 +80,13 @@ export class CubeToMetaTransformer {
8080
? this.isVisible(nameToDimension[1], !nameToDimension[1].primaryKey)
8181
: false,
8282
primaryKey: !!nameToDimension[1].primaryKey,
83+
granularities:
84+
nameToDimension[1].granularities
85+
? R.compose(R.map((g) => ({
86+
name: g[0],
87+
title: this.title(cubeTitle, g, true),
88+
})), R.toPairs)(nameToDimension[1].granularities)
89+
: undefined,
8390
})),
8491
R.toPairs
8592
)(cube.dimensions || {}),

packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ const BaseDimensionWithoutSubQuery = {
111111
then: Joi.object().pattern(identifierRegex,
112112
Joi.alternatives([
113113
Joi.object().keys({
114+
title: Joi.string(),
114115
interval: GranularityInterval.required(),
115116
origin: Joi.string().required().custom((value, helpers) => {
116117
const date = new Date(value);
@@ -122,6 +123,7 @@ const BaseDimensionWithoutSubQuery = {
122123
}),
123124
}),
124125
Joi.object().keys({
126+
title: Joi.string(),
125127
interval: GranularityInterval.required().custom((value, helper) => {
126128
const intParsed = value.split(' ');
127129
const msg = { custom: 'Arbitrary intervals cannot be used without origin point specified' };

packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,8 @@ describe('Cube Validation', () => {
603603
{
604604
const cube = newCube({
605605
half_year: {
606-
interval: '6 months'
606+
interval: '6 months',
607+
title: 'Half year intervals'
607608
}
608609
});
609610

@@ -675,6 +676,7 @@ describe('Cube Validation', () => {
675676
half_year: {
676677
interval: '6 months',
677678
offset: '4 weeks 5 days 6 hours',
679+
title: 'Half year intervals title'
678680
}
679681
});
680682

@@ -794,6 +796,7 @@ describe('Cube Validation', () => {
794796
half_year: {
795797
interval: '15 days',
796798
offset: '1 hours 7 minutes 8 seconds',
799+
title: 'Just title'
797800
}
798801
});
799802

@@ -884,6 +887,7 @@ describe('Cube Validation', () => {
884887
half_year: {
885888
interval: '10 months',
886889
origin: '2024-04',
890+
title: 'Someone loves number 10'
887891
}
888892
});
889893

packages/cubejs-schema-compiler/test/unit/schema.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { prepareCompiler } from './PrepareCompiler';
2-
import { createCubeSchema } from './utils';
2+
import { createCubeSchema, createCubeSchemaWithCustomGranularities } from './utils';
33

44
describe('Schema Testing', () => {
55
const schemaCompile = async () => {
@@ -294,6 +294,33 @@ describe('Schema Testing', () => {
294294
expect(segments.find((segment) => segment.name === 'CubeA.sfUsers').description).toBe('SF users segment from createCubeSchema');
295295
});
296296

297+
it('custom granularities in meta', async () => {
298+
const { compiler, metaTransformer } = prepareCompiler([
299+
createCubeSchemaWithCustomGranularities('orders')
300+
]);
301+
await compiler.compile();
302+
303+
const { dimensions } = metaTransformer.cubes[0].config;
304+
305+
expect(dimensions).toBeDefined();
306+
expect(dimensions.length).toBeGreaterThan(0);
307+
308+
const dg = dimensions.find((dimension) => dimension.name === 'orders.createdAt');
309+
expect(dg).toBeDefined();
310+
expect(dg.granularities).toBeDefined();
311+
expect(dg.granularities.length).toBeGreaterThan(0);
312+
313+
// Granularity defined with title
314+
let gr = dg.granularities.find(g => g.name === 'half_year');
315+
expect(gr).toBeDefined();
316+
expect(gr.title).toBe('6 month intervals');
317+
318+
// // Granularity defined without title -> titlize()
319+
gr = dg.granularities.find(g => g.name === 'half_year_by_1st_june');
320+
expect(gr).toBeDefined();
321+
expect(gr.title).toBe('Half Year By1 St June');
322+
});
323+
297324
it('join types', async () => {
298325
const { compiler, cubeEvaluator } = prepareCompiler([
299326
createCubeSchema({

packages/cubejs-schema-compiler/test/unit/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ export function createCubeSchemaWithCustomGranularities(name: string): string {
8888
granularities: {
8989
half_year: {
9090
interval: '6 months',
91+
title: '6 month intervals'
9192
},
9293
half_year_by_1st_april: {
94+
title: 'Half year from Apr to Oct',
9395
interval: '6 months',
9496
offset: '3 months'
9597
},

packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,10 +324,12 @@ describe('Yaml Schema Testing', () => {
324324
granularities:
325325
- name: six_months
326326
interval: 6 months
327+
title: 6 month intervals
327328
- name: three_months_offset
328329
interval: 3 months
329330
offset: 2 weeks
330331
- name: fiscal_year_1st_april
332+
title: Fiscal year by Apr
331333
interval: 1 year
332334
origin: >
333335
2024-04-01

0 commit comments

Comments
 (0)