Skip to content

Commit de9d194

Browse files
committed
fix the mess - kind of
1 parent 47091a8 commit de9d194

File tree

2 files changed

+118
-63
lines changed

2 files changed

+118
-63
lines changed

src/index.ts

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,91 @@
11
import { IMiddleware } from 'graphql-middleware'
22
import { GraphQLUpload } from 'apollo-upload-server'
3-
import { GraphQLResolveInfo } from 'graphql'
3+
import { GraphQLResolveInfo, GraphQLArgument, GraphQLField } from 'graphql'
44

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)
737
}
838

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 {
1055
stream: string
1156
filename: string
1257
mimetype: string
1358
encoding: string
1459
}
1560

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>
2062

21-
return uploadArgs.map(arg => arg.name)
63+
interface IConfig<T> {
64+
uploadHandler: IUploadHandler<T>
2265
}
2366

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+
}
3872

39-
const normalizedUploads = uploads.reduce(
40-
(args, arg) => ({ ...args, ...arg }),
41-
{},
42-
)
73+
// function processor<T>
4374

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
4781
}
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)
4888

49-
return resolve(parent, argsWithUploads, ctx, info)
89+
return resolve()
5090
}
5191
}

test.js

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,72 @@
11
import test from 'ava'
2-
import { GraphQLUpload } from 'apollo-upload-server'
3-
import { graphql } from 'graphql'
2+
import { graphql, GraphQLString, GraphQLInteger } from 'graphql'
43
import { makeExecutableSchema } from 'graphql-tools'
54
import { applyMiddleware } from 'graphql-middleware'
6-
import { upload } from './'
5+
import { executeWithArgumentType } from './'
76

8-
test('Finds the right arguments', async t => {
9-
const typeDefs = `
10-
scalar Upload
7+
// Helpers
118

12-
type Query {
13-
foo: String
14-
}
9+
const isStringType = x => {
10+
return x.type instanceof GraphQLString
11+
}
12+
13+
// Tests
1514

16-
type Mutation {
17-
test(nonUpload: String!, upload: Upload!): Boolean
15+
test('Finds Argument With Type - Triggers', async t => {
16+
t.plan(2)
17+
18+
// Schema
19+
const typeDefs = `
20+
type Query {
21+
test(string: String, boolean: Boolean!): String!
1822
}
1923
`
2024

2125
const resolvers = {
2226
Query: {
23-
foo: () => '',
24-
},
25-
Mutation: {
26-
test: async (parent, args, ctx, info) => {
27-
t.is(args.upload, 'passes')
28-
t.is(args.nonUpload, 'passes')
29-
},
27+
test: (parent, args, ctx, infp) => 'pass',
3028
},
31-
Upload: GraphQLUpload,
3229
}
3330

34-
const uploadFunction = async file => {
35-
return 'passes'
31+
// Middleware
32+
const processor = arg => {
33+
if (isStringType(arg)) {
34+
t.pass()
35+
return 'pass'
36+
} else {
37+
return null
38+
}
3639
}
3740

38-
const _schema = makeExecutableSchema({
39-
typeDefs,
40-
resolvers,
41-
})
41+
const middleware = {
42+
Query: {
43+
test: async (resolve, parent, args, ctx, info) => {
44+
executeWithArgumentType(processor, info, args)
45+
46+
return resolve()
47+
},
48+
},
49+
}
4250

43-
const schema = applyMiddleware(
44-
_schema,
45-
upload({ uploadHandler: uploadFunction }),
46-
)
51+
// Schema with Middleware
52+
const schema = makeExecutableSchema({ typeDefs, resolvers })
53+
const schemaWithMiddleware = applyMiddleware(schema, middleware)
4754

4855
const query = `
49-
mutation {
50-
test(nonUpload: "passes", upload: "fails")
56+
query {
57+
test(string: "trigger", boolean: true)
5158
}
5259
`
5360

5461
const res = await graphql(schema, query)
5562

56-
console.log(res)
63+
t.is(res.data.test, 'pass')
5764
})
65+
66+
test.todo('Finds Argument With Type - Does not trigger')
67+
test.todo('Finds Argument With Type - values in processor match')
68+
test.todo('Finds Argument With Type - values in resolver match')
69+
test.todo('Is GraphQLUpload type')
70+
test.todo('Is not GraphQLUpload type')
71+
test.todo('Is GraphQLUpload List type')
72+
test.todo('Is not GraphQLUpload List type')

0 commit comments

Comments
 (0)