Skip to content

Commit 6d91d49

Browse files
authored
feat: Handle custom generators for faker (#101)
Allow users to add custom generators when using the faker library. The syntax is similar to the current generators for casual. Fixes #97
1 parent 1e854be commit 6d91d49

File tree

4 files changed

+316
-8
lines changed

4 files changed

+316
-8
lines changed

README.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ Changes the case of the enums. Accepts `upper-case#upperCase`, `pascal-case#pasc
4444

4545
Allows you to define mappings for your custom scalars. Allows you to map any GraphQL Scalar to a
4646
[casual](https://github.com/boo1ean/casual#embedded-generators) embedded generator (string or
47-
function key) with optional arguments
47+
function key) with optional arguments, or a or [faker](https://fakerjs.dev/api/) generator with optional arguments
48+
49+
Examples using **casual**
4850

49-
Examples
5051
**With arguments**
5152

52-
```
53+
```yaml
5354
plugins:
5455
- typescript-mock-data:
5556
scalars:
@@ -60,7 +61,7 @@ plugins:
6061
6162
**With multiple arguments**
6263
63-
```
64+
```yaml
6465
plugins:
6566
- typescript-mock-data:
6667
scalars:
@@ -73,13 +74,48 @@ plugins:
7374
7475
**Shorthand if you don't have arguments**
7576
76-
```
77+
```yaml
7778
plugins:
7879
- typescript-mock-data:
7980
scalars:
8081
Date: date # gets translated to casual.date()
8182
```
8283
84+
Examples using **faker**
85+
86+
**With arguments**
87+
88+
```yaml
89+
plugins:
90+
- typescript-mock-data:
91+
scalars:
92+
Date: # gets translated to faker.date.past(10)
93+
generator: date.past
94+
arguments: 10
95+
```
96+
97+
**With multiple arguments**
98+
99+
```yaml
100+
plugins:
101+
- typescript-mock-data:
102+
scalars:
103+
Description: # gets translated to faker.lorem.paragraphs(3, '\n')
104+
generator: lorem.paragraphs
105+
arguments:
106+
- 3
107+
- '\n'
108+
```
109+
110+
**Shorthand if you don't have arguments**
111+
112+
```yaml
113+
plugins:
114+
- typescript-mock-data:
115+
scalars:
116+
Date: date.past # gets translated to faker.date.past()
117+
```
118+
83119
**Custom value generator**
84120
85121
```yaml

src/index.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ASTKindToNode, ListTypeNode, NamedTypeNode, parse, printSchema, TypeNode } from 'graphql';
2+
import { faker } from '@faker-js/faker';
23
import casual from 'casual';
34
import { PluginFunction, oldVisit } from '@graphql-codegen/plugin-helpers';
45
import { pascalCase } from 'pascal-case';
@@ -101,7 +102,7 @@ const getScalarDefinition = (value: ScalarDefinition | ScalarGeneratorName): Sca
101102
return value;
102103
};
103104

104-
const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
105+
const getCasualCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
105106
// If there is a mapping to a `casual` type, then use it and make sure
106107
// to call it if it's a function
107108
const embeddedGenerator = casual[customScalar.generator];
@@ -128,6 +129,65 @@ const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<Name
128129
return value;
129130
};
130131

132+
const getFakerGenerators = (generatorName: ScalarGeneratorName) => {
133+
let embeddedGenerator: unknown = faker;
134+
let dynamicGenerator = 'faker';
135+
136+
if (typeof generatorName === 'string') {
137+
const generatorPath = generatorName.split('.');
138+
for (const key of generatorPath) {
139+
if (typeof embeddedGenerator === 'object' && key in embeddedGenerator) {
140+
embeddedGenerator = embeddedGenerator[key];
141+
dynamicGenerator = `${dynamicGenerator}['${key}']`;
142+
}
143+
}
144+
}
145+
146+
// If the faker generator is not a function, we can assume the path is wrong
147+
if (typeof embeddedGenerator === 'function') {
148+
return { embeddedGenerator, dynamicGenerator };
149+
}
150+
151+
return { embeddedGenerator: null, dynamicGenerator: null };
152+
};
153+
154+
const getFakerCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
155+
// If there is a mapping to a `faker` type, then use it
156+
const { embeddedGenerator, dynamicGenerator } = getFakerGenerators(customScalar.generator);
157+
if (!embeddedGenerator && customScalar.generator) {
158+
return customScalar.generator;
159+
}
160+
161+
const generatorArgs: unknown[] = Array.isArray(customScalar.arguments)
162+
? customScalar.arguments
163+
: [customScalar.arguments];
164+
if (opts.dynamicValues) {
165+
return `${dynamicGenerator}(...${JSON.stringify(generatorArgs)})`;
166+
}
167+
const value = embeddedGenerator(...generatorArgs);
168+
169+
if (typeof value === 'string') {
170+
return `'${value}'`;
171+
}
172+
if (typeof value === 'object') {
173+
return `${JSON.stringify(value)}`;
174+
}
175+
return value;
176+
};
177+
178+
const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
179+
if (opts.generateLibrary === 'casual') {
180+
return getCasualCustomScalarValue(customScalar, opts);
181+
}
182+
183+
184+
if (opts.generateLibrary === 'faker') {
185+
return getFakerCustomScalarValue(customScalar, opts);
186+
}
187+
188+
throw `Unknown generator library: ${opts.generateLibrary}`;
189+
};
190+
131191
const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean => {
132192
if (!opts.currentType) {
133193
return '';

tests/scalars/__snapshots__/spec.ts.snap

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`should generate custom scalars for native and custom types 1`] = `
3+
exports[`should generate custom scalars for native and custom types using casual 1`] = `
44
"
55
export const anA = (overrides?: Partial<A>): A => {
66
return {
@@ -20,3 +20,76 @@ export const aB = (overrides?: Partial<B>): B => {
2020
};
2121
"
2222
`;
23+
24+
exports[`should generate custom scalars for native and custom types using faker 1`] = `
25+
"
26+
export const anA = (overrides?: Partial<A>): A => {
27+
return {
28+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,
29+
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',
30+
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
31+
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : '[email protected]',
32+
};
33+
};
34+
35+
export const aB = (overrides?: Partial<B>): B => {
36+
return {
37+
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,
38+
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,
39+
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
40+
};
41+
};
42+
"
43+
`;
44+
45+
exports[`should generate dynamic custom scalars for native and custom types using casual 1`] = `
46+
"import casual from 'casual';
47+
48+
casual.seed(0);
49+
50+
export const anA = (overrides?: Partial<A>): A => {
51+
return {
52+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),
53+
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],
54+
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
55+
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : casual['email'],
56+
};
57+
};
58+
59+
export const aB = (overrides?: Partial<B>): B => {
60+
return {
61+
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),
62+
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),
63+
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
64+
};
65+
};
66+
67+
export const seedMocks = (seed: number) => casual.seed(seed);
68+
"
69+
`;
70+
71+
exports[`should generate dynamic custom scalars for native and custom types using faker 1`] = `
72+
"import { faker } from '@faker-js/faker';
73+
74+
faker.seed(0);
75+
76+
export const anA = (overrides?: Partial<A>): A => {
77+
return {
78+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\\"min\\":1,\\"max\\":100}]),
79+
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),
80+
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
81+
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : faker['internet']['email'](...[]),
82+
};
83+
};
84+
85+
export const aB = (overrides?: Partial<B>): B => {
86+
return {
87+
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\\"min\\":-100,\\"max\\":0}]),
88+
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\\"min\\":-100,\\"max\\":0}]),
89+
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
90+
};
91+
};
92+
93+
export const seedMocks = (seed: number) => faker.seed(seed);
94+
"
95+
`;

tests/scalars/spec.ts

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { plugin } from '../../src';
22
import testSchema from './schema';
33

4-
it('should generate custom scalars for native and custom types', async () => {
4+
it('should generate custom scalars for native and custom types using casual', async () => {
55
const result = await plugin(testSchema, [], {
66
scalars: {
77
String: 'string',
@@ -45,3 +45,142 @@ it('should generate custom scalars for native and custom types', async () => {
4545

4646
expect(result).toMatchSnapshot();
4747
});
48+
49+
it('should generate dynamic custom scalars for native and custom types using casual', async () => {
50+
const result = await plugin(testSchema, [], {
51+
dynamicValues: true,
52+
scalars: {
53+
String: 'string',
54+
Float: {
55+
generator: 'double',
56+
arguments: [-100, 0],
57+
},
58+
ID: {
59+
generator: 'integer',
60+
arguments: [1, 100],
61+
},
62+
Boolean: 'false',
63+
Int: {
64+
generator: 'integer',
65+
arguments: [-100, 0],
66+
},
67+
AnyObject: 'email',
68+
},
69+
});
70+
71+
expect(result).toBeDefined();
72+
73+
// String
74+
expect(result).toContain(
75+
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],",
76+
);
77+
78+
// Float
79+
expect(result).toContain(
80+
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),",
81+
);
82+
83+
// ID
84+
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),");
85+
86+
// Boolean
87+
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");
88+
89+
// Int
90+
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),");
91+
92+
expect(result).toMatchSnapshot();
93+
});
94+
95+
it('should generate custom scalars for native and custom types using faker', async () => {
96+
const result = await plugin(testSchema, [], {
97+
generateLibrary: 'faker',
98+
scalars: {
99+
String: 'lorem.sentence',
100+
Float: {
101+
generator: 'datatype.float',
102+
arguments: [{ min: -100, max: 0}],
103+
},
104+
ID: {
105+
generator: 'datatype.number',
106+
arguments: [{ min: 1, max: 100}],
107+
},
108+
Boolean: 'false',
109+
Int: {
110+
generator: 'datatype.number',
111+
arguments: [{ min: -100, max: 0}],
112+
},
113+
AnyObject: 'internet.email',
114+
},
115+
});
116+
117+
expect(result).toBeDefined();
118+
119+
// String
120+
expect(result).toContain(
121+
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',",
122+
);
123+
124+
// Float
125+
expect(result).toContain(
126+
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,",
127+
);
128+
129+
// ID
130+
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,");
131+
132+
// Boolean
133+
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");
134+
135+
// Int
136+
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,");
137+
138+
expect(result).toMatchSnapshot();
139+
});
140+
141+
it('should generate dynamic custom scalars for native and custom types using faker', async () => {
142+
const result = await plugin(testSchema, [], {
143+
generateLibrary: 'faker',
144+
dynamicValues: true,
145+
scalars: {
146+
String: 'lorem.sentence',
147+
Float: {
148+
generator: 'datatype.float',
149+
arguments: [{ min: -100, max: 0}],
150+
},
151+
ID: {
152+
generator: 'datatype.number',
153+
arguments: [{ min: 1, max: 100}],
154+
},
155+
Boolean: 'false',
156+
Int: {
157+
generator: 'datatype.number',
158+
arguments: [{ min: -100, max: 0}],
159+
},
160+
AnyObject: 'internet.email',
161+
},
162+
});
163+
164+
expect(result).toBeDefined();
165+
166+
// String
167+
expect(result).toContain(
168+
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),",
169+
);
170+
171+
// Float
172+
expect(result).toContain(
173+
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\"min\":-100,\"max\":0}]),",
174+
);
175+
176+
// ID
177+
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\"min\":1,\"max\":100}]),");
178+
179+
// Boolean
180+
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");
181+
182+
// Int
183+
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\"min\":-100,\"max\":0}]),");
184+
185+
expect(result).toMatchSnapshot();
186+
});

0 commit comments

Comments
 (0)