Skip to content

Commit

Permalink
feat: allow yup transformations (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
paolotiu authored Oct 23, 2021
1 parent 314be02 commit f991fe0
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 48 deletions.
4 changes: 2 additions & 2 deletions examples/hello-world/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ const Mutation = mutationType({
// the rules from the first argument together with args and context
// to figure out if the provided arguments are valid or not
validate: ({ string, number }, args, ctx) => ({
name: string(),
email: string().email(),
name: string().trim(),
email: string().email().trim(),
age: number().min(18),
website: string().url(),
// create a custom rule for secret that uses a custom test,
Expand Down
89 changes: 46 additions & 43 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,53 +20,56 @@ export type ValidateResolver<
ctx: GetGen<'context'>
) => MaybePromise<ObjectShape | void>;

export const resolver = (validateConfig: ValidatePluginConfig = {}) => (
config: CreateFieldResolverInfo
): MiddlewareFn | undefined => {
const { formatError = defaultFormatError } = validateConfig;
export const resolver =
(validateConfig: ValidatePluginConfig = {}) =>
(config: CreateFieldResolverInfo): MiddlewareFn | undefined => {
const { formatError = defaultFormatError } = validateConfig;

const validate: ValidateResolver<any, any> =
config.fieldConfig.extensions?.nexus?.config.validate;
const validate: ValidateResolver<any, any> =
config.fieldConfig.extensions?.nexus?.config.validate;

// if the field doesn't have an validate field,
// don't worry about wrapping the resolver
if (validate == null) {
return;
}
// if the field doesn't have an validate field,
// don't worry about wrapping the resolver
if (validate == null) {
return;
}

// if it does have this field, but it's not a function,
// it's wrong - let's provide a warning
if (typeof validate !== 'function') {
console.error(
'\x1b[33m%s\x1b[0m',
`The validate property provided to [${
config.fieldConfig.name
}] with type [${
config.fieldConfig.type
}]. Should be a function, saw [${typeof validate}]`
);
return;
}
// if it does have this field, but it's not a function,
// it's wrong - let's provide a warning
if (typeof validate !== 'function') {
console.error(
'\x1b[33m%s\x1b[0m',
`The validate property provided to [${
config.fieldConfig.name
}] with type [${
config.fieldConfig.type
}]. Should be a function, saw [${typeof validate}]`
);
return;
}

const args = config?.fieldConfig?.args ?? {};
if (Object.keys(args).length === 0) {
console.error(
'\x1b[33m%s\x1b[0m',
`[${config.parentTypeConfig.name}.${config.fieldConfig.name}] does not have any arguments, but a validate function was passed`
);
}
const args = config?.fieldConfig?.args ?? {};
if (Object.keys(args).length === 0) {
console.error(
'\x1b[33m%s\x1b[0m',
`[${config.parentTypeConfig.name}.${config.fieldConfig.name}] does not have any arguments, but a validate function was passed`
);
}

return async (root, args, ctx, info, next) => {
try {
const schemaBase = await validate(rules, args, ctx);
if (typeof schemaBase !== 'undefined') {
const schema = rules.object().shape(schemaBase);
await schema.validate(args);
return async (root, rawArgs, ctx, info, next) => {
try {
const schemaBase = await validate(rules, rawArgs, ctx);
// clone args so we can transform them when validating with yup
let args = { ...rawArgs };
if (typeof schemaBase !== 'undefined') {
const schema = rules.object().shape(schemaBase);
// update args to the transformed ones by yup
args = await schema.validate(args);
}
return next(root, args, ctx, info);
} catch (_error) {
const error = _error as Error | ValidationError;
throw formatError({ error, args, ctx });
}
return next(root, args, ctx, info);
} catch (_error) {
const error = _error as Error | ValidationError
throw formatError({ error, args, ctx });
}
};
};
};
70 changes: 67 additions & 3 deletions tests/validate-fn.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { graphql } from 'graphql';
import { makeSchema, objectType } from 'nexus';
import { intArg, mutationField, stringArg } from 'nexus/dist/core';
import { floatArg, intArg, mutationField, stringArg } from 'nexus/dist/core';
import { UserInputError, validatePlugin } from '../src/index';

describe('validatePlugin', () => {
Expand Down Expand Up @@ -48,13 +48,17 @@ describe('validatePlugin', () => {
plugins: [validatePlugin()],
});
const mockCtx = { user: { id: 1 } };
const testOperation = (mutation: string, schema = testSchema) => {
const testOperation = (
mutation: string,
schema = testSchema,
fields?: string
) => {
return graphql(
schema,
`
mutation {
${mutation} {
id
${fields ? fields : 'id'}
}
}
`,
Expand Down Expand Up @@ -154,4 +158,64 @@ describe('validatePlugin', () => {
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy.mock.calls[0]).toMatchSnapshot();
});

it('it transforms data if validation passed', async () => {
const schema = makeSchema({
outputs: false,
nonNullDefaults: {
output: true,
},
plugins: [validatePlugin()],
types: [
objectType({
name: 'ShouldTransform',
definition(t) {
t.int('round');
t.int('truncate');
t.string('trim');
t.string('lowercase');
},
}),
mutationField('shouldTransform', {
type: 'ShouldTransform',
args: {
round: floatArg(),
truncate: floatArg(),
trim: stringArg(),
lowercase: stringArg(),
},

// @ts-ignore
validate: ({ string, number }) => {
return {
round: number().round(),
truncate: number().truncate(),
trim: string().trim(),
lowercase: string().lowercase(),
};
},
resolve: (_, args) => args,
}),
],
});

const { data } = await testOperation(
`
shouldTransform(round:5.9, trim: " trim me", lowercase: "LOWERCASE", truncate: 5.888)
`,
schema,
`
round
trim
lowercase
truncate
`
);
expect(data?.shouldTransform).toEqual({
round: 6,
trim: 'trim me',
lowercase: 'lowercase',
truncate: 5,
});
});
});

0 comments on commit f991fe0

Please sign in to comment.