Skip to content

Commit

Permalink
chore: Update Required decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Jun 21, 2019
1 parent da7ad4e commit 6fb9c9a
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 43 deletions.
37 changes: 25 additions & 12 deletions packages/common/src/mvc/decorators/required.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Type} from "@tsed/core";
import {ParamRegistry} from "../../filters/registries/ParamRegistry";
import {PropertyRegistry} from "../../jsonschema/registries/PropertyRegistry";
import {Allow} from "@tsed/common";
import {applyDecorators, DecoratorParameters, StoreMerge, UnsupportedDecoratorType} from "@tsed/core";
import {getStorableMetadata} from "./utils/getStorableMetadata";

/**
* Add required annotation for a function argument.
Expand All @@ -18,7 +18,7 @@ import {PropertyRegistry} from "../../jsonschema/registries/PropertyRegistry";
*
* ```typescript
* class Model {
* @JsonProperty()
* @Property()
* @Required()
* field: string;
* }
Expand All @@ -33,7 +33,7 @@ import {PropertyRegistry} from "../../jsonschema/registries/PropertyRegistry";
*
* ```typescript
* class Model {
* @JsonProperty()
* @Property()
* @Required()
* @Allow("")
* field: string;
Expand All @@ -45,11 +45,24 @@ import {PropertyRegistry} from "../../jsonschema/registries/PropertyRegistry";
* @converters
*/
export function Required(...allowedRequiredValues: any[]): any {
return (target: Type<any>, propertyKey: string, parameterIndex: number): void => {
if (typeof parameterIndex === "number") {
ParamRegistry.required(target, propertyKey, parameterIndex);
} else {
PropertyRegistry.required(target, propertyKey, allowedRequiredValues);
}
};
return applyDecorators(
(...decoratorArgs: DecoratorParameters) => {
const metadata = getStorableMetadata(decoratorArgs);

if (!metadata) {
throw new UnsupportedDecoratorType(Required, decoratorArgs);
}

metadata.required = true;

if (allowedRequiredValues.length) {
Allow(...allowedRequiredValues)(...decoratorArgs);
}
},
StoreMerge("responses", {
"400": {
description: "BadRequest"
}
})
);
}
83 changes: 52 additions & 31 deletions packages/common/test/mvc/decorators/required.spec.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
import * as Proxyquire from "proxyquire";
import {prototypeOf} from "@tsed/core";
import * as Sinon from "sinon";
import {ParamRegistry} from "../../../src/filters";
import {PropertyRegistry} from "../../../src/jsonschema";
import {Required} from "../../../src/mvc/decorators";

const paramRegistryStub: any = {
required: Sinon.stub()
};

const propertyRegistryStub: any = {
required: Sinon.stub()
};

class Test {
}

const {Required} = Proxyquire.load("../../../src/mvc/decorators/required", {
"../../filters/registries/ParamRegistry": {ParamRegistry: paramRegistryStub},
"../../jsonschema/registries/PropertyRegistry": {PropertyRegistry: propertyRegistryStub}
});

const sandbox = Sinon.createSandbox();
describe("Required", () => {
before(() => {
sandbox.spy(PropertyRegistry, "get");
sandbox.spy(ParamRegistry, "get");
});
after(() => sandbox.restore());

describe("when decorator is used as param", () => {
before(() => {
Required()(Test, "test", 0);
});
it("should called with the correct parameters", () => {
// WHEN
class Test {
test(@Required(null) test: string) {
}
}

after(() => {
paramRegistryStub.required.reset();
});
const metadata = ParamRegistry.get(prototypeOf(Test), "test", 0);
// THEN
metadata.required.should.be.eq(true);

it("should called with the correct parameters", () => {
paramRegistryStub.required.should.have.been.calledWithExactly(Test, "test", 0);
ParamRegistry.get.should.have.been.calledWithExactly(prototypeOf(Test), "test", 0);
metadata.allowedRequiredValues.should.deep.eq([null]);
});
});

describe("when decorator is used as property", () => {
before(() => {
Required(null, "")(Test, "test");
});
it("should called with the correct parameters", () => {
// WHEN
class Test {
@Required(null)
test: string;
}

const metadata = PropertyRegistry.get(prototypeOf(Test), "test");

// THEN
metadata.required.should.be.eq(true);

after(() => {
propertyRegistryStub.required.reset();
PropertyRegistry.get.should.have.been.calledWithExactly(prototypeOf(Test), "test");
metadata.allowedRequiredValues.should.deep.eq([null]);
});
});

describe("when decorator is used in another way", () => {
it("should called with the correct parameters", () => {
propertyRegistryStub.required.should.have.been.calledWithExactly(Test, "test", [null, ""]);


// WHEN
let actualError: any;
try {
@Required(null)
class Test {

test: string;
}
} catch (er) {
actualError = er;
}

actualError.message.should.deep.eq("Required cannot used as class decorator on Test");
});
});
});

0 comments on commit 6fb9c9a

Please sign in to comment.