Skip to content

Commit

Permalink
feat: Compatibility to support REST endpoints (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleshchev authored Oct 4, 2023
1 parent c34deb3 commit 9c6f386
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 60 deletions.
12 changes: 6 additions & 6 deletions .snapshot/all/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ export class ProductService extends DownloadFileService {
).pipe($mappers.mapSingle($models.Product));
}

public getProduct(): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
``,
).pipe($mappers.mapCollection($models.Product));
}

public getProducts(): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
`GetProducts`,
Expand Down Expand Up @@ -104,12 +110,6 @@ export class ProductService extends DownloadFileService {
).pipe($mappers.mapCollection($models.Product));
}

public productDefault(): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
``,
).pipe($mappers.mapCollection($models.Product));
}

public searchProducts(name: string): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
`SearchProducts?name=${encodeURIComponent(name)}`,
Expand Down
14 changes: 7 additions & 7 deletions .snapshot/withRequestOptions/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export class ProductService extends DownloadFileService {
).pipe($mappers.mapSingle($models.Product));
}

public getProduct(options?: $types.TypeOrUndefined<IAngularHttpRequestOptions>): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
``,
options,
).pipe($mappers.mapCollection($models.Product));
}

public getProducts(options?: $types.TypeOrUndefined<IAngularHttpRequestOptions>): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
`GetProducts`,
Expand Down Expand Up @@ -113,13 +120,6 @@ export class ProductService extends DownloadFileService {
).pipe($mappers.mapCollection($models.Product));
}

public productDefault(options?: $types.TypeOrUndefined<IAngularHttpRequestOptions>): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
``,
options,
).pipe($mappers.mapCollection($models.Product));
}

