Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smaller footprint #25

Open
wants to merge 2 commits into
base: typescript-axios
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public boolean getHasQueryParams() {
return nonempty(queryParams);
}

/**
* Check if there's body, form, query, or path params
*
* @return true if any exist, false otherwise
*/
public boolean getHasInputParams() {
return getHasFormParams() || getHasBodyParam() || getHasQueryParams() || getHasPathParams();
}

/**
* Check if there's at least one header parameter
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public TypeScriptAxiosClientCodegen() {

modelTemplateFiles.put("model.mustache", ".ts");
apiTemplateFiles.put("api.mustache", ".ts");
supportingFiles.add(new SupportingFile("client.mustache", "", "client.ts"));

modelPackage = "models";
apiPackage = "resources";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{{>licenseInfo}}
import axios, { AxiosRequestConfig, Method } from 'axios';
import type { AxiosRequestConfig } from 'axios';

import { mamba } from '../index';

import { axios as axiosClient, MambaApiError } from '../axios.config';
{{#hasFormParams}}
import { stringify } from 'qs';
{{/hasFormParams}}
Expand All @@ -28,39 +29,40 @@ export class {{classname}}Resource {
{{#allParams}}{{^isCookieParam}}{{^isHeaderParam}}* @param {{paramName}} {{description}}
{{/isHeaderParam}}{{/isCookieParam}}{{/allParams}}
*/
public {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isCookieParam}}{{^isHeaderParam}}{{^isBodyParam}}{{paramName}}{{/isBodyParam}}{{#isBodyParam}}body{{/isBodyParam}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isHeaderParam}}{{/isCookieParam}}{{/isQueryParam}}{{/allParams}}{{#hasQueryParams}}query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}},{{/-last}} {{/queryParams}}}, {{/hasQueryParams}}axiosConfig?: AxiosRequestConfig): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}{}{{/returnType}}> {
const apiRoute = '{{path}}';
const reqPath = apiRoute{{#pathParams}}
.replace('{' + '{{baseName}}}', String({{paramName}}{{#isDateTime}}.toISOString(){{/isDateTime}})){{/pathParams}};
{{#hasFormParams}}
let reqFormParams = {
{{#formParams}}
{{baseName}}: {{paramName}}{{^-last}},{{/-last}}
{{/formParams}}
};
{{/hasFormParams}}
const reqConfig = {
...axiosConfig,
method: '{{httpMethod}}' as Method,
url: reqPath{{#hasQueryParams}},
params: query{{/hasQueryParams}}{{#bodyParam}},
data: body{{/bodyParam}}{{#hasFormParams}},
headers: { 'Content-Type': 'application/x-www-form-urlencoded'},
data: stringify(reqFormParams),
{{/hasFormParams}}
public {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isCookieParam}}{{^isHeaderParam}}{{^isBodyParam}}{{paramName}}{{/isBodyParam}}{{#isBodyParam}}body{{/isBodyParam}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isHeaderParam}}{{/isCookieParam}}{{/isQueryParam}}{{/allParams}}{{#hasQueryParams}}query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}},{{/-last}} {{/queryParams}}}, {{/hasQueryParams}}axiosConfig?: AxiosRequestConfig): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
const url = '{{httpMethod}} {{path}}' as const;


{{#hasInputParams}}
const input = {
{{#pathParams}}
{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}: {{paramName}},
{{/pathParams}}

{{#hasQueryParams}}
query,
{{/hasQueryParams}}

{{#hasBodyParam}}
data: body,
{{/hasBodyParam}}

{{#hasFormParams}}
form: {
{{#formParams}}
{{#lambda.snakecase_param}}{{baseName}}{{/lambda.snakecase_param}}: {{paramName}}{{^-last}},{{/-last}}
{{/formParams}}
}
{{/hasFormParams}}
};
return axiosClient.request(reqConfig)
.then(res => {
return res.data;
})
.catch(error => {
if (axios.isAxiosError(error)) {
throw new MambaApiError({ apiRoute, error });
} else {
throw error;
}
});

return mamba.request(url, input, axiosConfig);
{{/hasInputParams}}

{{^hasInputParams}}
return mamba.request(url, axiosConfig);
{{/hasInputParams}}

}

{{/operation}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/* tslint:disable */
/* eslint-disable */
{{>licenseInfo}}
import axios, { AxiosRequestConfig, Method, AxiosInstance, AxiosError } from 'axios';
import qs from 'qs';
import { HasRequiredKeys } from 'type-fest';


{{#models}}
{{#model}}import { {{classname}} } from './models/{{classname}}';{{/model}}
{{/models}}

type RequestInputOutput = {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{^hasInputParams}}
'{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}} {{{path}}}': [
input: never, response: {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
]
{{/hasInputParams}}
{{#hasInputParams}}
'{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}} {{{path}}}': [
input: {
{{#pathParams}}
{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}},
{{/pathParams}}

{{#hasQueryParams}}
query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}, {{/queryParams}}},
{{/hasQueryParams}}

{{#hasBodyParam}}
{{#bodyParam}}
data{{^required}}?{{/required}}: {{{dataType}}}{{/bodyParam}},
{{/hasBodyParam}}

{{#hasFormParams}}
form: { {{#formParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}, {{/formParams}}},
{{/hasFormParams}}
},
response: {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
]
{{/hasInputParams}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}

export class MambaApiError<TransformedData, RawData> extends AxiosError<
TransformedData,
RawData
> {
public apiRoute: string;
public name = 'MambaApiError';

constructor({
apiRoute,
error,
}: {
apiRoute: string;
error: AxiosError<TransformedData, RawData>;
}) {
super(
error.message,
error.code,
error.config,
error.request,
error.response
);

this.apiRoute = apiRoute;
}
}

interface MambaApiClientOptions {
baseUrl?: string;
headers?: Headers;
onError?: (error: any) => void;
}

export class MambaApiClient {
private _axios: AxiosInstance;

constructor(options?: MambaApiClientOptions) {
this._axios = axios.create({
baseURL: options?.baseUrl ?? undefined,
withCredentials: true,
paramsSerializer: (params: any) =>
qs.stringify(params, { indices: false }),
});
}

configure(options: MambaApiClientOptions) {
if (options.baseUrl) {
this._axios.defaults.baseURL = options.baseUrl;
}

if (options.headers) {
const headers = Object.fromEntries(options.headers.entries());
this._axios.defaults.headers.common = headers;
}

const { onError } = options;
if (onError) {
this._axios.interceptors.response.use(
(response) => response,
(error) => {
onError(error);

return Promise.reject(error);
}
);
}
}

async request<P extends keyof RequestInputOutput>(
path: P,
...args: RequestInputOutput[P][0] extends never
? [config?: AxiosRequestConfig]
: HasRequiredKeys<RequestInputOutput[P][0]> extends true
? [input: RequestInputOutput[P][0], config?: AxiosRequestConfig]
: [input?: RequestInputOutput[P][0], config?: AxiosRequestConfig]
): Promise<RequestInputOutput[P][1]> {
if (!this._axios.defaults.baseURL) {
throw new Error(
'No baseUrl set for MambaApiClient. Call configure() before making requests.'
);
}

let [method, url] = path.split(' ');

const [input = {}, extraConfig = {}] = args;
// @ts-expect-error
const { data, query, form, ...pathParams } = input;

if (pathParams) {
url = url.replace(/{(\w+)}/g, (match, key: string) => {
// @ts-expect-error
return pathParams[key] || match;
});
}

const config: AxiosRequestConfig = {
...extraConfig,
method: method as Method,
url,
params: query,
data,
}

if (form) {
config.headers = {
...config.headers,
'Content-Type': 'application/x-www-form-urlencoded',
};
}

return this._axios
.request(config)
.then((res) => {
return res.data;
})
.catch((error) => {
if (axios.isAxiosError(error)) {
throw new MambaApiError({ apiRoute: url, error });
} else {
throw error;
}
});
}

}