Skip to content

Commit

Permalink
feat(): add use method name for operations option
Browse files Browse the repository at this point in the history
Make the behavior change for operation names that was introduced in #487 configurable
  • Loading branch information
dennisameling committed Aug 29, 2020
1 parent f75353f commit bdbdd35
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 16 deletions.
5 changes: 5 additions & 0 deletions lib/interfaces/swagger-document-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ export interface SwaggerDocumentOptions {
* If `true`, swagger will also load routes from the modules imported by `include` modules
*/
deepScanRoutes?: boolean;

/**
* If `true`, only uses the method name in operations (getUser) instead of methods prefixed by the controller name (UserController_getUser).
*/
useMethodNameForOperations?: boolean;
}
7 changes: 5 additions & 2 deletions lib/swagger-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ export class SwaggerExplorer {
private readonly metadataScanner = new MetadataScanner();
private readonly schemas: SchemaObject[] = [];
private readonly schemaRefsStack: string[] = [];
private useMethodNameForOperations: boolean;

constructor(private readonly schemaObjectFactory: SchemaObjectFactory) {}

public exploreController(
wrapper: InstanceWrapper<Controller>,
modulePath?: string,
globalPrefix?: string
globalPrefix?: string,
useMethodNameForOperations?: boolean
) {
this.useMethodNameForOperations = useMethodNameForOperations;
const { instance, metatype } = wrapper;
const prototype = Object.getPrototypeOf(instance);
const documentResolvers: DenormalizedDocResolvers = {
Expand Down Expand Up @@ -226,7 +229,7 @@ export class SwaggerExplorer {
}

private getOperationId(instance: object, method: Function): string {
if (instance.constructor) {
if (instance.constructor && !this.useMethodNameForOperations) {
return `${instance.constructor.name}_${method.name}`;
}
return method.name;
Expand Down
20 changes: 16 additions & 4 deletions lib/swagger-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export class SwaggerScanner {
deepScanRoutes,
include: includedModules = [],
extraModels = [],
ignoreGlobalPrefix = false
ignoreGlobalPrefix = false,
useMethodNameForOperations = false
} = options;

const container: NestContainer = (app as any).container;
Expand Down Expand Up @@ -64,7 +65,12 @@ export class SwaggerScanner {
? Reflect.getMetadata(MODULE_PATH, metatype)
: undefined;

return this.scanModuleRoutes(allRoutes, path, globalPrefix);
return this.scanModuleRoutes(
allRoutes,
path,
globalPrefix,
useMethodNameForOperations
);
}
);

Expand All @@ -85,10 +91,16 @@ export class SwaggerScanner {
public scanModuleRoutes(
routes: Map<string, InstanceWrapper>,
modulePath?: string,
globalPrefix?: string
globalPrefix?: string,
useMethodNameForOperations?: boolean
): Array<Omit<OpenAPIObject, 'openapi' | 'info'> & Record<'root', any>> {
const denormalizedArray = [...routes.values()].map((ctrl) =>
this.explorer.exploreController(ctrl, modulePath, globalPrefix)
this.explorer.exploreController(
ctrl,
modulePath,
globalPrefix,
useMethodNameForOperations
)
);
return flatten(denormalizedArray) as any;
}
Expand Down
114 changes: 104 additions & 10 deletions test/explorer/swagger-explorer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import { ModelPropertiesAccessor } from '../../lib/services/model-properties-acc
import { SchemaObjectFactory } from '../../lib/services/schema-object-factory';
import { SwaggerTypesMapper } from '../../lib/services/swagger-types-mapper';
import { SwaggerExplorer } from '../../lib/swagger-explorer';
import { DenormalizedDoc } from '../../lib/interfaces/denormalized-doc.interface';

describe('SwaggerExplorer', () => {
const schemaObjectFactory = new SchemaObjectFactory(
new ModelPropertiesAccessor(),
new SwaggerTypesMapper()
);

describe('when module only uses metadata', () => {
class Foo {}

Expand Down Expand Up @@ -76,11 +78,35 @@ describe('SwaggerExplorer', () => {
} as InstanceWrapper<FooController>,
'path'
);
const operationPrefix = 'FooController_';

validateRoutes(routes, operationPrefix);
});

it('sees two controller operations and their responses with useMethodNameForOperations = true', () => {
const explorer = new SwaggerExplorer(schemaObjectFactory);
const routes = explorer.exploreController(
{
instance: new FooController(),
metatype: FooController
} as InstanceWrapper<FooController>,
'path',
undefined,
true
);
const operationPrefix = '';

validateRoutes(routes, operationPrefix);
});

const validateRoutes = (
routes: DenormalizedDoc[],
operationPrefix: string
) => {
expect(routes.length).toEqual(2);

// POST
expect(routes[0].root.operationId).toEqual('FooController_create');
expect(routes[0].root.operationId).toEqual(operationPrefix + 'create');
expect(routes[0].root.method).toEqual('post');
expect(routes[0].root.path).toEqual('/path/foos');
expect(routes[0].root.summary).toEqual('Create foo');
Expand Down Expand Up @@ -141,7 +167,7 @@ describe('SwaggerExplorer', () => {
});

// GET
expect(routes[1].root.operationId).toEqual('FooController_find');
expect(routes[1].root.operationId).toEqual(operationPrefix + 'find');
expect(routes[1].root.method).toEqual('get');
expect(routes[1].root.path).toEqual('/path/foos/{objectId}');
expect(routes[1].root.summary).toEqual('List all Foos');
Expand Down Expand Up @@ -179,8 +205,9 @@ describe('SwaggerExplorer', () => {
}
}
});
});
};
});

describe('when explicit decorators and metadata are used', () => {
class Foo {}

Expand Down Expand Up @@ -223,11 +250,35 @@ describe('SwaggerExplorer', () => {
} as InstanceWrapper<FooController>,
'path'
);
const prefix = 'FooController_';

validateRoutes(routes, prefix);
});

it('sees two controller operations and their responses when useMethodNameForOperations = true', () => {
const explorer = new SwaggerExplorer(schemaObjectFactory);
const routes = explorer.exploreController(
{
instance: new FooController(),
metatype: FooController
} as InstanceWrapper<FooController>,
'path',
undefined,
true
);
const prefix = '';

validateRoutes(routes, prefix);
});

const validateRoutes = (
routes: DenormalizedDoc[],
operationPrefix: string
) => {
expect(routes.length).toEqual(2);

// POST
expect(routes[0].root.operationId).toEqual('FooController_create');
expect(routes[0].root.operationId).toEqual(operationPrefix + 'create');
expect(routes[0].root.method).toEqual('post');
expect(routes[0].root.path).toEqual('/path/foos');
expect(routes[0].root.summary).toEqual('Create foo');
Expand Down Expand Up @@ -260,7 +311,7 @@ describe('SwaggerExplorer', () => {
});

// GET
expect(routes[1].root.operationId).toEqual('FooController_find');
expect(routes[1].root.operationId).toEqual(operationPrefix + 'find');
expect(routes[1].root.method).toEqual('get');
expect(routes[1].root.path).toEqual('/path/foos/{objectId}');
expect(routes[1].root.summary).toEqual('List all Foos');
Expand Down Expand Up @@ -310,7 +361,7 @@ describe('SwaggerExplorer', () => {
}
}
});
});
};
});
describe('when only explicit decorators are used', () => {
class Foo {}
Expand Down Expand Up @@ -351,11 +402,35 @@ describe('SwaggerExplorer', () => {
} as InstanceWrapper<FooController>,
'path'
);
const operationPrefix = 'FooController_';

validateRoutes(routes, operationPrefix);
});

