Skip to content

Commit 47956ea

Browse files
authored
fix: graphql non capital cube name issue (#5680) Thanks @MattFanto!
* Fixed with normalized cube config * Refactor and solved TODO * Fixed lint issue * Added segments props * Fixed an indentation * removed warning Co-authored-by: mfantoni <[email protected]> Fixes #5643
1 parent 6f9433b commit 47956ea

File tree

2 files changed

+104
-16
lines changed

2 files changed

+104
-16
lines changed

packages/cubejs-api-gateway/src/graphql.ts

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
FieldNode,
99
VariableNode,
1010
ValueNode,
11+
GraphQLSchema,
1112
} from 'graphql';
1213

1314
import {
@@ -154,7 +155,7 @@ function mapWhereValue(operator: string, value: any) {
154155
if (value.length === 1 && !/^[0-9]/.test(value[0])) {
155156
return value[0].toString();
156157
}
157-
158+
158159
return value.map(v => v.toString());
159160
default:
160161
return Array.isArray(value) ? value.map(v => v.toString()) : [value.toString()];
@@ -173,6 +174,33 @@ function unCapitalize(name: string) {
173174
return `${name[0].toLowerCase()}${name.slice(1)}`;
174175
}
175176

177+
function normalizeCubeCapital(cube: any) {
178+
const { config } = cube;
179+
if (config.name[0] === config.name[0].toUpperCase()) {
180+
return cube;
181+
}
182+
const normalizedConfig = { ...config };
183+
normalizedConfig.name = capitalize(config.name);
184+
if (config.measures) {
185+
normalizedConfig.measures = config.measures.map(
186+
(m: {name: string}) => ({ ...m, name: capitalize(m.name) })
187+
);
188+
}
189+
if (config.dimensions) {
190+
normalizedConfig.dimensions = config.dimensions.map(
191+
(m: {name: string}) => ({ ...m, name: capitalize(m.name) })
192+
);
193+
}
194+
if (config.segments) {
195+
normalizedConfig.segments = config.dimensions.map(
196+
(m: {name: string}) => ({ ...m, name: capitalize(m.name) })
197+
);
198+
}
199+
return {
200+
config: normalizedConfig
201+
};
202+
}
203+
176204
function applyDirectives(
177205
directives: readonly DirectiveNode[] | undefined,
178206
values: Record<string, any>
@@ -245,7 +273,7 @@ function whereArgToQueryFilters(
245273
prefix?: string
246274
) {
247275
const queryFilters: any[] = [];
248-
276+
249277
Object.keys(whereArg).forEach((key) => {
250278
if (['OR', 'AND'].includes(key)) {
251279
queryFilters.push({
@@ -333,7 +361,7 @@ function parseDates(result: any) {
333361
});
334362
}
335363

336-
export function makeSchema(metaConfig: any) {
364+
export function makeSchema(metaConfig: any): GraphQLSchema {
337365
const types: any[] = [
338366
DateTimeScalar,
339367
FloatFilter,
@@ -342,12 +370,14 @@ export function makeSchema(metaConfig: any) {
342370
OrderBy,
343371
TimeDimension
344372
];
345-
373+
346374
function hasMembers(cube: any) {
347375
return cube.config.measures.length || cube.config.dimensions.length;
348376
}
349377

350-
metaConfig.forEach(cube => {
378+
const normalizedMetaConfig = metaConfig.map((cube: any) => (normalizeCubeCapital(cube)));
379+
380+
normalizedMetaConfig.forEach(cube => {
351381
if (hasMembers(cube)) {
352382
types.push(objectType({
353383
name: `${cube.config.name}Members`,
@@ -370,7 +400,7 @@ export function makeSchema(metaConfig: any) {
370400
});
371401
}
372402
}));
373-
403+
374404
types.push(inputObjectType({
375405
name: `${cube.config.name}WhereInput`,
376406
definition(t) {
@@ -420,7 +450,7 @@ export function makeSchema(metaConfig: any) {
420450
definition(t) {
421451
t.field('AND', { type: list(nonNull('RootWhereInput')) });
422452
t.field('OR', { type: list(nonNull('RootWhereInput')) });
423-
metaConfig.forEach(cube => {
453+
normalizedMetaConfig.forEach(cube => {
424454
if (hasMembers(cube)) {
425455
t.field(unCapitalize(cube.config.name), {
426456
type: `${cube.config.name}WhereInput`
@@ -429,11 +459,11 @@ export function makeSchema(metaConfig: any) {
429459
});
430460
}
431461
}));
432-
462+
433463
types.push(inputObjectType({
434464
name: 'RootOrderByInput',
435465
definition(t) {
436-
metaConfig.forEach(cube => {
466+
normalizedMetaConfig.forEach(cube => {
437467
if (hasMembers(cube)) {
438468
t.field(unCapitalize(cube.config.name), {
439469
type: `${cube.config.name}OrderByInput`
@@ -446,7 +476,7 @@ export function makeSchema(metaConfig: any) {
446476
types.push(objectType({
447477
name: 'Result',
448478
definition(t) {
449-
metaConfig.forEach(cube => {
479+
normalizedMetaConfig.forEach(cube => {
450480
if (hasMembers(cube)) {
451481
t.nonNull.field(unCapitalize(cube.config.name), {
452482
type: `${cube.config.name}Members`,
@@ -487,11 +517,11 @@ export function makeSchema(metaConfig: any) {
487517
const timeDimensions: any[] = [];
488518
let filters: any[] = [];
489519
const order: [string, 'asc' | 'desc'][] = [];
490-
520+
491521
if (where) {
492522
filters = whereArgToQueryFilters(where);
493523
}
494-
524+
495525
if (orderBy) {
496526
Object.entries<any>(orderBy).forEach(([cubeName, members]) => {
497527
Object.entries<any>(members).forEach(([member, value]) => {
@@ -514,20 +544,20 @@ export function makeSchema(metaConfig: any) {
514544
if (whereArg) {
515545
filters = whereArgToQueryFilters(whereArg, cubeName).concat(filters);
516546
}
517-
547+
518548
const inDateRangeFilters = {};
519549
filters = filters.filter((f) => {
520550
if (f.operator === 'inDateRange') {
521551
inDateRangeFilters[f.member] = f.values;
522552
return false;
523553
}
524-
554+
525555
return true;
526556
});
527557

528558
getFieldNodeChildren(cubeNode, infos).forEach(memberNode => {
529559
const memberName = memberNode.name.value;
530-
const memberType = getMemberType(metaConfig, cubeName, memberName);
560+
const memberType = getMemberType(normalizedMetaConfig, cubeName, memberName);
531561
const key = `${cubeName}.${memberName}`;
532562

533563
if (memberType === MemberType.MEASURES) {
@@ -567,7 +597,7 @@ export function makeSchema(metaConfig: any) {
567597
...(filters.length && { filters }),
568598
...(renewQuery && { renewQuery }),
569599
};
570-
600+
571601
// eslint-disable-next-line no-async-promise-executor
572602
const results = await (new Promise<any>(async (resolve, reject) => {
573603
try {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* globals describe,test,expect */
2+
3+
import { GraphQLObjectType } from 'graphql';
4+
import { makeSchema } from '../src/graphql';
5+
6+
const metaConfig = [
7+
{
8+
config: {
9+
name: 'Foo',
10+
measures: [
11+
{
12+
name: 'Foo.bar',
13+
isVisible: true,
14+
},
15+
],
16+
dimensions: [
17+
{
18+
name: 'Foo.id',
19+
isVisible: true,
20+
},
21+
{
22+
name: 'Foo.time',
23+
isVisible: true,
24+
},
25+
],
26+
segments: [
27+
{
28+
name: 'Foo.last1y',
29+
isVisible: true,
30+
}
31+
]
32+
},
33+
},
34+
];
35+
36+
function expectValidSchema(schema) {
37+
expect(schema).toBeDefined();
38+
expect(schema.getTypeMap()).toHaveProperty('FooMembers');
39+
const fooFields = (schema.getType('FooMembers') as GraphQLObjectType).getFields();
40+
expect(fooFields).toHaveProperty('bar');
41+
expect(fooFields).toHaveProperty('id');
42+
expect(fooFields).toHaveProperty('time');
43+
}
44+
45+
describe('Graphql Schema', () => {
46+
test('should make valid schema', () => {
47+
const schema = makeSchema(metaConfig);
48+
expectValidSchema(schema);
49+
});
50+
51+
test('should make valid schema when name is not capitalized', async () => {
52+
const schema = makeSchema(JSON.parse(
53+
JSON.stringify(metaConfig)
54+
.replace(/Foo/g, 'foo')
55+
));
56+
expectValidSchema(schema);
57+
});
58+
});

0 commit comments

Comments
 (0)