|
1 | 1 | import { IMiddleware } from 'graphql-middleware' |
2 | 2 | import { GraphQLUpload } from 'apollo-upload-server' |
3 | | -import { GraphQLResolveInfo } from 'graphql' |
| 3 | +import { GraphQLResolveInfo, GraphQLArgument, GraphQLField } from 'graphql' |
4 | 4 |
|
5 | | -interface IConfig<output> { |
6 | | - uploadHandler: (file: IFile) => Promise<output> |
| 5 | +// GraphQL |
| 6 | + |
| 7 | +type Maybe<T> = T | null |
| 8 | + |
| 9 | +function getResolverField( |
| 10 | + info: GraphQLResolveInfo, |
| 11 | +): GraphQLField<any, any, { [key: string]: any }> { |
| 12 | + const { fieldName, parentType } = info |
| 13 | + const typeFields = parentType.getFields() |
| 14 | + |
| 15 | + return typeFields[fieldName] |
| 16 | +} |
| 17 | + |
| 18 | +function getFieldArguments<TSource, TContext, TArgs>( |
| 19 | + field: GraphQLField<TSource, TContext, TArgs>, |
| 20 | +): GraphQLArgument[] { |
| 21 | + return field.args |
| 22 | +} |
| 23 | + |
| 24 | +function filterMap<T, U>(f: (x: T) => Maybe<U>, xs: T[]): U[] { |
| 25 | + return xs.reduce((acc, x) => { |
| 26 | + const res = f(x) |
| 27 | + if (res !== null) { |
| 28 | + return [res, ...acc] |
| 29 | + } else { |
| 30 | + return acc |
| 31 | + } |
| 32 | + }, []) |
| 33 | +} |
| 34 | + |
| 35 | +function getArgumentValue(args: { [key: string]: any }, arg: GraphQLArgument) { |
| 36 | + return args.get(arg.name) |
7 | 37 | } |
8 | 38 |
|
9 | | -export interface IFile { |
| 39 | +export function executeWithArgumentType<T>( |
| 40 | + f: (definition: GraphQLArgument, arg: any) => Maybe<T>, |
| 41 | + info: GraphQLResolveInfo, |
| 42 | + args: { [key: string]: any }, |
| 43 | +): T[] { |
| 44 | + const field = getResolverField(info) |
| 45 | + const fieldArguments = getFieldArguments(field) |
| 46 | + |
| 47 | + const fWithArguments = arg => f(arg, getArgumentValue(args, arg)) |
| 48 | + |
| 49 | + return filterMap(fWithArguments, fieldArguments) |
| 50 | +} |
| 51 | + |
| 52 | +// Upload |
| 53 | + |
| 54 | +export interface IUpload { |
10 | 55 | stream: string |
11 | 56 | filename: string |
12 | 57 | mimetype: string |
13 | 58 | encoding: string |
14 | 59 | } |
15 | 60 |
|
16 | | -function getUploadArgumentsNames(info: GraphQLResolveInfo): string[] { |
17 | | - const typeFields = info.parentType.getFields() |
18 | | - const args = typeFields[info.fieldName].args |
19 | | - const uploadArgs = args.filter(arg => arg.type === GraphQLUpload) |
| 61 | +declare type IUploadHandler<T> = (upload: Promise<IUpload>) => Promise<T> |
20 | 62 |
|
21 | | - return uploadArgs.map(arg => arg.name) |
| 63 | +interface IConfig<T> { |
| 64 | + uploadHandler: IUploadHandler<T> |
22 | 65 | } |
23 | 66 |
|
24 | | -export const upload = <output>(config: IConfig<output>): IMiddleware => { |
25 | | - return async (resolve, parent, args, ctx, info) => { |
26 | | - const uploadArgumentsNames = getUploadArgumentsNames(info) |
27 | | - const nonEmptyUploadArgumentsNames = uploadArgumentsNames.filter( |
28 | | - argName => args[argName] !== undefined, |
29 | | - ) |
30 | | - |
31 | | - const uploads = await Promise.all( |
32 | | - nonEmptyUploadArgumentsNames.map(uploadArgumentName => |
33 | | - args[uploadArgumentName] |
34 | | - .then(config.uploadHandler) |
35 | | - .then(res => ({ [uploadArgumentName]: res })), |
36 | | - ), |
37 | | - ) |
| 67 | +function isGraphQLUploadArgument( |
| 68 | + x: GraphQLArgument, |
| 69 | +): x is GraphQLArgument & { type: GraphQLUpload } { |
| 70 | + return x.type instanceof GraphQLUpload |
| 71 | +} |
38 | 72 |
|
39 | | - const normalizedUploads = uploads.reduce( |
40 | | - (args, arg) => ({ ...args, ...arg }), |
41 | | - {}, |
42 | | - ) |
| 73 | +// function processor<T> |
43 | 74 |
|
44 | | - const argsWithUploads = { |
45 | | - ...args, |
46 | | - ...normalizedUploads, |
| 75 | +async function processor<T>(uploadFunction: IUploadHandler<T>) { |
| 76 | + return async (def, value) => { |
| 77 | + if (isGraphQLUploadArgument(def)) { |
| 78 | + return (value as Promise<IUpload>).then(uploadFunction) |
| 79 | + } else { |
| 80 | + return null |
47 | 81 | } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +export function upload<T>({ uploadHandler }: IConfig<T>): IMiddleware { |
| 86 | + return (resolve, parent, args, ctx, info) => { |
| 87 | + const executed = executeWithArgumentType(uploadHandler, info) |
48 | 88 |
|
49 | | - return resolve(parent, argsWithUploads, ctx, info) |
| 89 | + return resolve() |
50 | 90 | } |
51 | 91 | } |
0 commit comments