Skip to content

Commit

Permalink
fix(amplify-appsync-simulator): remove graphql-scalars dependency (#6680
Browse files Browse the repository at this point in the history
)

This commit removes the dependency on graphql-scalars and adds
the implementations for the AWSEmail and AWSURL scalars.

Fixes: #6656
  • Loading branch information
cjihrig committed Feb 26, 2021
1 parent 08da6b2 commit f12c994
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 7 deletions.
1 change: 0 additions & 1 deletion packages/amplify-appsync-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"express": "^4.17.1",
"graphql": "^14.5.8",
"graphql-iso-date": "^3.6.1",
"graphql-scalars": "^1.0.6",
"graphql-subscriptions": "^1.1.0",
"graphql-tools": "^4.0.6",
"graphql-type-json": "^0.3.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { scalars } from '../../schema/appsync-scalars';

describe('AWSEmail parse', () => {
it('Should reject a non-string', () => {
function parse() {
scalars.AWSEmail.parseValue(1);
}
expect(parse).toThrowErrorMatchingSnapshot();
});

it('Should reject an invalid email address', () => {
function parse() {
scalars.AWSEmail.parseValue('@@');
}
expect(parse).toThrowErrorMatchingSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { URL } from 'url';
import { scalars } from '../../schema/appsync-scalars';

describe('AWSURL parse', () => {
it('Returns falsy values unchanged', () => {
expect(scalars.AWSURL.parseValue(0)).toEqual(0);
});

it('Returns valid URL objects', () => {
const parsed = new URL('http://www.amazon.com');
expect(scalars.AWSURL.parseValue('http://www.amazon.com')).toEqual(parsed);
});

it('Should reject an invalid URL', () => {
function serialize() {
scalars.AWSURL.parseValue('invalid-url');
}
expect(serialize).toThrowErrorMatchingSnapshot();
});
});

describe('AWSURL serialize', () => {
it('Returns falsy values unchanged', () => {
expect(scalars.AWSURL.serialize(0)).toEqual(0);
});

it('Returns valid URLs', () => {
expect(scalars.AWSURL.serialize('http://www.amazon.com')).toEqual('http://www.amazon.com/');
});

it('Should reject an invalid URL', () => {
function serialize() {
scalars.AWSURL.serialize('invalid-url');
}
expect(serialize).toThrowErrorMatchingSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AWSEmail parse Should reject a non-string 1`] = `"Value is not string: 1"`;

exports[`AWSEmail parse Should reject an invalid email address 1`] = `"Value is not a valid email address: @@"`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AWSURL parse Should reject an invalid URL 1`] = `"Invalid URL: invalid-url"`;

exports[`AWSURL serialize Should reject an invalid URL 1`] = `"Invalid URL: invalid-url"`;
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { URL } from 'url';
import { GraphQLInt, GraphQLScalarType, GraphQLError, Kind, StringValueNode } from 'graphql';
import { isValidNumber } from 'libphonenumber-js';

import { GraphQLDate, GraphQLTime, GraphQLDateTime } from 'graphql-iso-date';

import { EmailAddressResolver, URLResolver } from 'graphql-scalars';

const EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
const IPV4_REGEX = /^(?:(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\/(?:[0-9]|[1-2][0-9]|3[0-2]))?)$/;
const IPV6_REGEX = /^(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)(?:\/(?:0?0?[0-9]|0?[1-9][0-9]|1[01][0-9]|12[0-8]))?)$/;

// Some of the custom scalars in this file are inspired by the graphql-scalars npm module.

const phoneValidator = (ast, options) => {
const { country = 'US' } = options;
const { kind, value } = ast;
Expand Down Expand Up @@ -139,7 +141,7 @@ const AWSIPAddress = new GraphQLScalarType({
});

const parseJson = (value: string) => {
if (typeof value !== "string") {
if (typeof value !== 'string') {
throw new GraphQLError(`Unable to parse ${value} as valid JSON.`);
}

Expand All @@ -148,7 +150,7 @@ const parseJson = (value: string) => {
} catch (error) {
throw new TypeError(`Unable to parse ${value} as valid JSON.`);
}
}
};

const AWSJSON = new GraphQLScalarType({
name: 'AWSJSON',
Expand All @@ -168,14 +170,63 @@ const AWSJSON = new GraphQLScalarType({
},
});

const validateEmail = value => {
if (typeof value !== 'string') {
throw new TypeError(`Value is not string: ${value}`);
}

if (!EMAIL_ADDRESS_REGEX.test(value)) {
throw new TypeError(`Value is not a valid email address: ${value}`);
}

return value;
};

const AWSEmail = new GraphQLScalarType({
name: 'AWSEmail',
description:
'A field whose value conforms to the standard internet email address format as specified in RFC822: https://www.w3.org/Protocols/rfc822/.',
serialize: validateEmail,
parseValue: validateEmail,
parseLiteral(ast) {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError(`Can only validate strings as email addresses but got a: ${ast.kind}`);
}

return validateEmail(ast.value);
},
});

const parseUrlValue = value => (value ? new URL(value.toString()) : value);

const AWSURL = new GraphQLScalarType({
name: 'AWSURL',
description: 'A field whose value conforms to the standard URL format as specified in RFC3986: https://www.ietf.org/rfc/rfc3986.txt.',
serialize(value) {
if (value) {
return new URL(value.toString()).toString();
}

return value;
},
parseValue: parseUrlValue,
parseLiteral(ast) {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError(`Can only validate strings as URLs but got a: ${ast.kind}`);
}

return parseUrlValue(ast.value);
},
});

export const scalars = {
AWSJSON,
AWSDate,
AWSTime,
AWSDateTime,
AWSPhone: new AWSPhone({ name: 'AWSPhone', description: 'AWSPhone' }),
AWSEmail: EmailAddressResolver,
AWSURL: URLResolver,
AWSEmail,
AWSURL,
AWSTimestamp,
AWSIPAddress,
};
Expand Down

0 comments on commit f12c994

Please sign in to comment.