it('sees two controller operations and their responses when useMethodNameForOperations = true', () => {
const explorer = new SwaggerExplorer(schemaObjectFactory);
const routes = explorer.exploreController(
{
instance: new FooController(),
metatype: FooController
} as InstanceWrapper<FooController>,
'path',
undefined,
true
);
const operationPrefix = '';

validateRoutes(routes, operationPrefix);
});

const validateRoutes = (
routes: DenormalizedDoc[],
operationPrefix: string
) => {
expect(routes.length).toEqual(2);

// POST
expect(routes[0].root.operationId).toEqual('FooController_create');
expect(routes[0].root.operationId).toEqual(operationPrefix + 'create');
expect(routes[0].root.method).toEqual('post');
expect(routes[0].root.path).toEqual('/path/foos');
expect(routes[0].root.summary).toEqual('Create foo');
Expand Down Expand Up @@ -385,7 +460,7 @@ describe('SwaggerExplorer', () => {
});

// GET
expect(routes[1].root.operationId).toEqual('FooController_find');
expect(routes[1].root.operationId).toEqual(operationPrefix + 'find');
expect(routes[1].root.method).toEqual('get');
expect(routes[1].root.path).toEqual('/path/foos/{objectId}');
expect(routes[1].root.summary).toEqual('List all Foos');
Expand Down Expand Up @@ -424,7 +499,7 @@ describe('SwaggerExplorer', () => {
}
}
});
});
};
});
describe('when custom properties are passed', () => {
class Foo {}
Expand Down Expand Up @@ -492,6 +567,25 @@ describe('SwaggerExplorer', () => {
'path'
);

validateRoutes(routes);
});

it('should merge implicit metadata with explicit options and useMethodNameForOperations = true', () => {
const explorer = new SwaggerExplorer(schemaObjectFactory);
const routes = explorer.exploreController(
{
instance: new FooController(),
metatype: FooController
} as InstanceWrapper<FooController>,
'path',
undefined,
true
);

validateRoutes(routes);
});

const validateRoutes = (routes: DenormalizedDoc[]) => {
expect(routes.length).toEqual(2);

// POST
Expand Down Expand Up @@ -581,7 +675,7 @@ describe('SwaggerExplorer', () => {
}
}
});
});
};
});
describe('when enum is used', () => {
enum ParamEnum {
Expand Down

0 comments on commit bdbdd35

Please sign in to comment.