Skip to content

Commit e54e45e

Browse files
eokoneyoAlan-Cha
authored andcommitted
add implementation to support streaming uploaded file from GraphQL over to REST endpoint
Signed-off-by: Eyo O. Eyo <[email protected]> Signed-off-by: Eyo Okon Eyo <[email protected]>
1 parent e01e6e8 commit e54e45e

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

packages/openapi-to-graphql/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"isomorphic-git": "^1.9.2",
123123
"jest": "^27.0.6",
124124
"js-yaml": "^4.1.0",
125+
"memfs": "^3.4.0",
125126
"mqemitter": "^4.4.0",
126127
"mqtt": "^4.2.1",
127128
"nodemon": "^2.0.12",

packages/openapi-to-graphql/src/resolver_builder.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import crossFetch from 'cross-fetch'
1818
import { FileUpload } from 'graphql-upload'
1919

2020
// Imports:
21+
import stream from 'stream'
2122
import * as Oas3Tools from './oas_3_tools'
2223
import { JSONPath } from 'jsonpath-plus'
2324
import { debug } from 'debug'
@@ -32,6 +33,7 @@ const pubsub = new PubSub()
3233
const translationLog = debug('translation')
3334
const httpLog = debug('http')
3435
const pubsubLog = debug('pubsub')
36+
const uploadLog = debug('fileUpload')
3537

3638
// OAS runtime expression reference locations
3739
const RUNTIME_REFERENCES = ['header.', 'query.', 'path.', 'body']
@@ -325,7 +327,7 @@ export function getPublishResolver<TSource, TContext, TArgs>({
325327
* If the operation type is Query or Mutation, create and return a resolver
326328
* function that performs API requests for the given GraphQL query
327329
*/
328-
export function getResolver<TSource, TContext, TArgs>({
330+
export function getResolver<TSource, TContext, TArgs> ({
329331
operation,
330332
argsFromLink = {},
331333
payloadName,
@@ -578,13 +580,40 @@ export function getResolver<TSource, TContext, TArgs>({
578580
} else if (operation.payloadContentType === 'multipart/form-data') {
579581
form = new FormData()
580582

581-
Object.entries(args[sanePayloadName]).forEach(([key, value]) => {
582-
// if (typeof value === 'object' && (value as Partial<FileUpload>).createReadStream) {
583-
// form.append(key, (value as FileUpload).createReadStream())
584-
// }
583+
const formFieldsPayloadEntries = Object.entries(args[sanePayloadName]);
585584

586-
form.append(key, value)
587-
})
585+
(await Promise.all(formFieldsPayloadEntries.map(([_, v]) => v)))
586+
.forEach((fieldValue, idx) => {
587+
const fieldName = formFieldsPayloadEntries[idx][0]
588+
589+
if (typeof fieldValue === 'object' && Boolean((fieldValue as Partial<FileUpload>).createReadStream)) {
590+
const uploadingFile = fieldValue as FileUpload
591+
const originalFileStream = uploadingFile.createReadStream()
592+
const filePassThrough = new stream.PassThrough()
593+
594+
originalFileStream.on('readable', function () {
595+
let data
596+
// tslint:disable-next-line:no-conditional-assignment
597+
while (data = this.read()) {
598+
filePassThrough.write(data)
599+
}
600+
})
601+
602+
originalFileStream.on('end', () => {
603+
uploadLog(`Upload for received file ${uploadingFile.filename} completed`)
604+
filePassThrough.end()
605+
})
606+
607+
uploadLog(`Queuing upload for received file ${uploadingFile.filename}`)
608+
609+
form.append(fieldName, filePassThrough, {
610+
filename: uploadingFile.filename,
611+
contentType: uploadingFile.mimetype
612+
})
613+
} else {
614+
form.append(fieldName, fieldValue)
615+
}
616+
})
588617

589618
rawPayload = form
590619
} else {
@@ -672,6 +701,11 @@ export function getResolver<TSource, TContext, TArgs>({
672701

673702
let response: Response
674703
try {
704+
// if is form, remove default content type and
705+
// let fetch compute appropriate header content-type based off of form data
706+
if (form) {
707+
delete options.headers['content-type']
708+
}
675709
response = await fetch(url.toString(), options)
676710
} catch (err) {
677711
httpLog(err)

packages/openapi-to-graphql/yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,6 +2525,11 @@ fs-capacitor@^6.2.0:
25252525
resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5"
25262526
integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==
25272527

2528+
2529+
version "1.0.3"
2530+
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
2531+
integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
2532+
25282533
fs.realpath@^1.0.0:
25292534
version "1.0.0"
25302535
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -4013,6 +4018,13 @@ [email protected]:
40134018
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
40144019
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
40154020

4021+
memfs@^3.4.0:
4022+
version "3.4.0"
4023+
resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.0.tgz#8bc12062b973be6b295d4340595736a656f0a257"
4024+
integrity sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA==
4025+
dependencies:
4026+
fs-monkey "1.0.3"
4027+
40164028
40174029
version "1.0.1"
40184030
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"

0 commit comments

Comments
 (0)