Last active
February 14, 2020 17:55
-
-
Save singingwolfboy/90651ce26fb4cd1646550aa2713dd2ef to your computer and use it in GitHub Desktop.
A plugin for Postgraphile that provides support for the "phone_number" type from pg_libphonenumber: https://github.com/blm768/pg-libphonenumber Note that this support requires installing libphonenumber-js on the Postgraphile server: https://github.com/catamphetamine/libphonenumber-js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Plugin, Build, ScopeGraphQLScalarType } from "graphile-build"; | |
import { PhoneNumber, parsePhoneNumber } from "libphonenumber-js"; | |
declare module "graphile-build" { | |
interface ScopeGraphQLScalarType { | |
isPhoneNumberScalar: boolean; | |
} | |
} | |
export default (function PgTypesLibphonenumberPlugin(builder) { | |
builder.hook( | |
"build", | |
build => { | |
// This hook tells graphile-build-pg about the libphonenumber database type so it | |
// knows how to express it in input/output. | |
const { | |
pgIntrospectionResultsByKind: rawIntrospectionResultsByKind, | |
pgRegisterGqlTypeByTypeId, | |
pgRegisterGqlInputTypeByTypeId, | |
pg2GqlMapper, | |
pgSql: sql, | |
} = build; | |
if ( | |
!rawIntrospectionResultsByKind || | |
!sql || | |
!pgRegisterGqlTypeByTypeId || | |
!pgRegisterGqlInputTypeByTypeId || | |
!pg2GqlMapper | |
) { | |
throw new Error("Required helpers were not found on Build."); | |
} | |
const introspectionResultsByKind = rawIntrospectionResultsByKind; | |
// Check we have the libphonenumber extension | |
const libphonenumberExtension = introspectionResultsByKind.extension.find( | |
e => e.name === "pg_libphonenumber" | |
); | |
if (!libphonenumberExtension) { | |
return build; | |
} | |
// Get the 'phone_number' type itself: | |
const phoneNumberType = introspectionResultsByKind.type.find( | |
t => | |
t.name === "phone_number" && | |
t.namespaceId === libphonenumberExtension.namespaceId | |
); | |
if (!phoneNumberType) { | |
return build; | |
} | |
const phoneNumberTypeName = build.inflection.builtin("PhoneNumber"); | |
const GraphQLPhoneNumberType = makeGraphQLPhoneNumberType( | |
build as Build, | |
phoneNumberTypeName | |
); | |
// Now register the phone_number type with the type system for both output and input. | |
pgRegisterGqlTypeByTypeId( | |
phoneNumberType.id, | |
() => GraphQLPhoneNumberType | |
); | |
pgRegisterGqlInputTypeByTypeId( | |
phoneNumberType.id, | |
() => GraphQLPhoneNumberType | |
); | |
// Finally we must tell the system how to translate the data between PG-land and JS-land: | |
pg2GqlMapper[phoneNumberType.id] = { | |
// Turn string (from node-postgres) into PhoneNumber object | |
map: parsePhoneNumber, | |
// When unmapping we need to convert back to phone_number | |
unmap: (pn: PhoneNumber) => | |
sql.fragment`(${sql.value(pn.number)}::${sql.identifier( | |
phoneNumberType.namespaceName, | |
phoneNumberType.name | |
)})`, | |
}; | |
return build; | |
}, | |
["PgTypesLibphonenumber"], | |
[], | |
["PgTypes"] | |
); | |
/* End of libphonenumber type */ | |
} as Plugin); | |
function makeGraphQLPhoneNumberType(build: Build, phoneNumberTypeName: string) { | |
const { | |
graphql: { GraphQLScalarType, Kind }, | |
} = build; | |
function parseValue(obj: unknown): PhoneNumber { | |
if (!(typeof obj === "string")) { | |
throw new TypeError( | |
`This is not a valid ${phoneNumberTypeName} object, it must be a string.` | |
); | |
} | |
return parsePhoneNumber(obj); | |
} | |
const parseLiteral: import("graphql").GraphQLScalarLiteralParser<any> = ( | |
ast, | |
variables | |
) => { | |
switch (ast.kind) { | |
case Kind.STRING: { | |
return parsePhoneNumber(ast.value); | |
} | |
case Kind.NULL: | |
return null; | |
case Kind.VARIABLE: { | |
const name = ast.name.value; | |
const value = variables ? variables[name] : undefined; | |
return parsePhoneNumber(value); | |
} | |
default: | |
return undefined; | |
} | |
}; | |
const scope: ScopeGraphQLScalarType = { isPhoneNumberScalar: true }; | |
const GraphQLPhoneNumber = build.newWithHooks( | |
GraphQLScalarType, | |
{ | |
name: phoneNumberTypeName, | |
description: "An international phone number.", | |
serialize: (pn: PhoneNumber) => pn.number, | |
parseValue, | |
parseLiteral, | |
}, | |
scope | |
); | |
return GraphQLPhoneNumber; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment