- Added switch for xhr anf fetch

This commit is contained in:
Ferdi Koomen 2019-11-22 17:21:54 +01:00
parent f3bc279825
commit 20209f7aa2
17 changed files with 87 additions and 56 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
src/templates

View File

@ -24,9 +24,9 @@ export enum HttpClient {
* given language it will generate the client, including the typed models, validation schemas,
* service layer, etc.
* @param input The relative location of the OpenAPI spec.
* @param output The relative location of the output directory
* @param language: The language that should be generated (Typescript or Javascript)
* @param httpClient: The selected httpClient (fetch or XHR)
* @param output The relative location of the output directory.
* @param language: The language that should be generated (Typescript or Javascript).
* @param httpClient: The selected httpClient (fetch or XHR).
*/
export function generate(input: string, output: string, language: Language = Language.TYPESCRIPT, httpClient: HttpClient = HttpClient.FETCH): void {
const inputPath = path.resolve(process.cwd(), input);
@ -52,13 +52,13 @@ export function generate(input: string, output: string, language: Language = Lan
// Generate and write version 2 client
if (openApiVersion === OpenApiVersion.V2) {
const clientV2 = parseV2(openApi);
writeClient(clientV2, language, templates, outputPath);
writeClient(clientV2, language, httpClient, templates, outputPath);
}
// Generate and write version 3 client
if (openApiVersion === OpenApiVersion.V3) {
const clientV3 = parseV3(openApi);
writeClient(clientV3, language, templates, outputPath);
writeClient(clientV3, language, httpClient, templates, outputPath);
}
}
} catch (e) {

View File

@ -7,6 +7,7 @@ import { isSuccess } from './isSuccess';
import { Result } from './Result';
export class ApiError extends Error {
public readonly url: string;
public readonly status: number;
public readonly statusText: string;
@ -41,20 +42,13 @@ export namespace ApiError {
*/
export function catchGenericError(result: Result): void {
switch (result.status) {
case 400:
throw new ApiError(result, ApiError.Message.BAD_REQUEST);
case 401:
throw new ApiError(result, ApiError.Message.UNAUTHORIZED);
case 403:
throw new ApiError(result, ApiError.Message.FORBIDDEN);
case 404:
throw new ApiError(result, ApiError.Message.NOT_FOUND);
case 500:
throw new ApiError(result, ApiError.Message.INTERNAL_SERVER_ERROR);
case 502:
throw new ApiError(result, ApiError.Message.BAD_GATEWAY);
case 503:
throw new ApiError(result, ApiError.Message.SERVICE_UNAVAILABLE);
case 400: throw new ApiError(result, ApiError.Message.BAD_REQUEST);
case 401: throw new ApiError(result, ApiError.Message.UNAUTHORIZED);
case 403: throw new ApiError(result, ApiError.Message.FORBIDDEN);
case 404: throw new ApiError(result, ApiError.Message.NOT_FOUND);
case 500: throw new ApiError(result, ApiError.Message.INTERNAL_SERVER_ERROR);
case 502: throw new ApiError(result, ApiError.Message.BAD_GATEWAY);
case 503: throw new ApiError(result, ApiError.Message.SERVICE_UNAVAILABLE);
}
if (!isSuccess(result.status)) {

View File

@ -4,6 +4,7 @@
/* prettier-ignore */
export interface RequestOptions {
type: 'fetch' | 'xhr';
method: string;
path: string;
headers?: { [key: string]: any };

View File

@ -8,6 +8,7 @@ import { getQueryString } from './getQueryString';
import { OpenAPI } from './OpenAPI';
import { RequestOptions } from './RequestOptions';
import { requestUsingFetch } from './requestUsingFetch';
import { requestUsingXHR } from './requestUsingXHR';
import { Result } from './Result';
/**
@ -16,6 +17,7 @@ import { Result } from './Result';
* @returns Result object (see above)
*/
export async function request<T = any>(options: Readonly<RequestOptions>): Promise<Result<T>> {
// Create the request URL
let url = `${OpenAPI.BASE}${options.path}`;
@ -46,6 +48,7 @@ export async function request<T = any>(options: Readonly<RequestOptions>): Promi
if (options.formData) {
request.body = getFormData(options.formData);
} else if (options.body) {
// If this is blob data, then pass it directly to the body and set content type.
// Otherwise we just convert request data to JSON string (needed for fetch api)
if (options.body instanceof Blob) {
@ -60,7 +63,12 @@ export async function request<T = any>(options: Readonly<RequestOptions>): Promi
}
try {
return await requestUsingFetch<T>(url, request);
switch (options.type) {
case 'fetch':
return await requestUsingFetch<T>(url, request);
case 'xhr':
return await requestUsingXHR<T>(url, request);
}
} catch (error) {
return {
url,

View File

@ -13,6 +13,7 @@ import { Result } from './Result';
* @param request The request object, containing method, headers, body, etc.
*/
export async function requestUsingFetch<T = any>(url: string, request: Readonly<RequestInit>): Promise<Result<T>> {
// Fetch response using fetch API.
const response = await fetch(url, request);

View File

@ -31,6 +31,7 @@ export async function requestUsingXHR<T = any>(url: string, request: Readonly<Re
// Register the readystate handler, this will fire when the request is done.
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
// Create result object.
const result: Result = {
url,

View File

@ -14,6 +14,7 @@ import { OpenAPI } from '../core/OpenAPI';
import { Result } from '../core/Result';
export class {{{name}}} {
{{#each operations}}
/**
{{#if deprecated}}
@ -30,10 +31,12 @@ export class {{{name}}} {
* @param {{{name}}} {{{description}}}
{{/each}}
{{/if}}
* @return {{{result}}}
*/
public static async {{{name}}}({{#each parameters}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}}{{#unless @last}}, {{/unless}}{{/each}}): Promise<{{{result}}}> {
const result: Result<{{{result}}}> = await request({
type: '{{{../httpClient}}}',
method: '{{{method}}}',
path: `{{{path}}}`,
{{#if parametersHeader}}

View File

@ -18,11 +18,13 @@ export interface Templates {
export function readHandlebarsTemplates(language: Language): Templates {
try {
registerHandlebarHelpers();
const templates: Templates = {
index: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/index.hbs`)),
model: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/model.hbs`)),
service: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/service.hbs`)),
};
Handlebars.registerPartial({
exportGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportGeneric.hbs`)),
exportReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportReference.hbs`)),
@ -45,6 +47,7 @@ export function readHandlebarsTemplates(language: Language): Templates {
typeForReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForReference.hbs`)),
typeForGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForGeneric.hbs`)),
});
return templates;
} catch (e) {
throw e;

View File

@ -6,18 +6,19 @@ import * as mkdirp from 'mkdirp';
import * as rimraf from 'rimraf';
import { Templates } from './readHandlebarsTemplates';
import { writeClientIndex } from './writeClientIndex';
import { Language } from '../index';
import { HttpClient, Language } from '../index';
import * as fs from 'fs';
import * as glob from 'glob';
/**
* Write our OpenAPI client, using the given templates at the given output path
* @param client: Client object with all the models, services, etc.
* @param language: The output language (Typescript or javascript).
* @param templates: Templates wrapper with all loaded Handlebars templates.
* Write our OpenAPI client, using the given templates at the given output path.
* @param client Client object with all the models, services, etc.
* @param language The language that should be generated (Typescript or Javascript).
* @param httpClient The selected httpClient (fetch or XHR).
* @param templates Templates wrapper with all loaded Handlebars templates.
* @param outputPath
*/
export function writeClient(client: Client, language: Language, templates: Templates, outputPath: string): void {
export function writeClient(client: Client, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void {
const outputPathCore = path.resolve(outputPath, 'core');
const outputPathModels = path.resolve(outputPath, 'models');
const outputPathServices = path.resolve(outputPath, 'services');
@ -52,8 +53,8 @@ export function writeClient(client: Client, language: Language, templates: Templ
// Write the client files
try {
writeClientIndex(client, language, templates, outputPath);
writeClientModels(client.models, language, templates, outputPathModels);
writeClientServices(client.services, language, templates, outputPathServices);
writeClientModels(client.models, language, httpClient, templates, outputPathModels);
writeClientServices(client.services, language, httpClient, templates, outputPathServices);
} catch (e) {
throw e;
}

View File

@ -11,10 +11,10 @@ import { Templates } from './readHandlebarsTemplates';
* Generate the OpenAPI client index file using the Handlebar template and write it to disk.
* The index file just contains all the exports you need to use the client as a standalone
* library. But yuo can also import individual models and services directly.
* @param client: Client object, containing, models, schemas and services.
* @param language: The output language (Typescript or javascript).
* @param templates: The loaded handlebar templates.
* @param outputPath:
* @param client Client object, containing, models, schemas and services.
* @param language The output language (Typescript or javascript).
* @param templates The loaded handlebar templates.
* @param outputPath
*/
export function writeClientIndex(client: Client, language: Language, templates: Templates, outputPath: string): void {
const fileName = getFileName('index', language);

View File

@ -1,7 +1,7 @@
import { writeClientModels } from './writeClientModels';
import * as fs from 'fs';
import { Model } from '../client/interfaces/Model';
import { Language } from '../index';
import { HttpClient, Language } from '../index';
import { Templates } from './readHandlebarsTemplates';
jest.mock('fs');
@ -33,7 +33,7 @@ describe('writeClientModels', () => {
model: () => 'dummy',
service: () => 'dummy',
};
writeClientModels(models, Language.TYPESCRIPT, templates, '/');
writeClientModels(models, Language.TYPESCRIPT, HttpClient.FETCH, templates, '/');
expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy');
});
});

View File

@ -1,7 +1,7 @@
import * as fs from 'fs';
import { Model } from '../client/interfaces/Model';
import * as path from 'path';
import { Language } from '../index';
import { HttpClient, Language } from '../index';
import { getFileName } from './getFileName';
import { exportModel } from './exportModel';
import { Templates } from './readHandlebarsTemplates';
@ -9,17 +9,22 @@ import { format } from './format';
/**
* Generate Models using the Handlebar template and write to disk.
* @param models: Array of Models to write.
* @param language: The output language (Typescript or javascript).
* @param templates: The loaded handlebar templates.
* @param outputPath:
* @param models Array of Models to write.
* @param language The output language (Typescript or javascript).
* @param httpClient The selected httpClient (fetch or XHR).
* @param templates The loaded handlebar templates.
* @param outputPath
*/
export function writeClientModels(models: Map<string, Model>, language: Language, templates: Templates, outputPath: string): void {
export function writeClientModels(models: Map<string, Model>, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void {
models.forEach(model => {
const fileName = getFileName(model.name, language);
try {
const templateData = exportModel(model);
const templateResult = templates.model(templateData);
const templateResult = templates.model({
language,
httpClient,
...templateData,
});
fs.writeFileSync(path.resolve(outputPath, fileName), format(templateResult));
} catch (e) {
throw new Error(`Could not write model: "${fileName}"`);

View File

@ -1,7 +1,7 @@
import { writeClientServices } from './writeClientServices';
import * as fs from 'fs';
import { Service } from '../client/interfaces/Service';
import { Language } from '../index';
import { HttpClient, Language } from '../index';
import { Templates } from './readHandlebarsTemplates';
jest.mock('fs');
@ -21,7 +21,7 @@ describe('writeClientServices', () => {
model: () => 'dummy',
service: () => 'dummy',
};
writeClientServices(services, Language.TYPESCRIPT, templates, '/');
writeClientServices(services, Language.TYPESCRIPT, HttpClient.FETCH, templates, '/');
expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy');
});
});

View File

@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { Service } from '../client/interfaces/Service';
import { Language } from '../index';
import { HttpClient, Language } from '../index';
import { getFileName } from './getFileName';
import { exportService } from './exportService';
import { Templates } from './readHandlebarsTemplates';
@ -9,17 +9,22 @@ import { format } from './format';
/**
* Generate Services using the Handlebar template and write to disk.
* @param services: Array of Services to write.
* @param language: The output language (Typescript or javascript).
* @param templates: The loaded handlebar templates.
* @param outputPath:
* @param services Array of Services to write.
* @param language The output language (Typescript or javascript).
* @param httpClient The selected httpClient (fetch or XHR).
* @param templates The loaded handlebar templates.
* @param outputPath
*/
export function writeClientServices(services: Map<string, Service>, language: Language, templates: Templates, outputPath: string): void {
export function writeClientServices(services: Map<string, Service>, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void {
services.forEach(service => {
const fileName = getFileName(service.name, language);
try {
const templateData = exportService(service);
const templateResult = templates.service(templateData);
const templateResult = templates.service({
language,
httpClient,
...templateData,
});
fs.writeFileSync(path.resolve(outputPath, fileName), format(templateResult));
} catch (e) {
throw new Error(`Could not write service: "${fileName}"`);

View File

@ -25,12 +25,12 @@ OpenAPI.generate(
// OpenAPI.HttpClient.FETCH,
// );
//
OpenAPI.generate(
'./test/mock/v2/test-sites.json',
'./test/tmp/v2/ts/test-sites',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH,
);
// OpenAPI.generate(
// './test/mock/v2/test-sites.json',
// './test/tmp/v2/ts/test-sites',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH,
// );
//
// OpenAPI.generate(
// './test/mock/v2/test-petstore.yaml',

View File

@ -10,6 +10,14 @@
"http"
],
"paths": {
"/api/v{api-version}/dummy": {
"get": {
"tags": [
"Service"
],
"operationId": "GetCallWithoutParametersAndResponse"
}
}
},
"definitions": {
"SimpleInteger": {