diff --git a/README.md b/README.md index 548e28cf..cb0cb397 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,20 @@ import { OpenAPI } from './generated'; OpenAPI.TOKEN = 'some-bearer-token'; ``` +Alternatively, we also support an async method that provides the token for each request. +You can simply assign this method to the same `TOKEN `property in the global OpenAPI object. + +```typescript +import { OpenAPI } from './generated'; + +const getToken = async () => { + // Some code that requests a token... + return 'SOME_TOKEN'; +} + +OpenAPI.TOKEN = getToken; +``` + ### Compare to other generators Depending on which swagger generator you use, you will see different output. diff --git a/package.json b/package.json index 4ee3ddb3..5bd30858 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@beatgig/openapi-typescript-codegen", - "version": "0.5.0-async-token.2", + "name": "openapi-typescript-codegen", + "version": "0.5.1", "description": "NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.", "author": "Ferdi Koomen", "homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen", diff --git a/src/templates/core/fetch/getHeaders.hbs b/src/templates/core/fetch/getHeaders.hbs index 9bf669f5..f49610d0 100644 --- a/src/templates/core/fetch/getHeaders.hbs +++ b/src/templates/core/fetch/getHeaders.hbs @@ -3,9 +3,8 @@ async function getHeaders(options: ApiRequestOptions): Promise { Accept: 'application/json', ...options.headers, }); - - const token = typeof OpenAPI.TOKEN === 'function' ? await OpenAPI.TOKEN() : OpenAPI.TOKEN; + const token = await getToken(); if (isDefined(token) && token !== '') { headers.append('Authorization', `Bearer ${token}`); } diff --git a/src/templates/core/fetch/request.hbs b/src/templates/core/fetch/request.hbs index aefe8c6a..322ddb56 100644 --- a/src/templates/core/fetch/request.hbs +++ b/src/templates/core/fetch/request.hbs @@ -23,6 +23,9 @@ import { OpenAPI } from './OpenAPI'; {{>functions/getFormData}} +{{>functions/getToken}} + + {{>fetch/getHeaders}} diff --git a/src/templates/core/functions/getToken.hbs b/src/templates/core/functions/getToken.hbs new file mode 100644 index 00000000..7619004c --- /dev/null +++ b/src/templates/core/functions/getToken.hbs @@ -0,0 +1,6 @@ +async function getToken(): Promise { + if (typeof OpenAPI.TOKEN === 'function') { + return OpenAPI.TOKEN(); + } + return OpenAPI.TOKEN; +} diff --git a/src/templates/core/node/getHeaders.hbs b/src/templates/core/node/getHeaders.hbs index d7363845..b8d4dc77 100644 --- a/src/templates/core/node/getHeaders.hbs +++ b/src/templates/core/node/getHeaders.hbs @@ -1,11 +1,12 @@ -function getHeaders(options: ApiRequestOptions): Headers { +async function getHeaders(options: ApiRequestOptions): Promise { const headers = new Headers({ Accept: 'application/json', ...options.headers, }); - if (isDefined(OpenAPI.TOKEN) && OpenAPI.TOKEN !== '') { - headers.append('Authorization', `Bearer ${OpenAPI.TOKEN}`); + const token = await getToken(); + if (isDefined(token) && token !== '') { + headers.append('Authorization', `Bearer ${token}`); } if (options.body) { diff --git a/src/templates/core/node/request.hbs b/src/templates/core/node/request.hbs index 73969e30..8d916521 100644 --- a/src/templates/core/node/request.hbs +++ b/src/templates/core/node/request.hbs @@ -27,6 +27,9 @@ import { OpenAPI } from './OpenAPI'; {{>functions/getFormData}} +{{>functions/getToken}} + + {{>node/getHeaders}} diff --git a/src/templates/core/node/sendRequest.hbs b/src/templates/core/node/sendRequest.hbs index 98424249..e0a296e0 100644 --- a/src/templates/core/node/sendRequest.hbs +++ b/src/templates/core/node/sendRequest.hbs @@ -1,7 +1,7 @@ async function sendRequest(options: ApiRequestOptions, url: string): Promise { const request: RequestInit = { method: options.method, - headers: getHeaders(options), + headers: await getHeaders(options), body: getRequestBody(options), }; return await fetch(url, request); diff --git a/src/templates/core/xhr/getHeaders.hbs b/src/templates/core/xhr/getHeaders.hbs index 3d65e35e..f49610d0 100644 --- a/src/templates/core/xhr/getHeaders.hbs +++ b/src/templates/core/xhr/getHeaders.hbs @@ -1,11 +1,12 @@ -function getHeaders(options: ApiRequestOptions): Headers { +async function getHeaders(options: ApiRequestOptions): Promise { const headers = new Headers({ Accept: 'application/json', ...options.headers, }); - if (isDefined(OpenAPI.TOKEN) && OpenAPI.TOKEN !== '') { - headers.append('Authorization', `Bearer ${OpenAPI.TOKEN}`); + const token = await getToken(); + if (isDefined(token) && token !== '') { + headers.append('Authorization', `Bearer ${token}`); } if (options.body) { diff --git a/src/templates/core/xhr/request.hbs b/src/templates/core/xhr/request.hbs index bdbab10e..4d21325a 100644 --- a/src/templates/core/xhr/request.hbs +++ b/src/templates/core/xhr/request.hbs @@ -26,6 +26,9 @@ import { OpenAPI } from './OpenAPI'; {{>functions/getFormData}} +{{>functions/getToken}} + + {{>fetch/getHeaders}} diff --git a/src/templates/core/xhr/sendRequest.hbs b/src/templates/core/xhr/sendRequest.hbs index 9eef0b4f..a597407e 100644 --- a/src/templates/core/xhr/sendRequest.hbs +++ b/src/templates/core/xhr/sendRequest.hbs @@ -1,21 +1,21 @@ -function sendRequest(options: ApiRequestOptions, url: string): Promise { +async function sendRequest(options: ApiRequestOptions, url: string): Promise { + + const xhr = new XMLHttpRequest(); + xhr.open(options.method, url, true); + xhr.withCredentials = OpenAPI.WITH_CREDENTIALS; + + const headers = await getHeaders(options); + headers.forEach((value: string, key: string) => { + xhr.setRequestHeader(key, value); + }); + return new Promise((resolve, reject) => { try { - const xhr = new XMLHttpRequest(); - xhr.open(options.method, url, true); - xhr.withCredentials = OpenAPI.WITH_CREDENTIALS; - - const headers = getHeaders(options); - headers.forEach((value: string, key: string) => { - xhr.setRequestHeader(key, value); - }); - xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { resolve(xhr); } }; - xhr.send(getRequestBody(options)); } catch (error) { reject(error); diff --git a/src/utils/registerHandlebarTemplates.ts b/src/utils/registerHandlebarTemplates.ts index dcc4be20..e555eb1b 100644 --- a/src/utils/registerHandlebarTemplates.ts +++ b/src/utils/registerHandlebarTemplates.ts @@ -12,6 +12,7 @@ import fetchSendRequest from '../templates/core/fetch/sendRequest.hbs'; import functionCatchErrors from '../templates/core/functions/catchErrors.hbs'; import functionGetFormData from '../templates/core/functions/getFormData.hbs'; import functionGetQueryString from '../templates/core/functions/getQueryString.hbs'; +import functionGetToken from '../templates/core/functions/getToken.hbs'; import functionGetUrl from '../templates/core/functions/getUrl.hbs'; import functionIsBinary from '../templates/core/functions/isBinary.hbs'; import functionIsBlob from '../templates/core/functions/isBlob.hbs'; @@ -131,6 +132,7 @@ export function registerHandlebarTemplates(): Templates { // Generic functions used in 'request' file @see src/templates/core/request.hbs for more info Handlebars.registerPartial('functions/catchErrors', Handlebars.template(functionCatchErrors)); Handlebars.registerPartial('functions/getFormData', Handlebars.template(functionGetFormData)); + Handlebars.registerPartial('functions/getToken', Handlebars.template(functionGetToken)); Handlebars.registerPartial('functions/getQueryString', Handlebars.template(functionGetQueryString)); Handlebars.registerPartial('functions/getUrl', Handlebars.template(functionGetUrl)); Handlebars.registerPartial('functions/isBinary', Handlebars.template(functionIsBinary)); diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.js.snap index 840010cd..7f3ade70 100644 --- a/test/__snapshots__/index.spec.js.snap +++ b/test/__snapshots__/index.spec.js.snap @@ -61,7 +61,7 @@ interface Config { BASE: string; VERSION: string; WITH_CREDENTIALS: boolean; - TOKEN: string; + TOKEN: string | (() => Promise); } export const OpenAPI: Config = { @@ -134,14 +134,22 @@ function getFormData(params: Record): FormData { return formData; } -function getHeaders(options: ApiRequestOptions): Headers { +async function getToken(): Promise { + if (typeof OpenAPI.TOKEN === 'function') { + return OpenAPI.TOKEN(); + } + return OpenAPI.TOKEN; +} + +async function getHeaders(options: ApiRequestOptions): Promise { const headers = new Headers({ Accept: 'application/json', ...options.headers, }); - if (isDefined(OpenAPI.TOKEN) && OpenAPI.TOKEN !== '') { - headers.append('Authorization', \`Bearer \${OpenAPI.TOKEN}\`); + const token = await getToken(); + if (isDefined(token) && token !== '') { + headers.append('Authorization', \`Bearer \${token}\`); } if (options.body) { @@ -173,7 +181,7 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined { async function sendRequest(options: ApiRequestOptions, url: string): Promise { const request: RequestInit = { method: options.method, - headers: getHeaders(options), + headers: await getHeaders(options), body: getRequestBody(options), }; return await fetch(url, request); @@ -2096,7 +2104,7 @@ interface Config { BASE: string; VERSION: string; WITH_CREDENTIALS: boolean; - TOKEN: string; + TOKEN: string | (() => Promise); } export const OpenAPI: Config = { @@ -2169,14 +2177,22 @@ function getFormData(params: Record): FormData { return formData; } -function getHeaders(options: ApiRequestOptions): Headers { +async function getToken(): Promise { + if (typeof OpenAPI.TOKEN === 'function') { + return OpenAPI.TOKEN(); + } + return OpenAPI.TOKEN; +} + +async function getHeaders(options: ApiRequestOptions): Promise { const headers = new Headers({ Accept: 'application/json', ...options.headers, }); - if (isDefined(OpenAPI.TOKEN) && OpenAPI.TOKEN !== '') { - headers.append('Authorization', \`Bearer \${OpenAPI.TOKEN}\`); + const token = await getToken(); + if (isDefined(token) && token !== '') { + headers.append('Authorization', \`Bearer \${token}\`); } if (options.body) { @@ -2208,7 +2224,7 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined { async function sendRequest(options: ApiRequestOptions, url: string): Promise { const request: RequestInit = { method: options.method, - headers: getHeaders(options), + headers: await getHeaders(options), body: getRequestBody(options), }; return await fetch(url, request); diff --git a/test/e2e/scripts/browser.js b/test/e2e/scripts/browser.js index ca0dc6de..40b1d6e0 100644 --- a/test/e2e/scripts/browser.js +++ b/test/e2e/scripts/browser.js @@ -27,8 +27,8 @@ async function stop() { await browser.close(); } -async function evaluate(fn) { - return await page.evaluate(fn); +async function evaluate(fn, ...args) { + return await page.evaluate(fn, args); } module.exports = { diff --git a/test/e2e/v2.babel.spec.js b/test/e2e/v2.babel.spec.js index c9f3a880..b133573b 100644 --- a/test/e2e/v2.babel.spec.js +++ b/test/e2e/v2.babel.spec.js @@ -21,6 +21,18 @@ describe('v2.fetch', () => { await browser.stop(); }); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { const result = await browser.evaluate(async () => { return await window.api.ComplexService.complexTypes({ diff --git a/test/e2e/v2.fetch.spec.js b/test/e2e/v2.fetch.spec.js index eae92e03..e881e361 100644 --- a/test/e2e/v2.fetch.spec.js +++ b/test/e2e/v2.fetch.spec.js @@ -21,6 +21,18 @@ describe('v2.fetch', () => { await browser.stop(); }); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { const result = await browser.evaluate(async () => { return await window.api.ComplexService.complexTypes({ diff --git a/test/e2e/v2.node.spec.js b/test/e2e/v2.node.spec.js index 5dc2ca6b..9b602e5c 100644 --- a/test/e2e/v2.node.spec.js +++ b/test/e2e/v2.node.spec.js @@ -17,8 +17,17 @@ describe('v2.node', () => { await server.stop(); }); + it('requests token', async () => { + const { OpenAPI, SimpleService } = require('./generated/v2/node/index.js'); + const tokenRequest = jest.fn().mockResolvedValue('MY_TOKEN') + OpenAPI.TOKEN = tokenRequest; + const result = await SimpleService.getCallWithoutParametersAndResponse(); + expect(tokenRequest.mock.calls.length).toBe(1); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { - const {ComplexService} = require('./generated/v2/node/index.js'); + const { ComplexService } = require('./generated/v2/node/index.js'); const result = await ComplexService.complexTypes({ first: { second: { diff --git a/test/e2e/v2.xhr.spec.js b/test/e2e/v2.xhr.spec.js index 595f411b..590b226f 100644 --- a/test/e2e/v2.xhr.spec.js +++ b/test/e2e/v2.xhr.spec.js @@ -21,6 +21,18 @@ describe('v2.xhr', () => { await browser.stop(); }); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { const result = await browser.evaluate(async () => { return await window.api.ComplexService.complexTypes({ diff --git a/test/e2e/v3.babel.spec.js b/test/e2e/v3.babel.spec.js index 6c877cc5..dd378882 100644 --- a/test/e2e/v3.babel.spec.js +++ b/test/e2e/v3.babel.spec.js @@ -21,8 +21,16 @@ describe('v3.fetch', () => { await browser.stop(); }); - it('runs', async () => { - expect(true).toBeTruthy(); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); }); it('complexService', async () => { diff --git a/test/e2e/v3.fetch.spec.js b/test/e2e/v3.fetch.spec.js index b5d45ba6..e3df5a4b 100644 --- a/test/e2e/v3.fetch.spec.js +++ b/test/e2e/v3.fetch.spec.js @@ -21,8 +21,16 @@ describe('v3.fetch', () => { await browser.stop(); }); - it('runs', async () => { - expect(true).toBeTruthy(); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); }); it('complexService', async () => { diff --git a/test/e2e/v3.node.spec.js b/test/e2e/v3.node.spec.js index 7b026e65..26e2fbe8 100644 --- a/test/e2e/v3.node.spec.js +++ b/test/e2e/v3.node.spec.js @@ -17,8 +17,17 @@ describe('v3.node', () => { await server.stop(); }); + it('requests token', async () => { + const { OpenAPI, SimpleService } = require('./generated/v2/node/index.js'); + const tokenRequest = jest.fn().mockResolvedValue('MY_TOKEN') + OpenAPI.TOKEN = tokenRequest; + const result = await SimpleService.getCallWithoutParametersAndResponse(); + expect(tokenRequest.mock.calls.length).toBe(1); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { - const {ComplexService} = require('./generated/v3/node/index.js'); + const { ComplexService } = require('./generated/v3/node/index.js'); const result = await ComplexService.complexTypes({ first: { second: { diff --git a/test/e2e/v3.xhr.spec.js b/test/e2e/v3.xhr.spec.js index 1cad0068..3c1f4324 100644 --- a/test/e2e/v3.xhr.spec.js +++ b/test/e2e/v3.xhr.spec.js @@ -21,6 +21,18 @@ describe('v3.xhr', () => { await browser.stop(); }); + it('requests token', async () => { + const result = await browser.evaluate(async () => { + window.api.OpenAPI.TOKEN = new Promise(resolve => { + setTimeout(() => { + resolve('MY_TOKEN'); + }, 500); + }); + return await window.api.SimpleService.getCallWithoutParametersAndResponse(); + }); + expect(result.headers.authorization).toBe('Bearer MY_TOKEN'); + }); + it('complexService', async () => { const result = await browser.evaluate(async () => { return await window.api.ComplexService.complexTypes({