Skip to content

Commit

Permalink
impr: simplify processing path params
Browse files Browse the repository at this point in the history
and now templates are cleaner and shorter
And also mismatch between path expression and params will be much more visible
  • Loading branch information
yhnavein committed Jul 7, 2024
1 parent ac5221f commit 7ba815f
Show file tree
Hide file tree
Showing 17 changed files with 169 additions and 252 deletions.
41 changes: 34 additions & 7 deletions src/gen/genOperations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,6 @@ describe('prepareOperations', () => {
optional: true,
});

expect(res.pathParams.pop()).to.deep.include({
name: 'petId',
originalName: 'petId',
type: 'number',
optional: true,
});

expect(res.parameters.length).to.equal(3);
expect(res.parameters.map((p) => p.name)).to.deep.equal(['orgID', 'orgType', 'petId']);
});
Expand Down Expand Up @@ -105,6 +98,40 @@ describe('prepareOperations', () => {
expect(op2.parameters).to.deep.equal([]);
});

it('should prepare URL correctly', () => {
const ops: ApiOperation[] = [
{
operationId: 'get1',
method: 'get',
path: '/pet/{petId}',
responses: {},
group: null,
},
{
operationId: 'get2',
method: 'get',
path: '/users/{userId}/Wrong{/Path}',
parameters: [],
responses: {},
group: null,
},
{
operationId: 'get3',
method: 'get',
path: '/users/{}/Wrong{',
parameters: [],
responses: {},
group: null,
},
];

const [op1, op2, op3] = prepareOperations(ops, opts);

expect(op1.url).to.equal('/pet/${encodeURIComponent(`${petId}`)}');
expect(op2.url).to.equal('/users/${encodeURIComponent(`${userId}`)}/Wrong{/Path}');
expect(op3.url).to.equal('/users/{}/Wrong{');
});

