@loopback/metadata
Advanced tools
Comparing version 1.3.10 to 1.4.0
@@ -6,2 +6,20 @@ # Change Log | ||
# [1.4.0](https://github.com/strongloop/loopback-next/compare/@loopback/metadata@1.3.10...@loopback/metadata@1.4.0) (2020-01-27) | ||
### Bug Fixes | ||
* clarify the decorator application order ([ab0dc3d](https://github.com/strongloop/loopback-next/commit/ab0dc3d9f1dedb6b61ccf5b4d63854a4f9dfa814)) | ||
* ensure tests follow parameter patterns ([d9d8154](https://github.com/strongloop/loopback-next/commit/d9d815403b8b7c39aa71494f46b352c646324673)) | ||
* makes multi-decorator behavior more predictable ([0e00068](https://github.com/strongloop/loopback-next/commit/0e00068ccee12e004a2684afcf8bec5d651a9a20)) | ||
### Features | ||
* adds MultiMethodDecoratorFactory ([bf6c787](https://github.com/strongloop/loopback-next/commit/bf6c7872bb8ce492b43ab8f57a641dcbb341e96e)) | ||
## [1.3.10](https://github.com/strongloop/loopback-next/compare/@loopback/metadata@1.3.9...@loopback/metadata@1.3.10) (2020-01-07) | ||
@@ -8,0 +26,0 @@ |
@@ -263,1 +263,30 @@ import { DecoratorType, MetadataKey, MetadataMap } from './types'; | ||
} | ||
/** | ||
* Factory for an append-array of method-level decorators | ||
* The @response metadata for a method is an array. | ||
* Each item in the array should be a single value, containing | ||
* a response code and a single spec or Model. This should allow: | ||
* ```ts | ||
* @response(200, MyFirstModel) | ||
* @response(403, [NotAuthorizedReasonOne, NotAuthorizedReasonTwo]) | ||
* @response(404, NotFoundOne) | ||
* @response(404, NotFoundTwo) | ||
* @response(409, {schema: {}}) | ||
* public async myMethod() {} | ||
* ``` | ||
* | ||
* In the case that a ResponseObject is passed, it becomes the | ||
* default for description/content, and if possible, further Models are | ||
* incorporated as a `oneOf: []` array. | ||
* | ||
* In the case that a ReferenceObject is passed, it and it alone is used, since | ||
* references can be external and we cannot `oneOf` their content. | ||
* | ||
* The factory creates and updates an array of items T[], and the getter | ||
* provides the values as that array. | ||
*/ | ||
export declare class MethodMultiDecoratorFactory<T> extends MethodDecoratorFactory<T[]> { | ||
protected mergeWithInherited(inheritedMetadata: MetadataMap<T[]>, target: Object, methodName?: string): MetadataMap<T[]>; | ||
protected mergeWithOwn(ownMetadata: MetadataMap<T[]>, target: Object, methodName?: string, methodDescriptor?: TypedPropertyDescriptor<any> | number): MetadataMap<T[]>; | ||
private _mergeArray; | ||
} |
@@ -513,2 +513,58 @@ "use strict"; | ||
exports.MethodParameterDecoratorFactory = MethodParameterDecoratorFactory; | ||
/** | ||
* Factory for an append-array of method-level decorators | ||
* The @response metadata for a method is an array. | ||
* Each item in the array should be a single value, containing | ||
* a response code and a single spec or Model. This should allow: | ||
* ```ts | ||
* @response(200, MyFirstModel) | ||
* @response(403, [NotAuthorizedReasonOne, NotAuthorizedReasonTwo]) | ||
* @response(404, NotFoundOne) | ||
* @response(404, NotFoundTwo) | ||
* @response(409, {schema: {}}) | ||
* public async myMethod() {} | ||
* ``` | ||
* | ||
* In the case that a ResponseObject is passed, it becomes the | ||
* default for description/content, and if possible, further Models are | ||
* incorporated as a `oneOf: []` array. | ||
* | ||
* In the case that a ReferenceObject is passed, it and it alone is used, since | ||
* references can be external and we cannot `oneOf` their content. | ||
* | ||
* The factory creates and updates an array of items T[], and the getter | ||
* provides the values as that array. | ||
*/ | ||
class MethodMultiDecoratorFactory extends MethodDecoratorFactory { | ||
mergeWithInherited(inheritedMetadata, target, methodName) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
inheritedMetadata[methodName] = this._mergeArray(inheritedMetadata[methodName], this.withTarget(this.spec, target)); | ||
return inheritedMetadata; | ||
} | ||
mergeWithOwn(ownMetadata, target, methodName, methodDescriptor) { | ||
ownMetadata = ownMetadata || {}; | ||
ownMetadata[methodName] = this._mergeArray(ownMetadata[methodName], this.withTarget(this.spec, target)); | ||
return ownMetadata; | ||
} | ||
_mergeArray(result, methodMeta) { | ||
if (!result) { | ||
if (Array.isArray(methodMeta)) { | ||
result = methodMeta; | ||
} | ||
else { | ||
result = [methodMeta]; | ||
} | ||
} | ||
else { | ||
if (Array.isArray(methodMeta)) { | ||
result.push(...methodMeta); | ||
} | ||
else { | ||
result.push(methodMeta); | ||
} | ||
} | ||
return result; | ||
} | ||
} | ||
exports.MethodMultiDecoratorFactory = MethodMultiDecoratorFactory; | ||
//# sourceMappingURL=decorator-factory.js.map |
{ | ||
"name": "@loopback/metadata", | ||
"version": "1.3.10", | ||
"version": "1.4.0", | ||
"description": "LoopBack's metadata utilities for reflection and decoration", | ||
@@ -26,5 +26,5 @@ "engines": { | ||
"devDependencies": { | ||
"@loopback/build": "^3.0.1", | ||
"@loopback/eslint-config": "^5.0.1", | ||
"@loopback/testlab": "^1.10.1", | ||
"@loopback/build": "^3.1.0", | ||
"@loopback/eslint-config": "^5.0.2", | ||
"@loopback/testlab": "^1.10.2", | ||
"@types/debug": "^4.1.5", | ||
@@ -55,3 +55,3 @@ "@types/lodash": "^4.14.149", | ||
}, | ||
"gitHead": "598baf6e84de3917bb67aff47a1ab1cb1111e3d2" | ||
"gitHead": "d08f135a0d1040edc61497739a8d86a866e4e29a" | ||
} |
108
README.md
@@ -90,2 +90,110 @@ # @loopback/metadata | ||
### To create a decorator that can be used multiple times on a single method | ||
Instead of a single immutable object to be merged, the | ||
`MethodMultiDecoratorFactory` reduced parameters into a flat array of items. | ||
When fetching the metadata later, you will receive it as an array. | ||
```ts | ||
import {MethodMultiDecoratorFactory} from '@loopback/metadata'; | ||
function myMultiMethodDecorator(spec: object): MethodDecorator { | ||
return MethodMultiDecoratorFactory.createDecorator<object>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
spec, | ||
); | ||
} | ||
``` | ||
Now, you can use it multiple times on a method: | ||
```ts | ||
class MyController { | ||
@myMultiMethodDecorator({x: 1}) | ||
@myMultiMethodDecorator({y: 2}) | ||
@myMultiMethodDecorator({z: 3}) | ||
public point() {} | ||
} | ||
class MyOtherController { | ||
@myMultiMethodDecorator([{x: 1}, {y: 2}, {z: 3}]) | ||
public point() {} | ||
} | ||
``` | ||
And when you access this data: | ||
```ts | ||
const arrayOfSpecs = MetadataInspector.getMethodMetadata<object>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
constructor.prototype, | ||
op, | ||
); | ||
// [{z: 3}, {y: 2}, {x: 1}] | ||
``` | ||
Typescript | ||
[applies decorators in reverse order](https://www.typescriptlang.org/docs/handbook/decorators.html) | ||
per class, from the parent down. The metadata array resurned by `getOwnMetadata` | ||
will be in this order: | ||
```ts | ||
class Parent { | ||
@myMultiMethodDecorator('A') // second | ||
@myMultiMethodDecorator('B') // first | ||
public greet() {} | ||
} | ||
class Child extends Parent { | ||
@myMultiMethodDecorator(['C', 'D']) // [third, fourth] | ||
public greet() {} | ||
} | ||
class Grandchild extends Child { | ||
@myMultiMethodDecorator('E') // sixth | ||
@myMultiMethodDecorator('F') // fifth | ||
public greet() {} | ||
} | ||
// getMethodMetadata = ['B', 'A', 'C', 'D', 'F', 'E'] | ||
``` | ||
You can also create a decorator that takes an object that can contain an array: | ||
```ts | ||
interface Point { | ||
x?: number; | ||
y?: number; | ||
z?: number; | ||
} | ||
interface GeometryMetadata { | ||
points: Point[]; | ||
} | ||
function geometry(...points: Point[]): MethodDecorator { | ||
return MethodMultiDecoratorFactory.createDecorator<GeometryMetadata>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
points, | ||
); | ||
} | ||
class MyGeoController { | ||
@geometry({x: 1}) | ||
@geometry({x: 2}, {y: 3}) | ||
@geometry({z: 5}) | ||
public abstract() {} | ||
} | ||
const arrayOfSpecs = MetadataInspector.getMethodMetadata<GeometryMetadata>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
constructor.prototype, | ||
op, | ||
); | ||
// [ | ||
// { points: [{x: 1}]}, | ||
// { points: [{x:2}, {y:3}]}, | ||
// { points: [{z: 5}]}, | ||
// ] | ||
``` | ||
### To create a property decorator | ||
@@ -92,0 +200,0 @@ |
@@ -792,1 +792,76 @@ // Copyright IBM Corp. 2017,2019. All Rights Reserved. | ||
} | ||
/** | ||
* Factory for an append-array of method-level decorators | ||
* The @response metadata for a method is an array. | ||
* Each item in the array should be a single value, containing | ||
* a response code and a single spec or Model. This should allow: | ||
* ```ts | ||
* @response(200, MyFirstModel) | ||
* @response(403, [NotAuthorizedReasonOne, NotAuthorizedReasonTwo]) | ||
* @response(404, NotFoundOne) | ||
* @response(404, NotFoundTwo) | ||
* @response(409, {schema: {}}) | ||
* public async myMethod() {} | ||
* ``` | ||
* | ||
* In the case that a ResponseObject is passed, it becomes the | ||
* default for description/content, and if possible, further Models are | ||
* incorporated as a `oneOf: []` array. | ||
* | ||
* In the case that a ReferenceObject is passed, it and it alone is used, since | ||
* references can be external and we cannot `oneOf` their content. | ||
* | ||
* The factory creates and updates an array of items T[], and the getter | ||
* provides the values as that array. | ||
*/ | ||
export class MethodMultiDecoratorFactory<T> extends MethodDecoratorFactory< | ||
T[] | ||
> { | ||
protected mergeWithInherited( | ||
inheritedMetadata: MetadataMap<T[]>, | ||
target: Object, | ||
methodName?: string, | ||
) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
inheritedMetadata[methodName!] = this._mergeArray( | ||
inheritedMetadata[methodName!], | ||
this.withTarget(this.spec, target), | ||
); | ||
return inheritedMetadata; | ||
} | ||
protected mergeWithOwn( | ||
ownMetadata: MetadataMap<T[]>, | ||
target: Object, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
) { | ||
ownMetadata = ownMetadata || {}; | ||
ownMetadata[methodName!] = this._mergeArray( | ||
ownMetadata[methodName!], | ||
this.withTarget(this.spec, target), | ||
); | ||
return ownMetadata; | ||
} | ||
private _mergeArray(result: T[], methodMeta: T | T[]) { | ||
if (!result) { | ||
if (Array.isArray(methodMeta)) { | ||
result = methodMeta; | ||
} else { | ||
result = [methodMeta]; | ||
} | ||
} else { | ||
if (Array.isArray(methodMeta)) { | ||
result.push(...methodMeta); | ||
} else { | ||
result.push(methodMeta); | ||
} | ||
} | ||
return result; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
161865
2748
583