-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add example enhancers Signed-off-by: Douglas McConnachie <[email protected]>
- Loading branch information
Showing
8 changed files
with
272 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
packages/openapi-v3/src/enhancers/extensions/deprecate.spec.extension.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import {bind} from '@loopback/core'; | ||
import {OperationObject} from 'openapi3-ts'; | ||
import {OpenApiSpec} from '../../types'; | ||
import {asSpecEnhancer, OASEnhancer} from '../types'; | ||
|
||
/** | ||
* A spec enhancer to add verbose deprecation status to OperationObjects | ||
* | ||
*/ | ||
@bind(asSpecEnhancer) | ||
export class DeprecateSpecEnhancer implements OASEnhancer { | ||
name = 'deprecate'; | ||
|
||
modifySpec(spec: OpenApiSpec): OpenApiSpec { | ||
try { | ||
return this.defaultDeprecate(spec); | ||
} catch { | ||
console.log('Default Deprecate Enhancer failed, returned original spec.'); | ||
return spec; | ||
} | ||
} | ||
|
||
/** | ||
* add verbose deprecation status to OperationObjects if not set | ||
* | ||
*/ | ||
private defaultDeprecate(spec: OpenApiSpec): OpenApiSpec { | ||
Object.keys(spec.paths).forEach(path => | ||
Object.keys(spec.paths[path]).forEach(op => { | ||
const OpObj = spec.paths[path][op] as OperationObject; | ||
if (!OpObj.deprecated) OpObj.deprecated = false; | ||
}), | ||
); | ||
|
||
return spec; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
packages/openapi-v3/src/enhancers/extensions/prune.spec.extension.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import {bind} from '@loopback/core'; | ||
import _ from 'lodash'; | ||
import {ISpecificationExtension, OpenApiSpec} from '../..'; | ||
import {asSpecEnhancer, OASEnhancer} from '../types'; | ||
|
||
/** | ||
* A spec enhancer to remove unneccesary properties from the OpenAPI spec | ||
* | ||
*/ | ||
@bind(asSpecEnhancer) | ||
export class PruneSpecEnhancer implements OASEnhancer { | ||
name = 'prune'; | ||
|
||
modifySpec(spec: OpenApiSpec): OpenApiSpec { | ||
try { | ||
// note, title is valid but pruning as not needed atm | ||
const values = ['x-', 'title']; | ||
return this.pruneSchemas({spec, values}); | ||
} catch { | ||
console.log('Prune Enhancer failed, returned original spec.'); | ||
return spec; | ||
} | ||
} | ||
|
||
/** | ||
* Recursively walk OpenApiSpec and prune excess properties | ||
* | ||
*/ | ||
private pruneSchemas(ctx: PruneContext): OpenApiSpec { | ||
// use 'paths' as crawl root | ||
this.recursiveWalk(ctx.spec.paths, ['paths'], ctx); | ||
// use 'components.schemas' as crawl root | ||
if (ctx.spec.components && ctx.spec.components.schemas) { | ||
this.recursiveWalk( | ||
ctx.spec.components.schemas, | ||
['components', 'schemas'], | ||
ctx, | ||
); | ||
} | ||
|
||
return ctx.spec; | ||
} | ||
|
||
private recursiveWalk( | ||
rootSchema: ISpecificationExtension, | ||
parentPath: Array<string>, | ||
ctx: PruneContext, | ||
) { | ||
if (this.isTraversable(rootSchema)) { | ||
Object.entries(rootSchema).forEach(([key, subSchema]) => { | ||
if (subSchema) { | ||
this.recursiveWalk(subSchema, parentPath.concat(key), ctx); | ||
if (!this.isTraversable(subSchema)) { | ||
this.processSchema(rootSchema, parentPath, ctx); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Carry out schema pruning after tree traversal. If key starts with any of | ||
* the propsToRemove then unset. | ||
* | ||
* @param schema - current schema element to process | ||
* @param parentPath - path object to parent | ||
* @param ctx - prune context object | ||
* | ||
*/ | ||
private async processSchema( | ||
schema: ISpecificationExtension, | ||
parentPath: Array<string>, | ||
ctx: PruneContext, | ||
) { | ||
Object.keys(schema).forEach(key => { | ||
ctx.values.forEach(name => { | ||
if (key.startsWith(name)) { | ||
this.patchRemove(parentPath.concat(key), ctx); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
patchRemove(path: Array<string>, ctx: PruneContext) { | ||
_.unset(ctx.spec, path); | ||
} | ||
|
||
private isTraversable(schema: ISpecificationExtension): boolean { | ||
return schema && typeof schema === 'object' ? true : false; | ||
} | ||
} | ||
|
||
/** | ||
* Prune context | ||
* | ||
* @param spec - subject openapi specification | ||
* @param values - array of properties to remove | ||
* | ||
*/ | ||
interface PruneContext { | ||
spec: OpenApiSpec; | ||
values: Array<string>; | ||
} |
59 changes: 59 additions & 0 deletions
59
packages/openapi-v3/src/enhancers/extensions/structure.spec.extension.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import {bind} from '@loopback/core'; | ||
import {OpenApiSpec} from '../../types'; | ||
import {asSpecEnhancer, OASEnhancer} from '../types'; | ||
|
||
/** | ||
* A spec enhancer to restructure the OpenAPI spec in an opinionated manner. | ||
* (dougal83) | ||
* | ||
*/ | ||
@bind(asSpecEnhancer) | ||
export class StructureSpecEnhancer implements OASEnhancer { | ||
name = 'structure'; | ||
|
||
modifySpec(spec: OpenApiSpec): OpenApiSpec { | ||
try { | ||
return this.restructureSpec(spec); | ||
} catch { | ||
console.log('Restructure Enhancer failed, returned original spec.'); | ||
return spec; | ||
} | ||
} | ||
|
||
/** | ||
* Structure OpenApiSpec in an opinionated manner | ||
* | ||
*/ | ||
private restructureSpec(spec: OpenApiSpec): OpenApiSpec { | ||
spec = Object.assign( | ||
{ | ||
openapi: undefined, | ||
info: undefined, | ||
servers: undefined, | ||
paths: undefined, | ||
components: undefined, | ||
tags: undefined, | ||
}, | ||
spec, | ||
); | ||
|
||
Object.keys(spec.paths).forEach(path => | ||
Object.keys(spec.paths[path]).forEach(op => { | ||
spec.paths[path][op] = Object.assign( | ||
{ | ||
tags: undefined, | ||
summary: undefined, | ||
description: undefined, | ||
operationId: undefined, | ||
parameters: undefined, | ||
responses: undefined, | ||
depreciated: undefined, | ||
}, | ||
spec.paths[path][op], | ||
); | ||
}), | ||
); | ||
|
||
return spec; | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
packages/openapi-v3/src/enhancers/extensions/tags.spec.extension.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import {bind} from '@loopback/core'; | ||
import {OperationObject} from 'openapi3-ts'; | ||
import {OpenApiSpec} from '../../types'; | ||
import {asSpecEnhancer, OASEnhancer} from '../types'; | ||
|
||
/** | ||
* A spec enhancer to combine OperationObject Tags OpenApiSpec into spec.tags | ||
* | ||
*/ | ||
@bind(asSpecEnhancer) | ||
export class TagsSpecEnhancer implements OASEnhancer { | ||
name = 'tags'; | ||
|
||
modifySpec(spec: OpenApiSpec): OpenApiSpec { | ||
try { | ||
return this.tagsConsolidate(spec); | ||
} catch { | ||
console.log('Tags Enhancer failed, returned original spec.'); | ||
return spec; | ||
} | ||
} | ||
|
||
/** | ||
* Combine OperationObject Tags OpenApiSpec into spec.tags | ||
* | ||
*/ | ||
private tagsConsolidate(spec: OpenApiSpec): OpenApiSpec { | ||
Object.keys(spec.paths).forEach(path => | ||
Object.keys(spec.paths[path]).forEach(op => { | ||
const OpObj = spec.paths[path][op] as OperationObject; | ||
if (OpObj.tags) this.patchTags(OpObj.tags, spec); | ||
}), | ||
); | ||
|
||
return spec; | ||
} | ||
|
||
// TODO(dougal83) desc. resolution | ||
private patchTags(tags: Array<string>, spec: OpenApiSpec) { | ||
if (!spec.tags) { | ||
spec.tags = []; | ||
} | ||
tags.forEach(name => { | ||
if (spec.tags!.findIndex(tag => tag.name === name) <= 0) | ||
spec.tags!.push({name, description: ''}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters