Skip to content

Commit 7dcc9cf

Browse files
authored
feat(scalars): add support for custom scalar with arguments (#30)
Some `casual.xxx` generators can accept arguments. To give more flexibility, the `scalars` config now accepts: - a string for retrocompatibility - an object `{generator: string, arguments: any[]}` to pass arguments to the casual function
1 parent 79ccb90 commit 7dcc9cf

File tree

6 files changed

+270
-82
lines changed

6 files changed

+270
-82
lines changed

README.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,45 @@ Changes the case of the enums. Accepts `upper-case#upperCase`, `pascal-case#pasc
3131

3232
Changes the case of the enums. Accepts `upper-case#upperCase`, `pascal-case#pascalCase` or `keep`
3333

34-
### scalars (`{ [Scalar: string]: keyof Casual.Casual | Casual.functions }`, defaultValue: `undefined`)
34+
### scalars (`{ [Scalar: string]: ScalarDefinition }`, defaultValue: `undefined`)
3535

3636
Allows you to define mappings for your custom scalars. Allows you to map any GraphQL Scalar to a
3737
[casual](https://github.com/boo1ean/casual#embedded-generators) embedded generator (string or
38-
function key)
38+
function key) with optional arguments
39+
40+
Examples
41+
**With arguments**
42+
43+
```
44+
plugins:
45+
- typescript-mock-data:
46+
scalars:
47+
Date: # gets translated to casual.date('YYYY-MM-DD')
48+
generator: date
49+
arguments: 'YYYY-MM-DD'
50+
```
51+
52+
**With multiple arguments**
53+
54+
```
55+
plugins:
56+
- typescript-mock-data:
57+
scalars:
58+
PaginatedAmount: # gets translated to casual.date(-100, 100)
59+
generator: integer
60+
arguments:
61+
- -100
62+
- 100
63+
```
64+
65+
**Shorthand if you don't have arguments**
66+
67+
```
68+
plugins:
69+
- typescript-mock-data:
70+
scalars:
71+
Date: date # gets translated to casual.date()
72+
```
3973

4074
### typesPrefix (`string`, defaultValue: '')
4175

@@ -69,7 +103,7 @@ generates:
69103
- 'typescript'
70104
src/mocks/generated-mocks.ts:
71105
plugins:
72-
- 'graphql-codegen-typescript-mock-data':
106+
- typescript-mock-data:
73107
typesFile: '../generated-types.ts'
74108
enumValues: upper-case#upperCase
75109
typenames: keep
@@ -156,6 +190,18 @@ const user = aUser({ login: 'johndoe' });
156190
// will create a user object with `login` property overridden to `johndoe`
157191
```
158192

193+
### Dealing with Timezone
194+
195+
If some properties use generated dates, the result could different depending on the timezone of your machine.
196+
197+
To force a timezone, you can set environment variable `TZ`:
198+
199+
```bash
200+
TZ=UTC graphql-codegen
201+
```
202+
203+
This will force the timezone to `UTC`, whatever the timezone of your machine or CI
204+
159205
### Contributing
160206

161207
Feel free to open issues and pull requests. We always welcome support from the community.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"sideEffects": false,
6060
"scripts": {
6161
"build": "tsc -m esnext --outDir dist/esnext && tsc -m commonjs --outDir dist/commonjs",
62-
"test": "jest",
62+
"test": "TZ=UTC jest",
6363
"lint": "eslint 'src/**/*.{js,ts,tsx}' --quiet --fix && tsc --noEmit",
6464
"prettify": "prettier --config ./.prettierrc.js --write",
6565
"auto:version": "yarn version --`auto version` --message 'Bump version to: %s [skip ci]'",

src/index.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ const hashedString = (value: string) => {
5858
return hash;
5959
};
6060

61+
const getScalarDefinition = (value: ScalarDefinition | ScalarGeneratorName): ScalarDefinition => {
62+
if (typeof value === 'string') {
63+
return {
64+
generator: value,
65+
arguments: [],
66+
};
67+
}
68+
return value;
69+
};
70+
6171
const getNamedType = (
6272
typeName: string,
6373
fieldName: string,
@@ -85,8 +95,6 @@ const getNamedType = (
8595
return casual.boolean;
8696
case 'Int':
8797
return casual.integer(0, 9999);
88-
case 'Date':
89-
return `'${new Date(casual.unix_time).toISOString()}'`;
9098
default: {
9199
const foundType = types.find((enumType: TypeItem) => enumType.name === name);
92100
if (foundType) {
@@ -109,16 +117,26 @@ const getNamedType = (
109117
foundType.types && foundType.types[0],
110118
);
111119
case 'scalar': {
120+
const customScalar = customScalars ? getScalarDefinition(customScalars[foundType.name]) : null;
112121
// it's a scalar, let's use a string as a value if there is no custom
113122
// mapping for this particular scalar
114-
if (!customScalars || !customScalars[foundType.name]) {
123+
if (!customScalar || !customScalar.generator) {
124+
if (foundType.name === 'Date') {
125+
return `'${new Date(casual.unix_time).toISOString()}'`;
126+
}
115127
return `'${casual.word}'`;
116128
}
117129

118130
// If there is a mapping to a `casual` type, then use it and make sure
119131
// to call it if it's a function
120-
const embeddedGenerator = casual[customScalars[foundType.name]];
121-
const value = typeof embeddedGenerator === 'function' ? embeddedGenerator() : embeddedGenerator;
132+
const embeddedGenerator = casual[customScalar.generator];
133+
const generatorArgs: unknown[] = Array.isArray(customScalar.arguments)
134+
? customScalar.arguments
135+
: [customScalar.arguments];
136+
const value =
137+
typeof embeddedGenerator === 'function'
138+
? embeddedGenerator(...generatorArgs)
139+
: embeddedGenerator;
122140

123141
if (typeof value === 'string') {
124142
return `'${value}'`;
@@ -208,7 +226,15 @@ ${fields}
208226
};`;
209227
};
210228

