Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Handle custom generators for faker #101

Merged
merged 12 commits into from
Nov 17, 2022
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
57 changes: 56 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,60 @@ const getCustomScalarValue = (customScalar: ScalarDefinition, opts: Options<Name
return value;
};

const getFakerGenerators = (generatorName: ScalarGeneratorName): [Function, string] => {
let embeddedGenerator: unknown = faker;
let dynamicGenerator = 'faker';

if (typeof generatorName === 'string') {
const generatorPath = generatorName.split('.');
for (const key of generatorPath) {
if (embeddedGenerator.hasOwnProperty(key)) {
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 [null, 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);
}

return getFakerCustomScalarValue(customScalar, opts);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably check if the value is faker and throw an error if the value is unknown

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had one, but typescript told me the value couldn't be unknown. I'll add it back!

};

const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean => {
if (!opts.currentType) {
return '';
Expand Down
23 changes: 22 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,24 @@ 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,
};
};
"
`;
48 changes: 47 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,49 @@ it('should generate custom scalars for native and custom types', async () => {

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();
});
ardeois marked this conversation as resolved.
Show resolved Hide resolved