├── design
├── logo-negative.png
├── logo-primary.png
├── logo-negative.svg
└── logo-primary.svg
├── package.json
├── providers
└── ApolloServerProvider.js
├── LICENSE
├── .gitignore
├── src
└── ApolloServer
│ └── index.js
├── README.md
└── docs
└── pt-br.md
/design/logo-negative.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukinco/adonis-apollo-server/HEAD/design/logo-negative.png
--------------------------------------------------------------------------------
/design/logo-primary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lukinco/adonis-apollo-server/HEAD/design/logo-primary.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lukinco/adonis-apollo-server",
3 | "version": "0.2.0",
4 | "description": "Production-ready Node.js GraphQL server for AdonisJS",
5 | "main": "index.js",
6 | "repository": "git@github.com:lukinco/adonis-apollo-server.git",
7 | "author": "Lukin Co.",
8 | "license": "MIT",
9 | "scripts": {
10 | "postversion": "npm publish --access=public"
11 | },
12 | "dependencies": {
13 | "apollo-server-core": "2.14.2",
14 | "apollo-server-module-graphiql": "1.4.0",
15 | "graphql-tools": "6.0.16",
16 | "graphql-upload": "11.0.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/providers/ApolloServerProvider.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { ServiceProvider } = require('@adonisjs/fold')
4 | const ApolloServer = require('../src/ApolloServer')
5 |
6 | class ApolloServerProvider extends ServiceProvider {
7 | /**
8 | * Register AdonisApollo to the IoC container
9 | * with `Adonis/Addons/ApolloServer` namespace.
10 | *
11 | * @method register
12 | *
13 | * @return {void}
14 | */
15 | register () {
16 | this.app.singleton('Adonis/Addons/ApolloServer', () => {
17 | return new (ApolloServer)()
18 | })
19 |
20 | this.app.alias('Adonis/Addons/ApolloServer', 'ApolloServer')
21 | }
22 | }
23 |
24 | module.exports = ApolloServerProvider
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Lukin Co.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # System files
107 | .DS_Store
108 |
--------------------------------------------------------------------------------
/src/ApolloServer/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { HttpQueryError, runHttpQuery } = require('apollo-server-core')
4 | const GraphiQL = require('apollo-server-module-graphiql')
5 | const { print } = require('graphql')
6 | const { processRequest, GraphQLUpload } = require('graphql-upload')
7 | const { makeExecutableSchema } = require('graphql-tools')
8 |
9 | class ApolloServer {
10 | getQuery (request, response) {
11 | if (request.is('multipart/form-data')) {
12 | return processRequest(request.request, response.response)
13 | }
14 |
15 | return request.method() === 'POST' ? toString(request.post()) : request.get()
16 | }
17 |
18 | async graphql ({ options, request, response, onError }) {
19 | if (!options) {
20 | throw new Error('Apollo Server requires options.')
21 | }
22 |
23 | const schemaWithUpload = {
24 | typeDefs: `${options.schema.typeDefs}\nscalar Upload`,
25 | resolvers: {
26 | ...options.schema.resolvers,
27 | Upload: GraphQLUpload,
28 | },
29 | }
30 |
31 | options.schema = makeExecutableSchema(schemaWithUpload)
32 |
33 | const query = await this.getQuery(request, response)
34 |
35 | return runHttpQuery([request], {
36 | method: request.method(),
37 | options: options,
38 | query: query,
39 | }).then(({ graphqlResponse }) => {
40 | const parsedResponse = JSON.parse(graphqlResponse)
41 | if (!parsedResponse.errors) {
42 | return response.json(graphqlResponse)
43 | }
44 |
45 | const transformedError = onError ? onError(parsedResponse.errors) : parsedResponse.errors
46 | return response.json({
47 | errors: transformedError,
48 | data: parsedResponse.data
49 | })
50 | }, error => {
51 | if ('HttpQueryError' !== error.name) {
52 | throw error
53 | }
54 |
55 | if (error.headers) {
56 | Object.keys(error.headers).forEach(header => {
57 | response.header(header, error.headers[header])
58 | })
59 | }
60 |
61 | const errorParsed = JSON.parse(error.message).errors
62 | const transformedError = onError ? onError(errorParsed) : errorParsed
63 |
64 | response.status(error.statusCode).send({
65 | errors: transformedError
66 | })
67 | })
68 | }
69 |
70 | graphiql ({ options, request, response }) {
71 | if (!options) {
72 | throw new Error('Apollo Server GraphiQL requires options.')
73 | }
74 |
75 | const query = request.originalUrl()
76 |
77 | return GraphiQL.resolveGraphiQLString(query, options, request).then(graphiqlString => {
78 | response.header('Content-Type', 'text/html').send(graphiqlString)
79 | }, error => response.send(error))
80 | }
81 | }
82 |
83 | function toString (value) {
84 | if (Object.prototype.toString.call(value.query) === '[object String]') {
85 | return value
86 | }
87 |
88 | return {
89 | query: print(value.query)
90 | }
91 | }
92 |
93 | module.exports = ApolloServer
94 |
--------------------------------------------------------------------------------
/design/logo-negative.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/design/logo-primary.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # adonis-apollo-server
2 |
3 |
4 | :us: English | :brazil: Português do Brasil
5 |
6 |
7 | 
8 |
9 | > GraphQL implementation using Apollo Server for Adonis
10 |
11 | This package integrates Apollo GraphQL Server with the AdonisJS framework. It allows you to use Apollo server in your AdonisJS app.
12 |
13 | > **NOTE:** This package requires [@adonisjs/bodyparser](https://github.com/adonisjs/adonis-bodyparser) and [graphql](https://github.com/graphql/graphql-js)
14 |
15 | ## Installation
16 |
17 | ```bash
18 | yarn add --exact @lukinco/adonis-apollo-server
19 | ```
20 |
21 | ### Registering provider
22 |
23 | Make sure to register the provider inside `start/app.js` file.
24 |
25 | ```js
26 | const providers = [
27 | '@lukinco/adonis-apollo-server/providers/ApolloServerProvider'
28 | ]
29 | ```
30 |
31 | That's all!
32 |
33 | ## Usage
34 |
35 | Now you can use the provider by pulling it from IoC container
36 |
37 | ```js
38 | // start/routes.js
39 |
40 | 'use strict'
41 |
42 | const Route = use('Route')
43 | const ApolloServer = use('ApolloServer')
44 |
45 | const typeDefs = `
46 | type Query {
47 | testString: String
48 | }
49 | `
50 |
51 | const resolvers = {
52 | Query: {
53 | testString () {
54 | return 'Seems to be working!'
55 | }
56 | }
57 | }
58 |
59 | const schema = { typeDefs, resolvers }
60 |
61 | Route.post('/graphql', ({ auth, request, response }) => {
62 | return ApolloServer.graphql({
63 | options: {
64 | schema,
65 | context: { auth }
66 | },
67 | request,
68 | response,
69 | onError: (errors) => {
70 | // this function is optional. "errors" is an array of all errors.
71 | // You may show the errors the way you want to.
72 | // If this function is defined, you must return an array of errors.
73 | }
74 | })
75 | })
76 |
77 | Route.get('/graphiql', ({ request, response }) => {
78 | return ApolloServer.graphiql({
79 | options: { endpointURL: '/graphql' },
80 | request,
81 | response
82 | })
83 | })
84 | ```
85 |
86 | ## Uploads
87 |
88 | To make uploads, you must set `autoProcess: false` on `config/bodyParser.js`.
89 |
90 | You must define the field that will receive the uploaded file with scalar type `Upload`:
91 |
92 | ```gql
93 | type Mutation {
94 | upload (file: Upload!): String
95 | }
96 | ```
97 |
98 | #### Usage
99 |
100 | You may create a helper file in `app/Helpers/upload.js`:
101 |
102 | ```js
103 | 'use strict'
104 |
105 | const fs = require('fs')
106 | const { join } = require('path')
107 | const slugify = require('slugify')
108 | const { v4: uuidv4 } = require('uuid')
109 | const Env = use('Env')
110 |
111 | const UPLOAD_DIRNAME = 'uploads'
112 | const FULL_UPLOAD_PATH = join(process.cwd(), 'public', UPLOAD_DIRNAME)
113 |
114 | fs.mkdir(FULL_UPLOAD_PATH, { recursive: true }, () => {})
115 |
116 | module.exports = async (file) => {
117 | const { createReadStream, filename } = await file
118 | const fileStream = createReadStream()
119 | const newFilename = `${uuidv4()}-${slugify(filename, { lower: true })}`
120 |
121 | const pathToUploadFile = join(FULL_UPLOAD_PATH, newFilename)
122 | const uploadDistStream = fs.createWriteStream(pathToUploadFile)
123 |
124 | await new Promise((resolve, reject) => {
125 | uploadDistStream.on('finish', resolve)
126 |
127 | uploadDistStream.on('error', (error) => {
128 | console.log('error', error)
129 | fs.unlink(pathToUploadFile, () => reject(error))
130 | })
131 |
132 | fileStream.on('error', (error) => uploadDistStream.destroy(error))
133 |
134 | fileStream.pipe(uploadDistStream)
135 | })
136 |
137 | return `${Env.get('UPLOAD_HOST')}/${UPLOAD_DIRNAME}/${newFilename}`
138 | }
139 | ```
140 |
141 | Libs `slugify` and `uuid` are optional, but they will help you to create unique filenames.
142 |
143 | You can use an env var `UPLOAD_HOST`, like my example, if you want to upload files in different places by environment.
144 |
145 | Then, when you want to make an upload, you just have to use that function, passing the file received as argument, and the function will return a Promise, that resolves to a full path of the file uploaded.
146 |
147 | To send a file, look the next examples.
148 |
149 | #### How to test using Insomnia
150 |
151 | On Insomnia, you must use Multipart requests, and send 3 fields:
152 |
153 | - `operations`: a JSON with `query` and `variables` keys:
154 |
155 | ```json
156 | {
157 | "query": "mutation ($file: Upload!) {\n upload (file: $file) \n}",
158 | "variables": {
159 | "file": null
160 | }
161 | }
162 | ```
163 |
164 | - `map`: a JSON mapping the file that will be upload with your variables set in `operations`:
165 |
166 | ```json
167 | {
168 | "0": ["variables.file"]
169 | }
170 | ```
171 |
172 | - `0`: the file itself. `0` is used here because we set in `map` above that `0` will point to our `variables.file` entry.
173 |
174 | That's it :)
175 |
176 | #### How to write tests
177 |
178 | In Adonis v4, you have to install `axios` and `form-data` libraries to make the upload work on server side.
179 |
180 | Then, you can use a helper to upload file like this:
181 |
182 | ```js
183 | const fs = require('fs')
184 | const gql = require('graphql-tag')
185 | const { print } = require('graphql/language/printer')
186 | const axios = require('axios')
187 | const FormData = require('form-data')
188 | const Env = use('Env')
189 |
190 | async function requestQuery ({ client, token, query, variables, file }) {
191 | if (token) {
192 | response = response.header('Authorization', `Bearer ${token}`)
193 | }
194 |
195 | const operations = {
196 | query: print(query),
197 | variables,
198 | }
199 |
200 | file = typeof file === 'string'
201 | ? { field: 'file', attach: file }
202 | : file
203 |
204 | const variableInput = Object.keys(variables)[0] === file.field
205 | ? `${file.field}`
206 | : `${Object.keys(variables)[0]}.${file.field}`
207 |
208 | const map = {
209 | 0: [`variables.${variableInput}`],
210 | }
211 |
212 | const form = new FormData()
213 | form.append('operations', JSON.stringify(operations))
214 | form.append('map', JSON.stringify(map))
215 | form.append('0', fs.createReadStream(file.attach))
216 |
217 | return axios
218 | .post(`${Env.get('APP_URL')}/graphql`, form, {
219 | headers: {
220 | authorization: `Bearer ${token}`,
221 | ...form.getHeaders(),
222 | },
223 | })
224 | }
225 | ```
226 |
227 | Then, just write your test:
228 |
229 | ```js
230 | const fs = require('fs')
231 | const { test, trait } = use('Test/Suite')('Upload')
232 | const Helpers = use('Helpers')
233 | const gql = require('graphql-tag')
234 | const { expect } = require('chai')
235 |
236 | trait('Test/ApiClient')
237 | trait('Auth/Client')
238 |
239 | const UPLOAD = gql`
240 | mutation ($file: Upload!) {
241 | upload (file: $file) {
242 | location
243 | }
244 | }
245 | `
246 |
247 | test('Should upload an image with mimetype PNG', async ({ client }) => {
248 | const file = Helpers.tmpPath('file.png')
249 | fs.writeFile(file, 'TEST', () => {})
250 |
251 | const response = await requestQuery({
252 | client,
253 | token, // use a token if you only want allow uploads for logged in users
254 | query: UPLOAD,
255 | variables: { file: null },
256 | file,
257 | })
258 |
259 | const result = response.data
260 |
261 | expect(response.status).to.be.equal(200)
262 | expect(result).to.have.keys('data')
263 | expect(result.data).to.have.keys(['upload'])
264 | expect(result.data.upload).to.be.a('string')
265 | })
266 | ```
267 |
268 | ## License
269 |
270 | MIT
271 |
--------------------------------------------------------------------------------
/docs/pt-br.md:
--------------------------------------------------------------------------------
1 | # adonis-apollo-server
2 |
3 |
4 | :us: English | :brazil: Português do Brasil
5 |
6 |
7 | 
8 |
9 | > Implementação do GraphQL usando Apollo Server para Adonis
10 |
11 | Este pacote integra o Apollo GraphQL Server com o framework AdonisJS. Ele permite que você use o Apollo Server em sua aplicação AdonisJS.
12 |
13 | > **NOTA:** Este pacote requer [@adonisjs/bodyparser](https://github.com/adonisjs/adonis-bodyparser) e [graphql](https://github.com/graphql/graphql-js)
14 |
15 | ## Instalação
16 |
17 | ```bash
18 | yarn add --exact @lukinco/adonis-apollo-server
19 | ```
20 |
21 | ### Registrando provider
22 |
23 | Tenha certeza de registrar o provider no arquivo `start/app.js`.
24 |
25 | ```js
26 | const providers = [
27 | '@lukinco/adonis-apollo-server/providers/ApolloServerProvider'
28 | ];
29 | ```
30 |
31 | Isso é tudo!
32 |
33 | ## Uso
34 |
35 | Agora você pode usar o provider pegando ele do container IoC
36 |
37 | ```js
38 | // start/routes.js
39 |
40 | 'use strict'
41 |
42 | const Route = use('Route')
43 | const ApolloServer = use('ApolloServer')
44 |
45 | const typeDefs = `
46 | type Query {
47 | testString: String
48 | }
49 | `
50 |
51 | const resolvers = {
52 | Query: {
53 | testString () {
54 | return 'Seems to be working!'
55 | }
56 | }
57 | }
58 |
59 | const schema = { typeDefs, resolvers }
60 |
61 | Route.post('/graphql', ({ auth, request, response }) => {
62 | return ApolloServer.graphql({
63 | options: {
64 | schema,
65 | context: { auth }
66 | },
67 | request,
68 | response,
69 | onError: (errors) => {
70 | // Esta função é opcional. "errors" é um array de todos os erros.
71 | // Você pode mostrar os erros da maneira que quiser.
72 | // Se esta função for definida, você precisa retornar um array de erros.
73 | }
74 | })
75 | })
76 |
77 | Route.get('/graphiql', ({ request, response }) => {
78 | return ApolloServer.graphiql({
79 | options: { endpointURL: '/graphql' },
80 | request,
81 | response
82 | })
83 | })
84 | ```
85 |
86 | ## Uploads
87 |
88 | Para fazer uploads, você precisa configurar `autoProcess: false` em `config/bodyParser.js`.
89 |
90 | Você precisa definir o campo que vai receber o arquivo do upload com o tipo escalar `Upload`:
91 |
92 | ```gql
93 | type Mutation {
94 | upload (file: Upload!): String
95 | }
96 | ```
97 |
98 | #### Uso
99 |
100 | Você pode criar um arquivo 'helper' em `app/Helpers/upload.js`:
101 |
102 | ```js
103 | 'use strict'
104 |
105 | const fs = require('fs')
106 | const { join } = require('path')
107 | const slugify = require('slugify')
108 | const { v4: uuidv4 } = require('uuid')
109 | const Env = use('Env')
110 |
111 | const UPLOAD_DIRNAME = 'uploads'
112 | const FULL_UPLOAD_PATH = join(process.cwd(), 'public', UPLOAD_DIRNAME)
113 |
114 | fs.mkdir(FULL_UPLOAD_PATH, { recursive: true }, () => {})
115 |
116 | module.exports = async (file) => {
117 | const { createReadStream, filename } = await file
118 | const fileStream = createReadStream()
119 | const newFilename = `${uuidv4()}-${slugify(filename, { lower: true })}`
120 |
121 | const pathToUploadFile = join(FULL_UPLOAD_PATH, newFilename)
122 | const uploadDistStream = fs.createWriteStream(pathToUploadFile)
123 |
124 | await new Promise((resolve, reject) => {
125 | uploadDistStream.on('finish', resolve)
126 |
127 | uploadDistStream.on('error', (error) => {
128 | console.log('error', error)
129 | fs.unlink(pathToUploadFile, () => reject(error))
130 | })
131 |
132 | fileStream.on('error', (error) => uploadDistStream.destroy(error))
133 |
134 | fileStream.pipe(uploadDistStream)
135 | })
136 |
137 | return `${Env.get('UPLOAD_HOST')}/${UPLOAD_DIRNAME}/${newFilename}`
138 | }
139 | ```
140 |
141 | As bibliotecas `slugify` e `uuid` são opcionais, mas elas vão ajudar você a criar nomes únicos de arquivos.
142 |
143 | Você pode usar uma variável env `UPLOAD_HOST`, como no exemplo, se você quer fazer upload de arquivos em diferentes ambientes.
144 |
145 | Então, quando você quiser fazer um upload, você só tem que usar esta função, passando o arquivo recebido como argumento, e a função vai retornar uma Promise, que resolve o caminho completo do arquivo carregado.
146 |
147 | Para enviar um arquivo, dê uma olhada nos próximos exemplos.
148 |
149 | ### Como testar usando Insomnia
150 |
151 | No Insomnia, você precisa usar requisições Multipart, e enviar 3 campos:
152 |
153 | - `operations`: um JSON com chaves `query` e `variables`:
154 |
155 | ```json
156 | {
157 | "query": "mutation ($file: Upload!) {\n upload (file: $file) \n}",
158 | "variables": {
159 | "file": null
160 | }
161 | }
162 | ```
163 |
164 | - `map`: um JSON mapeando o arquivo que vai ser carregado com suas variáveis configuradas em `operations`:
165 |
166 |
167 | ```json
168 | {
169 | "0": ["variables.file"]
170 | }
171 | ```
172 |
173 | - `0`: o arquivo em si. `0` é usado aqui porque nós configuramos no `map` acima que `0` vai apontar para nossa entrada `variables.file`.
174 |
175 | É isso :)
176 |
177 | #### Como escrever testes
178 |
179 | No Adonis v4, você precisa instalar as bibliotecas `axios` e `form-data` para fazer o upload funcionar no lado do servidor.
180 |
181 | Então, você pode usar um helper para upload, como este:
182 |
183 | ```js
184 | const fs = require('fs')
185 | const gql = require('graphql-tag')
186 | const { print } = require('graphql/language/printer')
187 | const axios = require('axios')
188 | const FormData = require('form-data')
189 | const Env = use('Env')
190 |
191 | async function requestQuery ({ client, token, query, variables, file }) {
192 | if (token) {
193 | response = response.header('Authorization', `Bearer ${token}`)
194 | }
195 |
196 | const operations = {
197 | query: print(query),
198 | variables,
199 | }
200 |
201 | file = typeof file === 'string'
202 | ? { field: 'file', attach: file }
203 | : file
204 |
205 | const variableInput = Object.keys(variables)[0] === file.field
206 | ? `${file.field}`
207 | : `${Object.keys(variables)[0]}.${file.field}`
208 |
209 | const map = {
210 | 0: [`variables.${variableInput}`],
211 | }
212 |
213 | const form = new FormData()
214 | form.append('operations', JSON.stringify(operations))
215 | form.append('map', JSON.stringify(map))
216 | form.append('0', fs.createReadStream(file.attach))
217 |
218 | return axios
219 | .post(`${Env.get('APP_URL')}/graphql`, form, {
220 | headers: {
221 | authorization: `Bearer ${token}`,
222 | ...form.getHeaders(),
223 | },
224 | })
225 | }
226 | ```
227 |
228 | Então, simplesmente escreva seu teste:
229 |
230 | ```js
231 | const fs = require('fs')
232 | const { test, trait } = use('Test/Suite')('Upload')
233 | const Helpers = use('Helpers')
234 | const gql = require('graphql-tag')
235 | const { expect } = require('chai')
236 |
237 | trait('Test/ApiClient')
238 | trait('Auth/Client')
239 |
240 | const UPLOAD = gql`
241 | mutation ($file: Upload!) {
242 | upload (file: $file) {
243 | location
244 | }
245 | }
246 | `
247 |
248 | test('Should upload an image with mimetype PNG', async ({ client }) => {
249 | const file = Helpers.tmpPath('file.png')
250 | fs.writeFile(file, 'TEST', () => {})
251 |
252 | const response = await requestQuery({
253 | client,
254 | token, // use um token se você quer permitir uploads somente para usuários autenticados
255 | query: UPLOAD,
256 | variables: { file: null },
257 | file,
258 | })
259 |
260 | const result = response.data
261 |
262 | expect(response.status).to.be.equal(200)
263 | expect(result).to.have.keys('data')
264 | expect(result.data).to.have.keys(['upload'])
265 | expect(result.data.upload).to.be.a('string')
266 | })
267 | ```
268 |
269 | ## Licença
270 |
271 | MIT
272 |
--------------------------------------------------------------------------------