Skip to content

Commit

Permalink
feat: Handle custom generators for faker (#101)
Browse files Browse the repository at this point in the history
Allow users to add custom generators when using the faker library.

The syntax is similar to the current generators for casual.

Fixes #97
  • Loading branch information
Morphyish authored Nov 17, 2022
1 parent 1e854be commit 6d91d49
Show file tree
Hide file tree
Showing 4 changed files with 316 additions and 8 deletions.
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ Changes the case of the enums. Accepts `upper-case#upperCase`, `pascal-case#pasc

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

Examples using **casual**

Examples
**With arguments**

```
```yaml
plugins:
- typescript-mock-data:
scalars:
Expand All @@ -60,7 +61,7 @@ plugins:
**With multiple arguments**
```
```yaml
plugins:
- typescript-mock-data:
scalars:
Expand All @@ -73,13 +74,48 @@ plugins:
**Shorthand if you don't have arguments**
```
```yaml
plugins:
- typescript-mock-data:
scalars:
Date: date # gets translated to casual.date()
```
Examples using **faker**
**With arguments**
```yaml
plugins:
- typescript-mock-data:
scalars:
Date: # gets translated to faker.date.past(10)
generator: date.past
arguments: 10
```
**With multiple arguments**
```yaml
plugins:
- typescript-mock-data:
scalars:
Description: # gets translated to faker.lorem.paragraphs(3, '\n')
generator: lorem.paragraphs
arguments:
- 3
- '\n'
```
**Shorthand if you don't have arguments**
```yaml
plugins:
- typescript-mock-data:
scalars:
Date: date.past # gets translated to faker.date.past()
```
**Custom value generator**
```yaml
Expand Down
62 changes: 61 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ASTKindToNode, ListTypeNode, NamedTypeNode, parse, printSchema, TypeNode } from 'graphql';
import { faker } from '@faker-js/faker';
import casual from 'casual';
import { PluginFunction, oldVisit } from '@graphql-codegen/plugin-helpers';
import { pascalCase } from 'pascal-case';
Expand Down Expand Up @@ -101,7 +102,7 @@ const getScalarDefinition = (value: ScalarDefinition | ScalarGeneratorName): Sca
return value;
};

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

const getFakerGenerators = (generatorName: ScalarGeneratorName) => {
let embeddedGenerator: unknown = faker;
let dynamicGenerator = 'faker';

if (typeof generatorName === 'string') {
const generatorPath = generatorName.split('.');
for (const key of generatorPath) {
if (typeof embeddedGenerator === 'object' && key in embeddedGenerator) {
embeddedGenerator = embeddedGenerator[key];
dynamicGenerator = `${dynamicGenerator}['${key}']`;
}
}
}

// If the faker generator is not a function, we can assume the path is wrong
if (typeof embeddedGenerator === 'function') {
return { embeddedGenerator, dynamicGenerator };
}

return { embeddedGenerator: null, dynamicGenerator: null };
};

const getFakerCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
// If there is a mapping to a `faker` type, then use it
const { embeddedGenerator, dynamicGenerator } = getFakerGenerators(customScalar.generator);
if (!embeddedGenerator && customScalar.generator) {
return customScalar.generator;
}

const generatorArgs: unknown[] = Array.isArray(customScalar.arguments)
? customScalar.arguments
: [customScalar.arguments];
if (opts.dynamicValues) {
return `${dynamicGenerator}(...${JSON.stringify(generatorArgs)})`;
}
const value = embeddedGenerator(...generatorArgs);

if (typeof value === 'string') {
return `'${value}'`;
}
if (typeof value === 'object') {
return `${JSON.stringify(value)}`;
}
return value;
};

const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<NamedTypeNode>) => {
if (opts.generateLibrary === 'casual') {
return getCasualCustomScalarValue(customScalar, opts);
}


if (opts.generateLibrary === 'faker') {
return getFakerCustomScalarValue(customScalar, opts);
}

throw `Unknown generator library: ${opts.generateLibrary}`;
};

const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean => {
if (!opts.currentType) {
return '';
Expand Down
75 changes: 74 additions & 1 deletion tests/scalars/__snapshots__/spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should generate custom scalars for native and custom types 1`] = `
exports[`should generate custom scalars for native and custom types using casual 1`] = `
"
export const anA = (overrides?: Partial<A>): A => {
return {
Expand All @@ -20,3 +20,76 @@ export const aB = (overrides?: Partial<B>): B => {
};
"
`;
exports[`should generate custom scalars for native and custom types using faker 1`] = `
"
export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : '[email protected]',
};
};
export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};
"
`;
exports[`should generate dynamic custom scalars for native and custom types using casual 1`] = `
"import casual from 'casual';
casual.seed(0);
export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : casual['email'],
};
};
export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};
export const seedMocks = (seed: number) => casual.seed(seed);
"
`;
exports[`should generate dynamic custom scalars for native and custom types using faker 1`] = `
"import { faker } from '@faker-js/faker';
faker.seed(0);
export const anA = (overrides?: Partial<A>): A => {
return {
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\\"min\\":1,\\"max\\":100}]),
str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),
obj: overrides && overrides.hasOwnProperty('obj') ? overrides.obj! : aB(),
anyObject: overrides && overrides.hasOwnProperty('anyObject') ? overrides.anyObject! : faker['internet']['email'](...[]),
};
};
export const aB = (overrides?: Partial<B>): B => {
return {
int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\\"min\\":-100,\\"max\\":0}]),
flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\\"min\\":-100,\\"max\\":0}]),
bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false,
};
};
export const seedMocks = (seed: number) => faker.seed(seed);
"
`;
141 changes: 140 additions & 1 deletion tests/scalars/spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { plugin } from '../../src';
import testSchema from './schema';

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

expect(result).toMatchSnapshot();
});

it('should generate dynamic custom scalars for native and custom types using casual', async () => {
const result = await plugin(testSchema, [], {
dynamicValues: true,
scalars: {
String: 'string',
Float: {
generator: 'double',
arguments: [-100, 0],
},
ID: {
generator: 'integer',
arguments: [1, 100],
},
Boolean: 'false',
Int: {
generator: 'integer',
arguments: [-100, 0],
},
AnyObject: 'email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : casual['string'],",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : casual['double'](...[-100,0]),",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : casual['integer'](...[1,100]),");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : casual['integer'](...[-100,0]),");

expect(result).toMatchSnapshot();
});

it('should generate custom scalars for native and custom types using faker', async () => {
const result = await plugin(testSchema, [], {
generateLibrary: 'faker',
scalars: {
String: 'lorem.sentence',
Float: {
generator: 'datatype.float',
arguments: [{ min: -100, max: 0}],
},
ID: {
generator: 'datatype.number',
arguments: [{ min: 1, max: 100}],
},
Boolean: 'false',
Int: {
generator: 'datatype.number',
arguments: [{ min: -100, max: 0}],
},
AnyObject: 'internet.email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : 'Corrupti qui incidunt eius consequatur qui.',",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : -24.51,",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 83,");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : -93,");

expect(result).toMatchSnapshot();
});

it('should generate dynamic custom scalars for native and custom types using faker', async () => {
const result = await plugin(testSchema, [], {
generateLibrary: 'faker',
dynamicValues: true,
scalars: {
String: 'lorem.sentence',
Float: {
generator: 'datatype.float',
arguments: [{ min: -100, max: 0}],
},
ID: {
generator: 'datatype.number',
arguments: [{ min: 1, max: 100}],
},
Boolean: 'false',
Int: {
generator: 'datatype.number',
arguments: [{ min: -100, max: 0}],
},
AnyObject: 'internet.email',
},
});

expect(result).toBeDefined();

// String
expect(result).toContain(
"str: overrides && overrides.hasOwnProperty('str') ? overrides.str! : faker['lorem']['sentence'](...[]),",
);

// Float
expect(result).toContain(
"flt: overrides && overrides.hasOwnProperty('flt') ? overrides.flt! : faker['datatype']['float'](...[{\"min\":-100,\"max\":0}]),",
);

// ID
expect(result).toContain("id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : faker['datatype']['number'](...[{\"min\":1,\"max\":100}]),");

// Boolean
expect(result).toContain("bool: overrides && overrides.hasOwnProperty('bool') ? overrides.bool! : false");

// Int
expect(result).toContain("int: overrides && overrides.hasOwnProperty('int') ? overrides.int! : faker['datatype']['number'](...[{\"min\":-100,\"max\":0}]),");

expect(result).toMatchSnapshot();
});

0 comments on commit 6d91d49

Please sign in to comment.