-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(lambda) File uploads #3926
Conversation
@mathix420: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Apollo Contributor License Agreement here: https://contribute.apollographql.com/ |
Would love to see this merged! Thanks for your work @mathix420 |
@abernix or @trevor-scheer is it normal that circleci is not passing on my commits ? |
Thanks @mathix420! would love to see one of the three PRs that were created to address this issue get merged in |
Thanks so much @mathix420! Hope this gets approved soon! |
🙏🏼amazing thank you for this! Hopefully gets merged soon! |
This was effectively `undefined` as it's not exported from the `apollo-server-integration-testsuite` package. Node.js 8 wasn't LTS until much later anyhow, so no need to even try to support this as we never officially support non-LTS versions of Node.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for putting this together! This looks great, and I really appreciate you taking the time to reconcile the concerns from other PRs! I've added a few follow-up commits, but let's get it into an alpha
release for testing. I've targeted this PR to the release-2.13.0
branch which will shortly become a release PR.
I realize that some of the progress on the previous PRs seemed uncertain, but there is absolutely harmony to be found in not having diverging patterns between the many integrations. Providing a PR to add functionality is always welcome, but it's often on the maintainers of the project to provide support for it into the future, so I hope there's some understanding as to why the asks were being made. Assuming this works out well in alpha
testing, you'll have shown that it is possible to achieve that harmony by the patterns you've adopted within.
I've included co-authorship to the other PR authors in the merge for this, to give contribution credit in the project history. Thank you to all that were involved!
Sorry @lassesteffen I think I jumped the gun, that didn't fix the issue. I was able to get past the |
How can I set this header in apollo client or apollo-server-lambda? |
Which client are you using ? |
@mathix420 ApolloClient 2.6.8
|
directly in your handler (before
|
@jordanskole I'm not sure if the your client can be the problem but one simple way to know that is by using Altair client. |
Thanks @mathix420, I tried that and I ran into the same error with Altair 😞so far I have been using serverless-offline. I will try and deploy and see if that fixes the issue, unless there are other ideas. |
Have you tried changing the headers that you have both lowercase and uppercase content-type? |
I'm not sure I am following where I can modify them, if not in context? My handler.ts file doesn't have access to event/context? |
Can you share me your handler.ts? |
Yeah, it's actually up above handler.ts
graphql/context:
|
@jordanskole try this 👋 (the
|
That was it! 🙏🏼feel free to share your paypal email with me I'd love to buy you a beer! |
all good, cheers! |
Hey @mathix420 , I'm back! I have my application running fine locally, but I am having trouble actually deploying to AWS, and I think it might be related to this, since the error is getting tossed from the I'm currently running alpha.1 and I haven't had any issues running everything locally. This is of course barking about preflight CORS when I get to staging, but I think because preflight is failing since all of the below errors are happing in playground and CORS shouldn't be an issue there -- but perhaps I am wrong? Should this be a separate issue? Hitting playground throws the following error (on POST request):
Here is the complete event:
and that body base64decoded (just an introspection)
|
Hi @jordanskole, hope you're fine Not sure but it looks like your issue is identical as this one #2599 |
Hey @mathix420 Thanks for your help! That did fix the first issue I had, but now I am still struggling. All my other queries and mutations work in my staging deployment after base64 decoding the request, except for my single fileUpload mutation resolver, which attempts to stream data to cloudinary. Cloudinary is throwing the error which I am investigating, but it appears that my resolver is not building the stream correctly, or is closing the stream, since I see multiple (3+) invocations of my resolver, which makes me think AWS Gateway/Lambda is not handling the multipart request correctly (???) I think the error may have something to do with the lower-case
~ AWS API Gateway known issues Both Altair and |
Did you skip the decoding when a |
@mathix420 amazing! you are my hero again. You are definitely getting a shoutout on this app when it launches. For anybody that finds their way back here, this is what my complete handler looks like now:
|
Thank you for the wrapper, but with aws api gateway v2 and payload format v1 gives error: {
"errorType": "Runtime.UnhandledPromiseRejection",
"errorMessage": "TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object",
"reason": [
{
"errorType": "TypeError",
"errorMessage": "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object",
"message": "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object",
"extensions": {
"code": "INTERNAL_SERVER_ERROR"
}
}
],
"promise": {},
"stack": [
"Runtime.UnhandledPromiseRejection: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object",
" at process.<anonymous> (/var/runtime/index.js:35:15)",
" at process.emit (events.js:315:20)",
" at processPromiseRejections (internal/process/promises.js:209:33)",
" at processTicksAndRejections (internal/process/task_queues.js:98:32)"
]
} what could be the reason for that? |
I have the same error. |
Same error here as well, plus:
|
@lanwen could you please give us more context ? |
@mathix420 Its a apollo-server-lambda, with one mutation calling apollo-upload We use aws-cdk (1.55) to deploy the lambda packed with webpack (nothing special there, just handler as in examples above ^^^ as input and output, using ts) const fn = new lambda.Function(this, id, {
runtime: lambda.Runtime.NODEJS_12_X,
handler: "lambda.handler",
memorySize: 256,
code: lambda.Code.fromAsset(lambdaAsset),
});
const api = new gw.HttpApi(this, "graphql-api", {
createDefaultStage: false,
});
api.addRoutes({
path: "/graphql",
methods: [gw.HttpMethod.POST, gw.HttpMethod.GET, gw.HttpMethod.OPTIONS],
integration: new gw.LambdaProxyIntegration({
handler: fn,
// https://github.com/apollographql/apollo-server/issues/3958
payloadFormatVersion: gw.PayloadFormatVersion.VERSION_1_0,
}),
});
const stage = new gw.HttpStage(this, "DeploymentStage", {
httpApi: api,
stageName: environment,
autoDeploy: true,
}); Here is an event which comes into handler {
version: '1.0',
resource: '/graphql',
path: '/staging/graphql',
httpMethod: 'POST',
headers: {
'Content-Length': '5288',
'Content-Type': 'multipart/form-data; boundary=---------------------------7180963393185668272703501002',
Host: 'some-api-id.execute-api.region.amazonaws.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0',
'X-Amzn-Trace-Id': 'Root=1-5f282a99-5ad69cbafb4710',
'X-Forwarded-For': '1.2.3.4',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
accept: '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-GB,en;q=0.5',
authorization: 'eyJh...HgfdA',
origin: 'https://something.cloudfront.net',
referer: 'https://something.cloudfront.net/something/'
},
multiValueHeaders: {
'Content-Length': [ '5288' ],
'Content-Type': [
'multipart/form-data; boundary=---------------------------7180963393185668272703501002'
],
Host: [ 'some-api-id.execute-api.region.amazonaws.com' ],
'User-Agent': [
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0'
],
'X-Amzn-Trace-Id': [ 'Root=1-5f282a4710' ],
'X-Forwarded-For': [ '1.2.3.4' ],
'X-Forwarded-Port': [ '443' ],
'X-Forwarded-Proto': [ 'https' ],
accept: [ '*/*' ],
'accept-encoding': [ 'gzip, deflate, br' ],
'accept-language': [ 'en-GB,en;q=0.5' ],
authorization: [
'eyJhbGciOiJ...HgfdA'
],
origin: [ 'https://something.cloudfront.net' ],
referer: [ 'https://something.cloudfront.net/something/' ]
},
queryStringParameters: null,
multiValueQueryStringParameters: null,
requestContext: {
accountId: '1',
apiId: 'some-api-id',
domainName: 'some-api-id.execute-api.region.amazonaws.com',
domainPrefix: 'some-api-id',
extendedRequestId: 'QsBQ=',
httpMethod: 'POST',
identity: {
accessKey: null,
accountId: null,
caller: null,
cognitoAuthenticationProvider: null,
cognitoAuthenticationType: null,
cognitoIdentityId: null,
cognitoIdentityPoolId: null,
principalOrgId: null,
sourceIp: '1.2.3.4',
user: null,
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0',
userArn: null
},
path: '/staging/graphql',
protocol: 'HTTP/1.1',
requestId: 'QQ=',
requestTime: '03/Aug/2020:15:17:45 +0000',
requestTimeEpoch: 1596467865307,
resourceId: 'POST /graphql',
resourcePath: '/graphql',
stage: 'staging'
},
pathParameters: null,
stageVariables: null,
body: 'LS0tLS0tL...(some base64 png)...aA2RgwG...zM5MzE4NTY2ODI3MjcwMzUwMTAwMi0tDQo=',
isBase64Encoded: true
} dependencies: "dependencies": {
"@types/form-data": "^2.5.0",
"apollo-datasource-rest": "^0.9.3",
"apollo-server": "^2.16.0",
"apollo-server-lambda": "^2.16.0",
"commander": "^6.0.0",
"form-data": "^3.0.0",
"graphql": "^14.0.0",
"graphql-iso-date": "^3.6.1"
}, request doesn't come to our resolver and got failed somewhere before. Wasn't able to reproduce with serverless offline locally, didn't try if dependent on the deployment type (like using old rest-api) |
Seems like this always happens, unless I replace the |
This setup worked for me (using export function handler(event, context, callback) {
if (Object.keys(event.headers).includes('Content-Type')) {
event.headers['content-type'] = event.headers['Content-Type'];
}
if (event.isBase64Encoded && event.body && event.headers['content-type'].includes('multipart/form-data')) {
event.body = Buffer.from(event.body, 'base64').toString('binary');
event.isBase64Encoded = false;
}
return server.createHandler({
cors: {
origin: true,
credentials: true,
},
})(event, context, callback);
} |
Fix #1419, fix #1703 file upload using lambda.
Based on #3676 and #1739
TODO:
After reading both #3676 and #1739 and seeing no further advancements, I convinced myself to write this PR. I tried to consider all the comments from old PRs and merge best parts of both ideas into this one.
Hoping it can help this feature to move forward 💯
Big thanks to @charleswong28, @k00k and @smschick for having being built all the logic !
Don't hesitate to tell me if there is anything to change.
Closes #3676
Closes #1739