211-
type ScalarMap = { [name: string]: keyof (Casual.Casual | Casual.functions) };
229+
type ScalarGeneratorName = keyof Casual.Casual | keyof Casual.functions;
230+
type ScalarDefinition = {
231+
generator: ScalarGeneratorName;
232+
arguments: unknown;
233+
};
234+
235+
type ScalarMap = {
236+
[name: string]: ScalarGeneratorName | ScalarDefinition;
237+
};
212238

213239
export interface TypescriptMocksPluginConfig {
214240
typesFile?: string;

tests/__snapshots__/typescript-mock-data.spec.ts.snap

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,80 @@ export const aUser = (overrides?: Partial<Api.User>): Api.User => {
7474
"
7575
`;
7676
77+
exports[`should correctly generate the \`casual\` data for a function with arguments scalar mapping 1`] = `
78+
"
79+
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {
80+
return {
81+
abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit',
82+
};
83+
};
84+
85+
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
86+
return {
87+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38',
88+
url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid',
89+
};
90+
};
91+
92+
export const anUpdateUserInput = (overrides?: Partial<UpdateUserInput>): UpdateUserInput => {
93+
return {
94+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc',
95+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui',
96+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
97+
};
98+
};
99+
100+
export const aUser = (overrides?: Partial<User>): User => {
101+
return {
102+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
103+
creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z',
104+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
105+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
106+
status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online,
107+
customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : AbcStatus.HasXyzStatus,
108+
scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : '1977-06-26',
109+
};
110+
};
111+
"
112+
`;
113+
114+
exports[`should correctly generate the \`casual\` data for a function with one argument scalar mapping 1`] = `
115+
"
116+
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {
117+
return {
118+
abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit',
119+
};
120+
};
121+
122+
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
123+
return {
124+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38',
125+
url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid',
126+
};
127+
};
128+
129+
export const anUpdateUserInput = (overrides?: Partial<UpdateUserInput>): UpdateUserInput => {
130+
return {
131+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc',
132+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui',
133+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
134+
};
135+
};
136+
137+
export const aUser = (overrides?: Partial<User>): User => {
138+
return {
139+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
140+
creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z',
141+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
142+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
143+
status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online,
144+
customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : AbcStatus.HasXyzStatus,
145+
scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : '1977-06-26',
146+
};
147+
};
148+
"
149+
`;
150+
77151
exports[`should correctly generate the \`casual\` data for a non-string scalar mapping 1`] = `
78152
"
79153
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {

tests/typescript-mock-data.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,36 @@ it('should correctly generate the `casual` data for a non-string scalar mapping'
194194
expect(result).toMatchSnapshot();
195195
});
196196

197+
it('should correctly generate the `casual` data for a function with arguments scalar mapping', async () => {
198+
const result = await plugin(testSchema, [], {
199+
scalars: {
200+
AnyObject: {
201+
generator: 'date',
202+
arguments: ['YYYY-MM-DD'],
203+
},
204+
},
205+
});
206+
207+
expect(result).toBeDefined();
208+
expect(result).toContain("'1977-06-26'");
209+
expect(result).toMatchSnapshot();
210+
});
211+
212+
it('should correctly generate the `casual` data for a function with one argument scalar mapping', async () => {
213+
const result = await plugin(testSchema, [], {
214+
scalars: {
215+
AnyObject: {
216+
generator: 'date',
217+
arguments: 'YYYY-MM-DD',
218+
},
219+
},
220+
});
221+
222+
expect(result).toBeDefined();
223+
expect(result).toContain("'1977-06-26'");
224+
expect(result).toMatchSnapshot();
225+
});
226+
197227
it('should add typesPrefix to all types when option is specified', async () => {
198228
const result = await plugin(testSchema, [], { typesPrefix: 'Api.' });
199229

0 commit comments

Comments
 (0)