diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 318e8645..93896218 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,10 @@ # Contributing to OpenAPI-to-GraphQL -This document lists the differences between the contribution guidelines for this repository and the general [**Contributing to LoopBack**](http://loopback.io/doc/en/contrib/index.html) guidelines. +This document lists the differences between the contribution guidelines for this repository. ### Developer Certificate of Origin -This repository uses a [Developer Certificate of Origin (DCO)](https://developercertificate.org/) instead of a [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/loopback.io) like most other LoopBack repositories. DCO is an easier process to adhere to. [Full text of DCO](https://developercertificate.org/) can be found below, formatted for readability. +This repository uses a [Developer Certificate of Origin (DCO)](https://developercertificate.org/). DCO is an easier process to adhere to. [Full text of DCO](https://developercertificate.org/) can be found below, formatted for readability. > By making a contribution to this project, I certify that: > diff --git a/packages/openapi-to-graphql/README.md b/packages/openapi-to-graphql/README.md index 4fe873fc..75bc961c 100644 --- a/packages/openapi-to-graphql/README.md +++ b/packages/openapi-to-graphql/README.md @@ -131,6 +131,10 @@ For example, let's say that there was a library API that would allow you to get Notice that the slashes in the path `/favoriteBooks/{name}` must be escaped with `~1` and that you can compose parameter values with different [runtime expressions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#runtimeExpression) using brackets. +### File Uploads + +Support for file uploads through GraphQL is implemented based on the spec defined [here](https://github.com/jaydenseric/graphql-multipart-request-spec), through the module [`graphql-upload`](https://github.com/jaydenseric/graphql-upload). To opt in for file uploads, a GraphQL client that provides support for the spec must be used. Additionally, on the GraphQL server, the util method `processRequest` provided by the module `graphql-upload` must be integrated to format the received request body to match the expectations for the spec. + ### Options The `createGraphQLSchema` function takes an optional `options` object as a second argument: @@ -183,6 +187,8 @@ Resolver options: - `customSubscriptionResolvers` (type: `object`, default: `{}`): If the `createSubscriptionsFromCallbacks` is enabled, OpenAPI-to-GraphQL will generate Subscription fields. This option allows users to provide custom resolver and subscribe functions to be used in place of said ones created by OpenAPI-to-GraphQL. The field that the custom resolver and subscribe functions will affect is identifed first by the [title](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#infoObject) of the OAS, then the [path](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#paths-object) of the operation, and lastly the [method](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#path-item-object) of the operation. The resolver is provided via the `resolver` field and the publish function is provided via the `publish` field. The `customSubscriptionResolvers` object is thus a quadruply nested object where the outer key is the title, followed by the path, then the method, and lastly either `resolver` or `publish` which points to the [resolver function](https://graphql.org/learn/execution/#root-fields-resolvers) itself or publish function. See the [Subscriptions tutorial](./docs/subscriptions.md) for more information. _Note: Because the arguments are provided by the GraphQL interface, they may look different from the [parameters](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject) defined by the OAS. For example, they will have [sanitized](https://github.com/IBM/openapi-to-graphql#characteristics) names. The [request body](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#requestBodyObject) will also be contained in the arguments as an [input object type](https://graphql.org/graphql-js/mutations-and-input-types/)._ +- `fileUploadOptions` (type: `object`, default: `{}`): This options allows users to provide custom options for the form data object that will be used to process requests to endpoints that require a multipart request body, provided through the [`form-data` module](https://github.com/form-data/form-data). See [file uploads](#file-uploads). + *** Authentication options: diff --git a/packages/openapi-to-graphql/package.json b/packages/openapi-to-graphql/package.json index 28dd5d5d..16d15b84 100644 --- a/packages/openapi-to-graphql/package.json +++ b/packages/openapi-to-graphql/package.json @@ -84,9 +84,11 @@ "cross-fetch": "^3.1.4", "debug": "^4.2.0", "deep-equal": "^2.0.5", + "form-data": "^4.0.0", "form-urlencoded": "^6.0.4", "graphql-scalars": "^1.10.0", "graphql-subscriptions": "^1.1.0", + "graphql-upload": "^13.0.0", "json-ptr": "^2.2.0", "jsonpath-plus": "^6.0.1", "oas-validator": "^5.0.2", @@ -102,6 +104,7 @@ "devDependencies": { "@types/deep-equal": "^1.0.1", "@types/graphql": "^14.0.3", + "@types/graphql-upload": "^8.0.7", "@types/jest": "^26.0.14", "@types/node": "^16.3.3", "@types/url-join": "^4.0.1", @@ -119,6 +122,7 @@ "isomorphic-git": "^1.9.2", "jest": "^27.0.6", "js-yaml": "^4.1.0", + "memfs": "^3.4.0", "mqemitter": "^4.4.0", "mqtt": "^4.2.1", "nodemon": "^2.0.12", diff --git a/packages/openapi-to-graphql/src/index.ts b/packages/openapi-to-graphql/src/index.ts index 2f2f4340..05a5c01e 100644 --- a/packages/openapi-to-graphql/src/index.ts +++ b/packages/openapi-to-graphql/src/index.ts @@ -36,7 +36,8 @@ import { InternalOptions, Report, ConnectOptions, - RequestOptions + RequestOptions, + FileUploadOptions } from './types/options' import { Oas3 } from './types/oas3' import { Oas2 } from './types/oas2' @@ -112,6 +113,7 @@ const DEFAULT_OPTIONS: InternalOptions = { requestOptions: {}, customResolvers: {}, customSubscriptionResolvers: {}, + fileUploadOptions: {}, // Authentication options viewer: true, @@ -193,6 +195,7 @@ export function translateOpenAPIToGraphQL( headers, qs, requestOptions, + fileUploadOptions, connectOptions, baseUrl, customResolvers, @@ -234,6 +237,7 @@ export function translateOpenAPIToGraphQL( headers, qs, requestOptions, + fileUploadOptions, connectOptions, baseUrl, customResolvers, @@ -477,6 +481,7 @@ function addQueryFields({ singularNames, baseUrl, requestOptions, + fileUploadOptions, connectOptions, fetch } = options @@ -486,6 +491,7 @@ function addQueryFields({ baseUrl, data, requestOptions, + fileUploadOptions, connectOptions, fetch ) @@ -654,6 +660,7 @@ function addMutationFields({ singularNames, baseUrl, requestOptions, + fileUploadOptions, connectOptions, fetch } = options @@ -663,6 +670,7 @@ function addMutationFields({ baseUrl, data, requestOptions, + fileUploadOptions, connectOptions, fetch ) @@ -802,13 +810,14 @@ function addSubscriptionFields({ options: InternalOptions data: PreprocessingData }) { - const { baseUrl, requestOptions, connectOptions, fetch } = options + const { baseUrl, requestOptions, connectOptions, fetch, fileUploadOptions } = options const field = getFieldForOperation( operation, baseUrl, data, requestOptions, + fileUploadOptions, connectOptions, fetch ) @@ -912,6 +921,7 @@ function getFieldForOperation( baseUrl: string, data: PreprocessingData, requestOptions: Partial>, + fileUploadOptions: FileUploadOptions, connectOptions: ConnectOptions, fetch: typeof crossFetch ): GraphQLFieldConfig { @@ -978,6 +988,7 @@ function getFieldForOperation( data, baseUrl, requestOptions, + fileUploadOptions, fetch }) diff --git a/packages/openapi-to-graphql/src/oas_3_tools.ts b/packages/openapi-to-graphql/src/oas_3_tools.ts index 9771718d..d7af5f14 100644 --- a/packages/openapi-to-graphql/src/oas_3_tools.ts +++ b/packages/openapi-to-graphql/src/oas_3_tools.ts @@ -473,7 +473,9 @@ export function getSchemaTargetGraphQLType( if (typeof schema.format === 'string') { if (schema.type === 'integer' && schema.format === 'int64') { return TargetGraphQLType.bigint - + // CASE: file upload + } else if (schema.type === 'string' && schema.format === 'binary') { + return TargetGraphQLType.upload // CASE: id } else if ( schema.type === 'string' && @@ -840,7 +842,8 @@ export function getRequestSchemaAndNames( if ( payloadContentType === 'application/json' || payloadContentType === '*/*' || - payloadContentType === 'application/x-www-form-urlencoded' + payloadContentType === 'application/x-www-form-urlencoded' || + payloadContentType === 'multipart/form-data' ) { // Name extracted from a reference, if applicable let fromRef: string diff --git a/packages/openapi-to-graphql/src/resolver_builder.ts b/packages/openapi-to-graphql/src/resolver_builder.ts index 5b21c739..d99d2c6e 100644 --- a/packages/openapi-to-graphql/src/resolver_builder.ts +++ b/packages/openapi-to-graphql/src/resolver_builder.ts @@ -13,10 +13,12 @@ import { ConnectOptions } from './types/options' import { TargetGraphQLType, Operation } from './types/operation' import { SubscriptionContext } from './types/graphql' import { PreprocessingData } from './types/preprocessing_data' -import { RequestOptions } from './types/options' +import { RequestOptions, FileUploadOptions } from './types/options' import crossFetch from 'cross-fetch' +import { FileUpload } from 'graphql-upload' // Imports: +import stream from 'stream' import * as Oas3Tools from './oas_3_tools' import { JSONPath } from 'jsonpath-plus' import { debug } from 'debug' @@ -24,12 +26,14 @@ import { GraphQLError, GraphQLFieldResolver } from 'graphql' import formurlencoded from 'form-urlencoded' import { PubSub } from 'graphql-subscriptions' import urljoin from 'url-join' +import FormData from 'form-data' const pubsub = new PubSub() const translationLog = debug('translation') const httpLog = debug('http') const pubsubLog = debug('pubsub') +const uploadLog = debug('fileUpload') // OAS runtime expression reference locations const RUNTIME_REFERENCES = ['header.', 'query.', 'path.', 'body'] @@ -57,6 +61,7 @@ type GetResolverParams = { data: PreprocessingData baseUrl?: string requestOptions?: Partial> + fileUploadOptions?: FileUploadOptions fetch: typeof crossFetch } @@ -330,6 +335,7 @@ export function getResolver({ data, baseUrl, requestOptions, + fileUploadOptions, fetch }: GetResolverParams): GraphQLFieldResolver< TSource & OpenAPIToGraphQLSource, @@ -554,6 +560,7 @@ export function getResolver({ * GraphQL produces sanitized payload names, so we have to sanitize before * lookup here */ + let form: FormData resolveData.usedPayload = undefined if (typeof payloadName === 'string') { // The option genericPayloadArgName will change the payload name to "requestBody" @@ -572,6 +579,56 @@ export function getResolver({ rawPayload = formurlencoded( Oas3Tools.desanitizeObjectKeys(args[sanePayloadName], data.saneMap) ) + } else if (operation.payloadContentType === 'multipart/form-data') { + form = new FormData(fileUploadOptions) + + const formFieldsPayloadEntries = Object.entries(args[sanePayloadName]); + + (await Promise.all(formFieldsPayloadEntries.map(([_, v]) => v))) + .forEach((fieldValue, idx) => { + const fieldName = formFieldsPayloadEntries[idx][0] + + if (typeof fieldValue === 'object' && Boolean((fieldValue as Partial).createReadStream)) { + const uploadingFile = fieldValue as FileUpload + const originalFileStream = uploadingFile.createReadStream() + const filePassThrough = new stream.PassThrough() + + originalFileStream.on('readable', function () { + let data + + while (data = this.read()) { + const canReadNext = filePassThrough.write(data) + if (!canReadNext) { + this.pause() + filePassThrough.once('drain', () => this.resume()) + } + } + }) + + originalFileStream.on('error', () => { + uploadLog('Encountered an error while uploading the file %s', uploadingFile.filename) + }) + + originalFileStream.on('end', () => { + uploadLog('Upload for received file %s completed', uploadingFile.filename) + filePassThrough.end() + }) + + uploadLog('Queuing upload for received file %s', uploadingFile.filename) + + form.append(fieldName, filePassThrough, { + filename: uploadingFile.filename, + contentType: uploadingFile.mimetype + }) + } else if (typeof fieldValue !== 'string') { + // Handle all other primitives that aren't strings as strings the way the web server would expect it + form.append(fieldName, JSON.stringify(fieldValue)) + } else { + form.append(fieldName, fieldValue) + } + }) + + rawPayload = form } else { // Payload is not an object rawPayload = args[sanePayloadName] @@ -598,6 +655,17 @@ export function getResolver({ if (typeof headers === 'object') { Object.assign(options.headers, headers) } + + if (form) { + /** + * When there is a form, remove default content type and leave + * computation of content-type header to fetch + * + * See https://github.com/github/fetch/issues/505#issuecomment-293064470 + */ + Object.assign(options.headers, form.getHeaders()) + delete options.headers['content-type'] + } } // Query string: diff --git a/packages/openapi-to-graphql/src/schema_builder.ts b/packages/openapi-to-graphql/src/schema_builder.ts index 5bb82db7..61fec92c 100644 --- a/packages/openapi-to-graphql/src/schema_builder.ts +++ b/packages/openapi-to-graphql/src/schema_builder.ts @@ -36,6 +36,7 @@ import { GraphQLInputType, GraphQLInputFieldConfigMap } from 'graphql' +import { GraphQLUpload } from 'graphql-upload' // Imports: import { GraphQLBigInt, GraphQLJSON } from 'graphql-scalars' @@ -222,6 +223,10 @@ export function getGraphQLType({ case TargetGraphQLType.bigint: def.graphQLType = GraphQLBigInt return def.graphQLType + + case TargetGraphQLType.upload: + def.graphQLType = GraphQLUpload + return def.graphQLType } } @@ -780,6 +785,7 @@ function createFields({ data, baseUrl: data.options.baseUrl, requestOptions: data.options.requestOptions, + fileUploadOptions: data.options.fileUploadOptions, fetch }) diff --git a/packages/openapi-to-graphql/src/types/operation.ts b/packages/openapi-to-graphql/src/types/operation.ts index ccdb5fcb..a94e4cc8 100644 --- a/packages/openapi-to-graphql/src/types/operation.ts +++ b/packages/openapi-to-graphql/src/types/operation.ts @@ -38,6 +38,7 @@ export enum TargetGraphQLType { boolean = 'boolean', id = 'id', bigint = 'bigint', + upload = 'upload', // JSON json = 'json', diff --git a/packages/openapi-to-graphql/src/types/options.ts b/packages/openapi-to-graphql/src/types/options.ts index ae4ea551..3722bcd0 100644 --- a/packages/openapi-to-graphql/src/types/options.ts +++ b/packages/openapi-to-graphql/src/types/options.ts @@ -7,6 +7,7 @@ import { GraphQLOperationType, SubscriptionContext } from './graphql' import { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql' import crossFetch from 'cross-fetch' +import FormData from 'form-data' /** * Type definition of the options that users can pass to OpenAPI-to-GraphQL. @@ -76,6 +77,17 @@ export type RequestOptions = Omit< qs?: Record } +/** + * We use the form-data library to prepare multipart requests within the resolver API calls, + * also it provides support for handling file as streams this way the file upload has a minimal memory footprint, + * unlike the situation where the entire file in memory initially. + * + * Provides accommodation to allow overrides or add options for how the form data representation for multipart requests is generated + * + * Based on: https://github.com/form-data/form-data#custom-options + */ +export type FileUploadOptions = ConstructorParameters[0] + export type Options = Partial< InternalOptions > @@ -275,6 +287,14 @@ export type InternalOptions = { resolve: GraphQLFieldResolver }> + /** + * Allows one to define config for the form data that will be used in streaming + * the uploaded file from the client to the intending endpoint + * + * Based on: https://github.com/form-data/form-data#custom-options + */ + fileUploadOptions?: FileUploadOptions + // Authentication options /** diff --git a/packages/openapi-to-graphql/test/README.md b/packages/openapi-to-graphql/test/README.md index f6dbc490..aef256ab 100644 --- a/packages/openapi-to-graphql/test/README.md +++ b/packages/openapi-to-graphql/test/README.md @@ -25,11 +25,12 @@ The following table summarizes the purposes of these tests. | Test file | API(s) | Testing purpose | |---|---|---| | `example_api.test.ts` | `Example API` | An assortment of basic functionality and options on a company-themed API | -| `authentication.test.ts` | `Example API` | Basic authentication tests including using the [viewer functionality](https://github.com/IBM/openapi-to-graphql/blob/master/packages/openapi-to-graphql/README.md#authentication) | -| `example_api2.test.ts` | `Example API 2` | The [`operationIdFieldNames` option](https://github.com/IBM/openapi-to-graphql/blob/master/packages/openapi-to-graphql/README.md#options) | -| `example_api3.test.ts` | `Example API` and `Example API 3` | Creating GraphQL wrappers from multiple APIs and [interOAS links](https://github.com/IBM/openapi-to-graphql/blob/master/packages/openapi-to-graphql/README.md#nested-objects) | +| `authentication.test.ts` | `Example API` | Basic authentication tests including using the [viewer functionality](../README.md#authentication) | +| `example_api2.test.ts` | `Example API 2` | The [`operationIdFieldNames` option](../README.md#options) | +| `example_api3.test.ts` | `Example API` and `Example API 3` | Creating GraphQL wrappers from multiple APIs and [interOAS links](../README.md#nested-objects) | | `example_api4.test.ts` | `Example API 4` | JSON schema [combining schema](https://json-schema.org/understanding-json-schema/reference/combining.html) keywords | -| `example_api5.test.ts` | `Example API 5` | The [`simpleNames` option](https://github.com/IBM/openapi-to-graphql/blob/master/packages/openapi-to-graphql/README.md#options) | +| `example_api5.test.ts` | `Example API 5` | The [`simpleNames` option](../README.md#options) | | `example_api6.test.ts` | `Example API 6` | An assortment of other functionality and options | | `example_api7.test.ts` | `Example API 7` | [Subscription support](../docs/subscriptions.md) | +| `file_upload.test.ts` | `File Upload API` | [File uploads](../README.md#file-uploads) and [file upload options](../README.md#options) | | `extensions.test.ts` | `Extensions`, `Extensions Error 1`, `Extensions Error 2`, `Extensions Error 3`, `Extensions Error 4`, `Extensions Error 5`, `Extensions Error 6`, `Extensions Error 7` | The [`x-graphql-field-name`, `x-graphql-type-name`, and `x-graphql-enum-mapping` extensions](https://github.com/IBM/openapi-to-graphql/tree/master/packages/openapi-to-graphql#custom-type-and-field-names-and-enum-values) \ No newline at end of file diff --git a/packages/openapi-to-graphql/test/authentication.test.ts b/packages/openapi-to-graphql/test/authentication.test.ts index d47453b9..e5b8a19a 100644 --- a/packages/openapi-to-graphql/test/authentication.test.ts +++ b/packages/openapi-to-graphql/test/authentication.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql } from 'graphql' +import { graphql, GraphQLSchema } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -16,11 +16,9 @@ const PORT = 3003 // update PORT for this test case: oas.servers[0].variables.port.default = String(PORT) -let createdSchema +let createdSchema: GraphQLSchema -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL.createGraphQLSchema(oas).then(({ schema }) => { @@ -30,9 +28,7 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return stopServer() }) diff --git a/packages/openapi-to-graphql/test/cloudfunction.test.ts b/packages/openapi-to-graphql/test/cloudfunction.test.ts index 17907930..51517440 100644 --- a/packages/openapi-to-graphql/test/cloudfunction.test.ts +++ b/packages/openapi-to-graphql/test/cloudfunction.test.ts @@ -5,14 +5,14 @@ 'use strict' -import { graphql, parse, validate } from 'graphql' +import { graphql, GraphQLSchema, parse, validate } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' const oas = require('./fixtures/cloudfunction.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(async () => { const { schema } = await openAPIToGraphQL.createGraphQLSchema(oas) diff --git a/packages/openapi-to-graphql/test/example_api.test.ts b/packages/openapi-to-graphql/test/example_api.test.ts index cbb56725..0e7976df 100644 --- a/packages/openapi-to-graphql/test/example_api.test.ts +++ b/packages/openapi-to-graphql/test/example_api.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql, parse, validate } from 'graphql' +import { graphql, GraphQLInputObjectType, GraphQLInputObjectTypeConfig, GraphQLObjectTypeConfig, GraphQLSchema, parse, validate } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -18,11 +18,9 @@ const PORT = 3002 // Update PORT for this test case: oas.servers[0].variables.port.default = String(PORT) -let createdSchema +let createdSchema: GraphQLSchema -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL @@ -36,9 +34,7 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return stopServer() }) @@ -1959,10 +1955,10 @@ test('Required properties for input object types', () => { const userInputType = createdSchema.getType('UserInput') // The exclamation mark shows that it is a required (non-nullable) property - expect(userInputType.toConfig().fields.address.type.toString()).toEqual( + expect((userInputType.toConfig() as GraphQLInputObjectTypeConfig).fields['address'].type.toString()).toEqual( 'AddressInput!' ) - expect(userInputType.toConfig().fields.address2.type.toString()).toEqual( + expect((userInputType.toConfig() as GraphQLInputObjectTypeConfig).fields['address2'].type.toString()).toEqual( 'AddressInput' ) }) @@ -2312,8 +2308,8 @@ test('Non-nullable properties for object types', () => { const coordinates = createdSchema.getType('Coordinates') // The exclamation mark shows that it is a required (non-nullable) property - expect(coordinates.toConfig().fields.lat.type.toString()).toEqual('Float!') - expect(coordinates.toConfig().fields.long.type.toString()).toEqual('Float!') + expect((coordinates.toConfig() as GraphQLObjectTypeConfig).fields['lat'].type.toString()).toEqual('Float!') + expect((coordinates.toConfig() as GraphQLObjectTypeConfig).fields['long'].type.toString()).toEqual('Float!') }) test('Option genericPayloadArgName', () => { diff --git a/packages/openapi-to-graphql/test/example_api2.test.ts b/packages/openapi-to-graphql/test/example_api2.test.ts index 00e8b1c7..10f6677a 100644 --- a/packages/openapi-to-graphql/test/example_api2.test.ts +++ b/packages/openapi-to-graphql/test/example_api2.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql } from 'graphql' +import { graphql, GraphQLObjectType, GraphQLSchema } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -16,7 +16,7 @@ const PORT = 3004 // Update PORT for this test case: oas.servers[0].variables.port.default = String(PORT) -let createdSchema +let createdSchema: GraphQLSchema /** * This test suite is used to verify the behavior of the operationIdFieldNames @@ -26,9 +26,7 @@ let createdSchema * have operationIDs. */ -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL @@ -40,9 +38,7 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return stopServer() }) @@ -62,7 +58,7 @@ test('The option operationIdFieldNames should allow both operations to be presen } } - const gqlTypes = Object.keys(createdSchema._typeMap.Query.getFields()).length + const gqlTypes = Object.keys((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields()).length expect(gqlTypes).toEqual(oasGetCount) }) diff --git a/packages/openapi-to-graphql/test/example_api3.test.ts b/packages/openapi-to-graphql/test/example_api3.test.ts index 0bfc17c1..b56db353 100644 --- a/packages/openapi-to-graphql/test/example_api3.test.ts +++ b/packages/openapi-to-graphql/test/example_api3.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql, parse, validate } from 'graphql' +import { graphql, GraphQLSchema, parse, validate } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -22,16 +22,14 @@ const PORT2 = 3006 oas.servers[0].variables.port.default = String(PORT) oas3.servers[0].variables.port.default = String(PORT2) +let createdSchema: GraphQLSchema + /** * This test suite is used to verify the behavior of interOAS links, i.e. * links across different OASs */ -let createdSchema - -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL @@ -44,9 +42,7 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return Promise.all([api.stopServer(), api2.stopServer()]) }) diff --git a/packages/openapi-to-graphql/test/example_api4.test.ts b/packages/openapi-to-graphql/test/example_api4.test.ts index 2959ae09..b7afc7e8 100644 --- a/packages/openapi-to-graphql/test/example_api4.test.ts +++ b/packages/openapi-to-graphql/test/example_api4.test.ts @@ -5,20 +5,18 @@ 'use strict' -import { graphql } from 'graphql' +import { graphql, GraphQLSchema } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' const oas = require('./fixtures/example_oas4.json') -// This test suite is used to verify the behavior of anyOf and oneOf handling +let createdSchema: GraphQLSchema -let createdSchema +// This test suite is used to verify the behavior of anyOf and oneOf handling -/** - * Set up the schema - */ +// Set up the schema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) diff --git a/packages/openapi-to-graphql/test/example_api5.test.ts b/packages/openapi-to-graphql/test/example_api5.test.ts index 2a7f2949..de8e78fa 100644 --- a/packages/openapi-to-graphql/test/example_api5.test.ts +++ b/packages/openapi-to-graphql/test/example_api5.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql } from 'graphql' +import { graphql, GraphQLSchema } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -18,11 +18,9 @@ oas.servers[0].variables.port.default = String(PORT) // Testing the simpleNames option -let createdSchema +let createdSchema: GraphQLSchema -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL @@ -36,9 +34,7 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return stopServer() }) diff --git a/packages/openapi-to-graphql/test/example_api6.test.ts b/packages/openapi-to-graphql/test/example_api6.test.ts index 0f02b360..6adb6a08 100644 --- a/packages/openapi-to-graphql/test/example_api6.test.ts +++ b/packages/openapi-to-graphql/test/example_api6.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql, parse, validate } from 'graphql' +import { graphql, GraphQLSchema, parse, validate } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import * as openAPIToGraphQL from '../src/index' @@ -17,11 +17,9 @@ const PORT = 3008 // Update PORT for this test case: oas.servers[0].variables.port.default = String(PORT) -let createdSchema +let createdSchema: GraphQLSchema -/** - * Set up the schema first and run example API server - */ +// Set up the schema first and run example API server beforeAll(() => { return Promise.all([ openAPIToGraphQL.createGraphQLSchema(oas).then(({ schema, report }) => { @@ -31,15 +29,13 @@ beforeAll(() => { ]) }) -/** - * Shut down API server - */ +// Shut down API server afterAll(() => { return stopServer() }) test('Option requestOptions should work with links', () => { - // Verifying the behavior of the link by itself + // Verify the behavior of the link by itself const query = `{ object { object2Link { diff --git a/packages/openapi-to-graphql/test/example_api7.test.ts b/packages/openapi-to-graphql/test/example_api7.test.ts index 62b29cbd..271b23ad 100644 --- a/packages/openapi-to-graphql/test/example_api7.test.ts +++ b/packages/openapi-to-graphql/test/example_api7.test.ts @@ -5,7 +5,7 @@ 'use strict' -import { graphql, parse, validate, execute, subscribe } from 'graphql' +import { graphql, parse, validate, execute, subscribe, GraphQLSchema } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' import { createServer } from 'http' @@ -29,14 +29,12 @@ const MQTT_PORT = 1885 oas.servers[0].variables.port.default = String(HTTP_PORT) oas.servers[1].variables.port.default = String(MQTT_PORT) -let createdSchema +let createdSchema: GraphQLSchema let wsServer let mqttClient let subscriptionServer -/** - * Set up the schema first and run example API servers - */ +// Set up the schema first and run example API servers beforeAll(() => { return Promise.all([ openAPIToGraphQL diff --git a/packages/openapi-to-graphql/test/example_gql_server.js b/packages/openapi-to-graphql/test/example_gql_server.js index 77c8511d..91ab9f36 100644 --- a/packages/openapi-to-graphql/test/example_gql_server.js +++ b/packages/openapi-to-graphql/test/example_gql_server.js @@ -11,12 +11,13 @@ const { graphqlHTTP } = require('express-graphql') const app = express() const openAPIToGraphQL = require('../dist/index') -const oas = require('./fixtures/example_oas.json') +// const oas = require('./fixtures/example_oas.json') // const oas = require('./fixtures/example_oas2.json') // const oas = require('./fixtures/example_oas3.json') // const oas = require('./fixtures/example_oas4.json') // const oas = require('./fixtures/example_oas5.json') // const oas = require('./fixtures/example_oas6.json') +const oas = require('./fixtures/file_upload.json') // const oas = require('./fixtures/github.json') // const oas = require('./fixtures/instagram.json') diff --git a/packages/openapi-to-graphql/test/extensions.test.ts b/packages/openapi-to-graphql/test/extensions.test.ts index 8e938f91..4c2d457e 100644 --- a/packages/openapi-to-graphql/test/extensions.test.ts +++ b/packages/openapi-to-graphql/test/extensions.test.ts @@ -16,10 +16,6 @@ import { import * as openAPIToGraphQL from '../src/index' import { Oas3 } from '../src/types/oas3' -/** - * Set up the schema first - */ - describe('GraphQL Extensions', () => { describe('Schema output', () => { let oas: Oas3 diff --git a/packages/openapi-to-graphql/test/file_upload.test.ts b/packages/openapi-to-graphql/test/file_upload.test.ts new file mode 100644 index 00000000..f1bb4eea --- /dev/null +++ b/packages/openapi-to-graphql/test/file_upload.test.ts @@ -0,0 +1,176 @@ +// Copyright IBM Corp. 2017,2018. All Rights Reserved. +// Node module: openapi-to-graphql +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict' + +import { afterAll, beforeAll, expect, test } from '@jest/globals' +import * as openAPIToGraphQL from '../src/index' +import * as Oas3Tools from '../src/oas_3_tools' + +import { Volume } from 'memfs' +import FormData from 'form-data' +import { createServer } from 'http' +import fetch from 'cross-fetch' +import { graphql, GraphQLObjectType, GraphQLSchema } from 'graphql' +import { GraphQLOperation, processRequest } from 'graphql-upload' +import { startServer as startAPIServer, stopServer as stopAPIServer } from './file_upload_api_server' + +// Set up the schema first +const oas = require('./fixtures/file_upload.json') + +const PORT = 3010 + +// Update PORT for this test case: +oas.servers[0].variables.port.default = String(PORT) + +let createdSchema: GraphQLSchema + +beforeAll(async () => { + const [{ schema }] = await Promise.all([ + openAPIToGraphQL.createGraphQLSchema(oas), + startAPIServer(PORT) + ]) + + createdSchema = schema +}) + +afterAll(async () => { + await stopAPIServer() +}) + +test('All mutation endpoints are found to be present', () => { + let oasMutCount = 0 + for (let path in oas.paths) { + for (let method in oas.paths[path]) { + if (Oas3Tools.isHttpMethod(method) && method !== 'get') oasMutCount++ + } + } + const gqlTypes = Object.keys((createdSchema.getTypeMap().Mutation as GraphQLObjectType).getFields()).length + expect(gqlTypes).toEqual(oasMutCount) +}) + +test('Registers the graphql-upload Upload scalar type', async () => { + const query = `{ + __type(name: "Upload") { + name + kind + } + }` + + const result = await graphql(createdSchema, query) + expect(result).toEqual({ + data: { + __type: { + name: 'Upload', + kind: 'SCALAR' + } + } + }) +}) + +test('Introspection for mutations returns a mutation matching the custom field specified for the multipart API definition', async () => { + const query = `{ + __schema { + mutationType { + fields { + name + args { + name + type { + name + kind + } + } + type { + name + kind + } + } + } + } + }` + + const result = await graphql(createdSchema, query) + + expect(result).toEqual({ + data: { + __schema: { + mutationType: { + fields: expect.arrayContaining([ + expect.objectContaining({ + name: 'fileUploadTest', + args: expect.arrayContaining([ + expect.objectContaining({ + name: 'uploadInput' + }) + ]) + }) + ]) + } + } + } + }) +}) + +test('Upload completes without any error', async () => { + // Setup GraphQL for integration test + const graphqlServer = createServer(async (req, res) => { + try { + const operation = await processRequest(req, res) as GraphQLOperation + const result = await graphql(createdSchema, operation.query, null, null, operation.variables) + res.end(JSON.stringify(result)) + } catch (e) { + console.log(e) + } + }) + + const { port: graphqlServerPort, close: closeGraphQLServer } = await new Promise((resolve, reject) => { + graphqlServer.listen(function (err) { + if (err) { + return reject(err) + } + + return resolve({ + port: this.address().port, + close: () => this.close() + }) + }) + }) + + const vol = new Volume() + + // Create mocked in memory file for upload + vol.fromJSON({ + './README.md': '1' + }, '/app') + + // Prepare request to match GraphQL multipart request spec + // Reference: https://github.com/jaydenseric/graphql-multipart-request-spec + const form = new FormData() + const query = ` + mutation FileUploadTest($file: Upload!) { + fileUploadTest(uploadInput: { file: $file }) { + id + url + } + } + ` + form.append('operations', JSON.stringify({ query, variables: { file: null } })) + form.append('map', JSON.stringify({ 0: ['variables.file'] })) + form.append('0', vol.createReadStream('/app/README.md'), { + filename: 'readme.md', + filepath: '/app' + }) + + // @ts-ignore + const uploadResult = await fetch(`http://localhost:${graphqlServerPort}`, { method: 'POST', body: form }) + .then(res => res.json()) + + expect(uploadResult.errors).not.toBeDefined() + expect(uploadResult.data).toBeDefined() + expect(uploadResult.data.fileUploadTest).toBeDefined() + + closeGraphQLServer() +}) diff --git a/packages/openapi-to-graphql/test/file_upload_api_server.js b/packages/openapi-to-graphql/test/file_upload_api_server.js new file mode 100644 index 00000000..82dd95ea --- /dev/null +++ b/packages/openapi-to-graphql/test/file_upload_api_server.js @@ -0,0 +1,56 @@ +// Copyright IBM Corp. 2017,2018. All Rights Reserved. +// Node module: openapi-to-graphql +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict' + +const express = require('express') + +let server // holds server object for shutdown + +/** + * Starts the server at the given port + */ +function startServer (PORT) { + const app = express() + + const bodyParser = require('body-parser') + app.use(bodyParser.json()) + + app.post('/api/upload', (req, res) => { + res.json({ + id: '1234567098', + url: 'https://some-random-url.domain/assets/upload-file.ext' + }) + }) + + return new Promise(resolve => { + server = app.listen(PORT, () => { + console.log(`Example API accessible on port ${PORT}`) + resolve() + }) + }) +} + +/** + * Stops server. + */ +function stopServer () { + return new Promise(resolve => { + server.close(() => { + console.log(`Stopped API server`) + resolve() + }) + }) +} + +// If run from command line, start server: +if (require.main === module) { + void (async () => startServer(3002))() +} + +module.exports = { + startServer, + stopServer +} diff --git a/packages/openapi-to-graphql/test/fixtures/file_upload.json b/packages/openapi-to-graphql/test/fixtures/file_upload.json new file mode 100644 index 00000000..e06ebfa8 --- /dev/null +++ b/packages/openapi-to-graphql/test/fixtures/file_upload.json @@ -0,0 +1,74 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "File Upload API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:{port}/{basePath}", + "description": "The location of the local test server.", + "variables": { + "port": { + "default": "3010" + }, + "basePath": { + "default": "api" + } + } + } + ], + "paths": { + "/upload": { + "post": { + "x-graphql-field-name": "fileUploadTest", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file" + ], + "type": "object", + "properties": { + "file": { + "maxLength": 255, + "pattern": "^[0-9a-zA-Z_./ -]*$", + "type": "string", + "format": "binary" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "required": [ + "id", + "url" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "url": { + "type": "string" + } + } + } + } + } + } + } + } + } + } +} diff --git a/packages/openapi-to-graphql/test/government_social_work.test.ts b/packages/openapi-to-graphql/test/government_social_work.test.ts index 578d1a22..701ebd83 100644 --- a/packages/openapi-to-graphql/test/government_social_work.test.ts +++ b/packages/openapi-to-graphql/test/government_social_work.test.ts @@ -5,18 +5,16 @@ 'use strict' -import { graphql, parse, validate } from 'graphql' +import { graphql, GraphQLObjectType, GraphQLSchema, parse, validate } from 'graphql' import { afterAll, beforeAll, expect, test } from '@jest/globals' const openAPIToGraphQL = require('../src/index') const Oas3Tools = require('../src/oas_3_tools') -/** - * Set up the schema first - */ +// Set up the schema first const oas = require('./fixtures/government_social_work.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) @@ -32,7 +30,7 @@ test('All query endpoints present', () => { if (method === 'get') oasGetCount++ } } - const gqlTypes = Object.keys(createdSchema._typeMap.Query.getFields()).length + const gqlTypes = Object.keys((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields()).length expect(gqlTypes).toEqual(oasGetCount) }) @@ -43,7 +41,7 @@ test('All mutation endpoints present', () => { if (Oas3Tools.isHttpMethod(method) && method !== 'get') oasMutCount++ } } - const gqlTypes = Object.keys(createdSchema._typeMap.Mutation.getFields()) + const gqlTypes = Object.keys((createdSchema.getTypeMap().Mutation as GraphQLObjectType).getFields()) .length expect(gqlTypes).toEqual(oasMutCount) }) diff --git a/packages/openapi-to-graphql/test/ibm_language_translator.test.ts b/packages/openapi-to-graphql/test/ibm_language_translator.test.ts index 30530e69..61a112b3 100644 --- a/packages/openapi-to-graphql/test/ibm_language_translator.test.ts +++ b/packages/openapi-to-graphql/test/ibm_language_translator.test.ts @@ -6,15 +6,14 @@ 'use strict' import { afterAll, beforeAll, expect, test } from '@jest/globals' +import { GraphQLObjectType, GraphQLSchema } from 'graphql' import * as openAPIToGraphQL from '../src/index' -/** - * Set up the schema first - */ +// Set up the schema first const oas = require('./fixtures/ibm_language_translator.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) @@ -31,7 +30,7 @@ test('All IBM Language Translator query endpoints present', () => { } } const gqlTypes = Object.keys( - createdSchema._typeMap.Query.getFields().viewerAnyAuth.type.getFields() + ((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields().viewerAnyAuth.type as GraphQLObjectType).getFields() ).length expect(gqlTypes).toEqual(oasGetCount) diff --git a/packages/openapi-to-graphql/test/instagram.test.ts b/packages/openapi-to-graphql/test/instagram.test.ts index 911b9ee3..c701da06 100644 --- a/packages/openapi-to-graphql/test/instagram.test.ts +++ b/packages/openapi-to-graphql/test/instagram.test.ts @@ -6,15 +6,14 @@ 'use strict' import { afterAll, beforeAll, expect, test } from '@jest/globals' +import { GraphQLObjectType, GraphQLSchema } from 'graphql' import * as openAPIToGraphQL from '../src/index' -/** - * Set up the schema first - */ +// Set up the schema first const oas = require('./fixtures/instagram.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) @@ -31,7 +30,7 @@ test('All Instagram query endpoints present', () => { } } const gqlTypes = Object.keys( - createdSchema._typeMap.Query.getFields().viewerAnyAuth.type.getFields() + ((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields().viewerAnyAuth.type as GraphQLObjectType).getFields() ).length expect(gqlTypes).toEqual(oasGetCount) }) diff --git a/packages/openapi-to-graphql/test/stripe.test.ts b/packages/openapi-to-graphql/test/stripe.test.ts index 08ca71b0..286506e7 100644 --- a/packages/openapi-to-graphql/test/stripe.test.ts +++ b/packages/openapi-to-graphql/test/stripe.test.ts @@ -6,15 +6,14 @@ 'use strict' import { afterAll, beforeAll, expect, test } from '@jest/globals' +import { GraphQLObjectType, GraphQLSchema } from 'graphql' import * as openAPIToGraphQL from '../src/index' -/** - * Set up the schema first - */ +// Set up the schema first const oas = require('./fixtures/stripe.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) @@ -31,7 +30,7 @@ test('All Stripe query endpoints present', () => { } } const gqlTypes = Object.keys( - createdSchema._typeMap.Query.getFields().viewerAnyAuth.type.getFields() + ((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields().viewerAnyAuth.type as GraphQLObjectType).getFields() ).length expect(gqlTypes).toEqual(oasGetCount) diff --git a/packages/openapi-to-graphql/test/weather_underground.test.ts b/packages/openapi-to-graphql/test/weather_underground.test.ts index a75291e6..d9b685e7 100644 --- a/packages/openapi-to-graphql/test/weather_underground.test.ts +++ b/packages/openapi-to-graphql/test/weather_underground.test.ts @@ -6,15 +6,14 @@ 'use strict' import { afterAll, beforeAll, expect, test } from '@jest/globals' +import { GraphQLObjectType, GraphQLSchema } from 'graphql' import * as openAPIToGraphQL from '../src/index' -/** - * Set up the schema first - */ +// Set up the schema first const oas = require('./fixtures/weather_underground.json') -let createdSchema +let createdSchema: GraphQLSchema beforeAll(() => { return openAPIToGraphQL .createGraphQLSchema(oas) @@ -30,6 +29,6 @@ test('All Weather Underground query endpoints present', () => { if (method === 'get') oasGetCount++ } } - const gqlTypes = Object.keys(createdSchema._typeMap.Query.getFields()).length + const gqlTypes = Object.keys((createdSchema.getTypeMap().Query as GraphQLObjectType).getFields()).length expect(gqlTypes).toEqual(oasGetCount) }) diff --git a/packages/openapi-to-graphql/yarn.lock b/packages/openapi-to-graphql/yarn.lock index 8dff7df3..32a1858e 100644 --- a/packages/openapi-to-graphql/yarn.lock +++ b/packages/openapi-to-graphql/yarn.lock @@ -550,6 +550,13 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/accepts@*": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.15" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" @@ -583,11 +590,67 @@ dependencies: "@babel/types" "^7.3.0" +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" + integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== + +"@types/cookies@*": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" + integrity sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + "@types/deep-equal@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== +"@types/express-serve-static-core@^4.17.18": + version "4.17.26" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz#5d9a8eeecb9d5f9d7fc1d85f541512a84638ae88" + integrity sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/fs-capacitor@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" + integrity sha512-FKVPOCFbhCvZxpVAMhdBdTfVfXUpsh15wFHgqOKxh9N9vzWZVuWCSijZ5T4U34XYNnuj2oduh6xcs1i+LPI+BQ== + dependencies: + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -595,6 +658,16 @@ dependencies: "@types/node" "*" +"@types/graphql-upload@^8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@types/graphql-upload/-/graphql-upload-8.0.7.tgz#71dd5d4a8d9ddb598df91298d6e98a943061b255" + integrity sha512-uXhInuUY/W6n9a+PdCt9vcZ7z2m+NzByBJFvvDM+46pljqEwXXnIAjsEI1Dka2FKRTGthetm/imN//RhtEEYSA== + dependencies: + "@types/express" "*" + "@types/fs-capacitor" "*" + "@types/koa" "*" + graphql "^15.3.0" + "@types/graphql@^0.9.1": version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" @@ -607,6 +680,16 @@ dependencies: graphql "*" +"@types/http-assert@*": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" + integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== + +"@types/http-errors@*": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67" + integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -634,6 +717,37 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + +"@types/koa-compose@*": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" + integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.13.4" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" + integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -654,6 +768,24 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -1124,6 +1256,13 @@ bulk-write-stream@^2.0.1: inherits "^2.0.3" readable-stream "^3.1.1" +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -1598,6 +1737,13 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -2342,6 +2488,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-urlencoded@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/form-urlencoded/-/form-urlencoded-6.0.4.tgz#62305ea704bb86e1c8b946d1aae5a34e02b6634f" @@ -2365,6 +2520,16 @@ from2@^2.3.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-capacitor@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" + integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== + +fs-monkey@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2565,6 +2730,16 @@ graphql-subscriptions@^1.1.0: dependencies: iterall "^1.3.0" +graphql-upload@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-13.0.0.tgz#1a255b64d3cbf3c9f9171fa62a8fb0b9b59bb1d9" + integrity sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA== + dependencies: + busboy "^0.3.1" + fs-capacitor "^6.2.0" + http-errors "^1.8.1" + object-path "^0.11.8" + graphql@*, graphql@^15.3.0: version "15.5.1" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" @@ -2675,6 +2850,17 @@ http-errors@1.8.0: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -3832,6 +4018,13 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memfs@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.0.tgz#8bc12062b973be6b295d4340595736a656f0a257" + integrity sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA== + dependencies: + fs-monkey "1.0.3" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -4203,6 +4396,11 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-path@^0.11.8: + version "0.11.8" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.8.tgz#ed002c02bbdd0070b78a27455e8ae01fc14d4742" + integrity sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA== + object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -5166,6 +5364,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -5435,6 +5638,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" diff --git a/yarn.lock b/yarn.lock index 4f290825..75f66862 100644 --- a/yarn.lock +++ b/yarn.lock @@ -684,6 +684,13 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/accepts@*": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + "@types/babel__core@^7.0.0": version "7.1.14" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" @@ -728,6 +735,36 @@ dependencies: "@babel/types" "^7.3.0" +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" + integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== + +"@types/cookies@*": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" + integrity sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + "@types/deep-equal@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" @@ -738,6 +775,32 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/express-serve-static-core@^4.17.18": + version "4.17.26" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz#5d9a8eeecb9d5f9d7fc1d85f541512a84638ae88" + integrity sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/fs-capacitor@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" + integrity sha512-FKVPOCFbhCvZxpVAMhdBdTfVfXUpsh15wFHgqOKxh9N9vzWZVuWCSijZ5T4U34XYNnuj2oduh6xcs1i+LPI+BQ== + dependencies: + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -745,6 +808,16 @@ dependencies: "@types/node" "*" +"@types/graphql-upload@^8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@types/graphql-upload/-/graphql-upload-8.0.7.tgz#71dd5d4a8d9ddb598df91298d6e98a943061b255" + integrity sha512-uXhInuUY/W6n9a+PdCt9vcZ7z2m+NzByBJFvvDM+46pljqEwXXnIAjsEI1Dka2FKRTGthetm/imN//RhtEEYSA== + dependencies: + "@types/express" "*" + "@types/fs-capacitor" "*" + "@types/koa" "*" + graphql "^15.3.0" + "@types/graphql@^0.9.1": version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" @@ -757,6 +830,16 @@ dependencies: graphql "*" +"@types/http-assert@*": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" + integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== + +"@types/http-errors@*": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67" + integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -784,6 +867,37 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + +"@types/koa-compose@*": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" + integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.13.4" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" + integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/minimatch@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" @@ -819,6 +933,16 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -826,6 +950,14 @@ dependencies: "@types/node" "*" +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -1347,6 +1479,13 @@ bulk-write-stream@^2.0.1: inherits "^2.0.3" readable-stream "^3.1.1" +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -1944,6 +2083,13 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -2907,6 +3053,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-urlencoded@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/form-urlencoded/-/form-urlencoded-6.0.4.tgz#62305ea704bb86e1c8b946d1aae5a34e02b6634f" @@ -2930,6 +3085,11 @@ from2@^2.3.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-capacitor@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" + integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== + fs-extra@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -2949,6 +3109,11 @@ fs-extra@9.0.0: jsonfile "^6.0.1" universalify "^1.0.0" +fs-monkey@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3171,6 +3336,16 @@ graphql-subscriptions@^1.1.0: dependencies: iterall "^1.3.0" +graphql-upload@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-13.0.0.tgz#1a255b64d3cbf3c9f9171fa62a8fb0b9b59bb1d9" + integrity sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA== + dependencies: + busboy "^0.3.1" + fs-capacitor "^6.2.0" + http-errors "^1.8.1" + object-path "^0.11.8" + graphql@*, graphql@^15.3.0: version "15.5.1" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" @@ -3281,6 +3456,17 @@ http-errors@1.8.0: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -4544,6 +4730,13 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memfs@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.0.tgz#8bc12062b973be6b295d4340595736a656f0a257" + integrity sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA== + dependencies: + fs-monkey "1.0.3" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -4938,6 +5131,11 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-path@^0.11.8: + version "0.11.8" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.8.tgz#ed002c02bbdd0070b78a27455e8ae01fc14d4742" + integrity sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA== + object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -6127,6 +6325,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -6408,6 +6611,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"