public searchProducts(name: string, options?: $types.TypeOrUndefined<IAngularHttpRequestOptions>): Observable<$models.Product[]> {
return this.get<$models.IProduct[]>(
`SearchProducts?name=${encodeURIComponent(name)}`,
Expand Down
54 changes: 37 additions & 17 deletions __tests__/services/EndpointNameResolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,76 @@ describe('EndpointNameResolver tests', () => {
};
}

let service: EndpointNameResolver;
beforeEach(() => (service = new EndpointNameResolver()));

describe('checkDuplicates', () => {
test('store with duplicates', () => {
// Arrange
const service = new EndpointNameResolver();
const origin = '/api/v1/Product/Product';
const infos = [
toEndpointInfo({ actions: [{ name: 'product', origin: '' }], origin }),
toEndpointInfo({ actions: [{ name: 'product', origin: '' }], origin })
];

// Assert
expect(() => { service.checkDuplicates(infos) }).toThrowError(new Error(`Duplicate by path: '${origin}' was detected. Please, rename your endpoints`));
expect(() => {
service.checkDuplicates(infos);
}).toThrowError(new Error(`Duplicate by path: '${origin}' was detected. Please, rename your endpoints`));
});
});

describe('generateNameByPath', () => {
test('short path', () => {
describe('generateName', () => {
test('empty', () => {
// Arrange
const service = new EndpointNameResolver();
const group = 'Product';
const verb = 'get';
const endpoint = '';

// Act
const result = service.generateNameByPath('SearchProducts')
const result = service.generateName(group, endpoint, verb, 1);

// Assert
expect(result).toEqual('searchProducts');
expect(result).toEqual('getProduct');
});

test('only one query parameter', () => {
// Arrange
const group = 'Product';
const verb = 'get';
const endpoint = '{id}';

// Act
const result = service.generateName(group, endpoint, verb, 1);

// Assert
expect(result).toEqual('getProductById');
});

test('long path', () => {
// Arrange
const service = new EndpointNameResolver();
const group = 'Product';
const verb = 'get';
const endpoint = 'customer/{customer}/type/{type}';

// Act
const result = service.generateNameByPath('getByCustomer/{customer}/type/{type}')
const result = service.generateName(group, endpoint, verb, 1);

// Assert
expect(result).toEqual('getByCustomerType');
expect(result).toEqual('customerType');
});
});

describe('generateNameDefault', () => {
test('name', () => {
test('multiple verbs', () => {
// Arrange
const service = new EndpointNameResolver();
const name = 'product';
const group = 'Product';
const verb = 'get';
const endpoint = 'customer/{customer}/type/{type}';

// Act
const result = service.generateNameDefault(name);
const result = service.generateName(group, endpoint, verb, 2);

// Assert
expect(result).toEqual(`${name}Default`);
expect(result).toEqual('getCustomerType');
});
});
});
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@luxbss/gengen",
"version": "1.1.1",
"version": "1.2.0",
"description": "Tool for generating models and Angular services based on OpenAPIs and Swagger's JSON",
"bin": {
"gengen": "./bin/index.js"
Expand Down
28 changes: 16 additions & 12 deletions src/services/EndpointNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ interface IEndpointInfoItem {
export class EndpointNameResolver {
public checkDuplicates(endpointInfos: IEndpointInfo[]): void {
const infos = endpointInfos.reduce<IEndpointInfoItem[]>((arr, z) => {
arr.push(...z.actions.map(x => ({ name: z.name, origin: z.origin, action: x })))
arr.push(...z.actions.map((x) => ({ name: z.name, origin: z.origin, action: x })));
return arr;
}, []);

infos.forEach(z => {
infos.forEach((z) => {
const duplicates = this.findDuplicates(z, infos);
if (duplicates.length > 1) {
const duplicate = first(duplicates);
Expand All @@ -24,21 +24,25 @@ export class EndpointNameResolver {
});
}

public generateNameByPath(path: string): string {
return path
.split(pathOptions.separator)
.filter((z) => z && !this.queryParameterRegExp.test(z))
.map((z, i) => i ? upperFirst(z) : lowerFirst(z))
public generateName(groupName: string, enpoint: string, verb: string, verbCount: number): string {
if (!enpoint) {
return `${verb}${groupName}`;
}
const parts = enpoint.split(pathOptions.separator).filter((x) => x);
if (parts.length === 1 && this.queryParameterRegExp.test(parts[0])) {
return `${verb}${groupName}By${upperFirst(parts[0].substring(1, parts[0].length - 1))}`;
}

const name = parts
.filter((x) => !this.queryParameterRegExp.test(x))
.map((x, i) => (i ? upperFirst(x) : lowerFirst(x)))
.join('');
}

public generateNameDefault(name: string): string {
return lowerFirst(`${name}Default`);
return verbCount > 1 ? `${verb}${upperFirst(name)}` : name;
}


private findDuplicates(info: IEndpointInfoItem, infos: IEndpointInfoItem[]): IEndpointInfoItem[] {
return infos.filter(x => info.action.name === x.action.name && info.name === x.name);
return infos.filter((x) => info.action.name === x.action.name && info.name === x.name);
}

private queryParameterRegExp = new RegExp('^{(.*)}$');
Expand Down
15 changes: 4 additions & 11 deletions src/services/EndpointsService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MethodOperation } from '../models/kinds/MethodOperation';
import { pathOptions } from '../options';
import { OpenAPIService } from '../swagger/OpenAPIService';
import { first, sortBy, upperFirst } from '../utils';
Expand Down Expand Up @@ -67,16 +66,10 @@ export class EndpointsService {
name: groupName,
origin: endpoint,
relativePath: endpoint.slice(0, groupNameStartIndex) + rawGroupName,
actions: methods.map((z) => {
const name = rawEndpointName
? this.endpointNameResolver.generateNameByPath(rawEndpointName)
: this.endpointNameResolver.generateNameDefault(groupName);

return {
name: `${methods.length > 1 ? `${MethodOperation[z.method].toLocaleLowerCase()}${upperFirst(name)}` : name}`,
origin: rawEndpointName
};
})
actions: methods.map((z) => ({
name: this.endpointNameResolver.generateName(groupName, rawEndpointName, z.methodName, methods.length),
origin: rawEndpointName
}))
};
}

Expand Down
15 changes: 11 additions & 4 deletions src/swagger/OpenAPIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface IOperation {
key: string;
operation: IOpenAPI3Operation;
method: MethodOperation;
methodName: string;
}

export type IOpenAPI3Operations = { [key: string]: { method: MethodOperation; operation: IOpenAPI3Operation }[] };
Expand Down Expand Up @@ -133,21 +134,27 @@ export class OpenAPIService {
const [name, pathItem] = path;

return Object.keys(pathItem).reduce<IOperation[]>((operations, method) => {
const operation: Partial<IOperation> = { key: name, methodName: method };
switch (method) {
case 'get':
operations.push({ key: name, operation: pathItem.get, method: MethodOperation.GET } as IOperation);
operation.method = MethodOperation.GET;
operation.operation = pathItem.get;
break;
case 'post':
operations.push({ key: name, operation: pathItem.post, method: MethodOperation.POST } as IOperation);
operation.method = MethodOperation.POST;
operation.operation = pathItem.post;
break;
case 'put':
operations.push({ key: name, operation: pathItem.put, method: MethodOperation.PUT } as IOperation);
operation.method = MethodOperation.PUT;
operation.operation = pathItem.put;
break;
case 'delete':
operations.push({ key: name, operation: pathItem.delete, method: MethodOperation.DELETE } as IOperation);
operation.method = MethodOperation.DELETE;
operation.operation = pathItem.delete;
break;
}

operations.push(operation as IOperation);
return operations;
}, []);
}
Expand Down

0 comments on commit 9c6f386

Please sign in to comment.