describe('requestBody (JSON)', () => {
it('should handle requestBody with ref type', () => {
const ops: ApiOperation[] = [
Expand Down
17 changes: 14 additions & 3 deletions src/gen/genOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,28 @@ export function prepareOperations(
responseContentType,
method: op.method.toUpperCase(),
name: getOperationName(op.operationId, op.group),
url: op.path,
url: prepareUrl(op.path),
parameters: params,
query: queryParams,
pathParams: getParams(op.parameters as OA3.ParameterObject[], options, ['path']),
body,
headers: getParams(op.parameters as OA3.ParameterObject[], options, ['header']),
};
});
}

/**
* This function will replace path template expressions with ${encodeURIComponent('paramName')} placeholders
* The end result will be a string that is effectively a template (i.e. you should wrap end result with backticks)
* This method is not really safe, but it will point out the potential issues with the path template expressions
* So that the developer can see actual problem in the compiled code (as opposed to having a runtime issues)
*/
function prepareUrl(path: string): string {
return path.replace(
/{([^}/]+)}/g,
(_, paramName) => `\${encodeURIComponent(\`\${${paramName}}\`)}`
);
}

/**
* Let's add numbers to the duplicated operation names to avoid breaking code.
* Duplicated operation names are not allowed by the OpenAPI spec, but in the real world
Expand Down Expand Up @@ -209,7 +221,6 @@ interface IOperation {
url: string;
parameters: IOperationParam[];
query: IOperationParam[];
pathParams: IOperationParam[];
body: IBodyParam;
headers: IOperationParam[];
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/templateManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ describe('render', () => {
parameters: [],
name: 'TestName',
returnType: 'string',
responseContentType: 'json',
url: 'api/test',
pathParams: [],
method: 'GET',
formData: [],
body: null,
Expand Down
7 changes: 1 addition & 6 deletions templates/axios/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@
<% }); %>
$config?: AxiosRequestConfig
): AxiosPromise<<%~ it.returnType %>> {
let url = '<%= it.url %>';
<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
const url = `<%= it.url %>`;

return axios.request<<%~ it.returnType %>>({
url: url,
Expand Down
5 changes: 0 additions & 5 deletions templates/fetch/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@
$config?: RequestInit
): Promise<<%~ it.returnType %>> {
let url = `${defaults.baseUrl}<%= it.url %>?`;
<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
<% if(it.query && it.query.length > 0) { %>
<% it.query.forEach((parameter) => { %>
if (<%= parameter.name %> !== undefined) {
Expand Down
7 changes: 1 addition & 6 deletions templates/ng1/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
<% }); %>
config?: IRequestShortcutConfig
): IPromise<<%~ it.returnType %>> {
let url = '<%= it.url %>?';
<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
let url = `<%= it.url %>?`;
<% if(it.query && it.query.length > 0) { %>
<% it.query.forEach((parameter) => { %>
if (<%= parameter.name %> !== undefined) {
Expand Down
7 changes: 1 addition & 6 deletions templates/ng2/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@
<% }); %>
config?: any
): Observable<<%~ it.returnType %>> {
let url = '<%= it.url %>?';
<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
let url = `<%= it.url %>?`;
<% if(it.query && it.query.length > 0) { %>
<% it.query.forEach((parameter) => { %>
if (<%= parameter.name %> !== undefined && <%= parameter.name %> !== null) {
Expand Down
8 changes: 1 addition & 7 deletions templates/swr-axios/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@
<% }); %>
$config?: AxiosRequestConfig
): AxiosPromise<<%~ it.returnType %>> {
let url = '<%= it.url %>';

<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
const url = `<%= it.url %>`;

return axios.request<<%~ it.returnType %>>({
url: url,
Expand Down
8 changes: 1 addition & 7 deletions templates/swr-axios/swrOperation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,9 @@ export function <%= it.swrOpName %>(<% it.parameters.forEach((parameter) => { %>
<% }); %>
$config?: SwrConfig
) {
let url = '<%= it.url %>';
const url = `<%= it.url %>`;
const { axios: $axiosConf, key, ...config } = $config || {};

<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>

let cacheUrl = url + '?';
<% if(it.query && it.query.length > 0) { %>
<% it.query.forEach((parameter) => { %>
Expand Down
2 changes: 1 addition & 1 deletion templates/xior/barrel.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function serializeQueryParam(obj: any) {
if (obj instanceof Date) return obj.toJSON();
if (typeof obj !== 'object' || Array.isArray(obj)) return obj;
return Object.keys(obj)
.reduce((a: any, b) => a.push(b + '=' + obj[b]) && a, [])
.reduce((a: any, b) => a.push(`${b}=${obj[b]}`) && a, [])
.join('&');
}

7 changes: 1 addition & 6 deletions templates/xior/operation.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@
<% }); %>
$config?: XiorRequestConfig
): Promise<XiorResponse<<%~ it.returnType %>>> {
let url = '<%= it.url %>';
<% if(it.pathParams && it.pathParams.length > 0) {
it.pathParams.forEach((parameter) => { %>
url = url.replace('{<%= parameter.name %>}', encodeURIComponent(`${<%= parameter.name %>}`));
<% });
} %>
const url = `<%= it.url %>`;

return http.request<<%~ it.returnType %>>({
url: url,
Expand Down
47 changes: 19 additions & 28 deletions test/snapshots/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const petClient = {
addPet(body: Pet ,
$config?: AxiosRequestConfig
): AxiosPromise<Pet> {
let url = '/pet';
const url = `/pet`;

return axios.request<Pet>({
url: url,
Expand All @@ -40,8 +40,7 @@ export const petClient = {
petId: number ,
$config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/pet/{petId}';
url = url.replace('{petId}', encodeURIComponent(`${petId}`));
const url = `/pet/${encodeURIComponent(`${petId}`)}`;

return axios.request<unknown>({
url: url,
Expand All @@ -59,7 +58,7 @@ export const petClient = {
findPetsByStatus(status: ("available" | "pending" | "sold") | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<Pet[]> {
let url = '/pet/findByStatus';
const url = `/pet/findByStatus`;

return axios.request<Pet[]>({
url: url,
Expand All @@ -77,7 +76,7 @@ export const petClient = {
findPetsByTags(tags: string[] | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<Pet[]> {
let url = '/pet/findByTags';
const url = `/pet/findByTags`;

return axios.request<Pet[]>({
url: url,
Expand All @@ -95,8 +94,7 @@ export const petClient = {
getPetById(petId: number ,
$config?: AxiosRequestConfig
): AxiosPromise<Pet> {
let url = '/pet/{petId}';
url = url.replace('{petId}', encodeURIComponent(`${petId}`));
const url = `/pet/${encodeURIComponent(`${petId}`)}`;

return axios.request<Pet>({
url: url,
Expand All @@ -111,7 +109,7 @@ export const petClient = {
updatePet(body: Pet ,
$config?: AxiosRequestConfig
): AxiosPromise<Pet> {
let url = '/pet';
const url = `/pet`;

return axios.request<Pet>({
url: url,
Expand All @@ -131,8 +129,7 @@ export const petClient = {
status: string | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/pet/{petId}';
url = url.replace('{petId}', encodeURIComponent(`${petId}`));
const url = `/pet/${encodeURIComponent(`${petId}`)}`;

return axios.request<unknown>({
url: url,
Expand All @@ -155,8 +152,7 @@ export const petClient = {
additionalMetadata: string | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<File> {
let url = '/pet/{petId}/uploadImage';
url = url.replace('{petId}', encodeURIComponent(`${petId}`));
const url = `/pet/${encodeURIComponent(`${petId}`)}/uploadImage`;

return axios.request<File>({
url: url,
Expand All @@ -178,8 +174,7 @@ export const storeClient = {
deleteOrder(orderId: number ,
$config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/store/order/{orderId}';
url = url.replace('{orderId}', encodeURIComponent(`${orderId}`));
const url = `/store/order/${encodeURIComponent(`${orderId}`)}`;

return axios.request<unknown>({
url: url,
Expand All @@ -192,7 +187,7 @@ export const storeClient = {
*/
getInventory($config?: AxiosRequestConfig
): AxiosPromise<{ [key: string]: number }> {
let url = '/store/inventory';
const url = `/store/inventory`;

return axios.request<{ [key: string]: number }>({
url: url,
Expand All @@ -207,8 +202,7 @@ export const storeClient = {
getOrderById(orderId: number ,
$config?: AxiosRequestConfig
): AxiosPromise<Order> {
let url = '/store/order/{orderId}';
url = url.replace('{orderId}', encodeURIComponent(`${orderId}`));
const url = `/store/order/${encodeURIComponent(`${orderId}`)}`;

return axios.request<Order>({
url: url,
Expand All @@ -223,7 +217,7 @@ export const storeClient = {
placeOrder(body: Order | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<Order> {
let url = '/store/order';
const url = `/store/order`;

return axios.request<Order>({
url: url,
Expand All @@ -242,7 +236,7 @@ export const userClient = {
createUser(body: User | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<User> {
let url = '/user';
const url = `/user`;

return axios.request<User>({
url: url,
Expand All @@ -258,7 +252,7 @@ export const userClient = {
createUsersWithListInput(body: User[] | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<User> {
let url = '/user/createWithList';
const url = `/user/createWithList`;

return axios.request<User>({
url: url,
Expand All @@ -274,8 +268,7 @@ export const userClient = {
deleteUser(username: string ,
$config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/user/{username}';
url = url.replace('{username}', encodeURIComponent(`${username}`));
const url = `/user/${encodeURIComponent(`${username}`)}`;

return axios.request<unknown>({
url: url,
Expand All @@ -290,8 +283,7 @@ export const userClient = {
getUserByName(username: string ,
$config?: AxiosRequestConfig
): AxiosPromise<User> {
let url = '/user/{username}';
url = url.replace('{username}', encodeURIComponent(`${username}`));
const url = `/user/${encodeURIComponent(`${username}`)}`;

return axios.request<User>({
url: url,
Expand All @@ -308,7 +300,7 @@ export const userClient = {
password: string | null | undefined,
$config?: AxiosRequestConfig
): AxiosPromise<string> {
let url = '/user/login';
const url = `/user/login`;

return axios.request<string>({
url: url,
Expand All @@ -325,7 +317,7 @@ export const userClient = {
*/
logoutUser($config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/user/logout';
const url = `/user/logout`;

return axios.request<unknown>({
url: url,
Expand All @@ -342,8 +334,7 @@ export const userClient = {
username: string ,
$config?: AxiosRequestConfig
): AxiosPromise<unknown> {
let url = '/user/{username}';
url = url.replace('{username}', encodeURIComponent(`${username}`));
const url = `/user/${encodeURIComponent(`${username}`)}`;

return axios.request<unknown>({
url: url,
Expand Down
Loading

0 comments on commit 7ba815f

Please sign in to comment.