From 7ce4e4a7bc602fe1869824b29b763108f5cff84f Mon Sep 17 00:00:00 2001 From: Kristof Van Miegem Date: Sat, 9 Feb 2019 16:02:08 +0100 Subject: [PATCH] feat: defining a custom scalar DateTime --- src/resolvers.js | 4 ++- src/scalars/dateTime.js | 65 +++++++++++++++++++++++++++++++++++++++++ src/typeDefs.js | 12 ++++---- 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 src/scalars/dateTime.js diff --git a/src/resolvers.js b/src/resolvers.js index e302e99..e4e0e3f 100644 --- a/src/resolvers.js +++ b/src/resolvers.js @@ -1,5 +1,6 @@ const { UserInputError } = require('apollo-server'); const yup = require('yup'); +const dateTimeScalar = require('./scalars/dateTime'); const data = require('./data'); const uuidSchema = yup.string().min(36).max(36); @@ -76,5 +77,6 @@ module.exports = { const { reservationProducts } = input; return data.createReservation({ reservationProducts }); } - } + }, + DateTime: dateTimeScalar, }; \ No newline at end of file diff --git a/src/scalars/dateTime.js b/src/scalars/dateTime.js new file mode 100644 index 0000000..889246d --- /dev/null +++ b/src/scalars/dateTime.js @@ -0,0 +1,65 @@ +const { GraphQLScalarType } = require('graphql'); +const { GraphQLError } = require('graphql/error') +const { Kind } = require('graphql/language'); + +module.exports = new GraphQLScalarType({ + name: 'DateTime', + description: 'Use JavaScript Date object for date/time fields.', + serialize(value) { + let v = value; + + if (!(v instanceof Date) && typeof v !== 'string' && typeof v !== 'number') { + throw new TypeError( + `Value is not an instance of Date, Date string or number: ${v}`, + ); + } + + if (typeof v === 'string') { + v = new Date(); + + v.setTime(Date.parse(value)); + } else if (typeof v === 'number') { + v = new Date(v); + } + + if (Number.isNaN(v.getTime())) { + throw new TypeError(`Value is not a valid Date: ${v}`); + } + + return v.toJSON(); + }, + + parseValue(value) { + const date = new Date(value); + + if (Number.isNaN(date.getTime())) { + throw new TypeError(`Value is not a valid Date: ${value}`); + } + + return date; + }, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING && ast.kind !== Kind.INT) { + throw new GraphQLError( + `Can only parse strings & integers to dates but got a: ${ast.kind}`, + ); + } + + const result = new Date(ast.kind === Kind.INT ? Number(ast.value) : ast.value); + + if (Number.isNaN(result.getTime())) { + throw new GraphQLError(`Value is not a valid Date: ${ast.value}`); + } + + if (ast.kind === Kind.STRING && ast.value !== result.toJSON()) { + throw new GraphQLError( + `Value is not a valid Date format (YYYY-MM-DDTHH:MM:SS.SSSZ): ${ + ast.value + }`, + ); + } + + return result; + }, +}); \ No newline at end of file diff --git a/src/typeDefs.js b/src/typeDefs.js index bcaba06..a6d424b 100644 --- a/src/typeDefs.js +++ b/src/typeDefs.js @@ -3,11 +3,13 @@ const { gql } = require('apollo-server'); // Type definitions define the "shape" of your data and specify // which ways the data can be fetched from the GraphQL server. module.exports = gql` + scalar DateTime + # Comments in GraphQL are defined with the hash (#) symbol. type Product { description: String - id: String + id: ID name: String price: Float } @@ -15,7 +17,7 @@ module.exports = gql` # This "Store" type can be used in other type declarations. type Store { city: String - id: String + id: ID name: String number: Int postalCode: String @@ -29,8 +31,8 @@ module.exports = gql` } type Reservation { - date: String - id: String + date: DateTime + id: ID reservationProducts: [ReservationProduct] } @@ -38,7 +40,7 @@ module.exports = gql` # (A "Mutation" type will be covered later on.) type Query { stores: [Store] - store(id: String): Store + store(id: ID): Store } input StoreInput {