Skip to content

Commit

Permalink
feat(mvc): Authenticated can be used on class controller
Browse files Browse the repository at this point in the history
Closes: #467
  • Loading branch information
Romakita committed Apr 17, 2019
1 parent 3bfff57 commit fa16810
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 25 deletions.
33 changes: 28 additions & 5 deletions packages/common/src/mvc/decorators/method/authenticated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import {Store} from "@tsed/core";
import {classOf, DecoratorParameters, descriptorOf, getDecoratorType, methodsOf, prototypeOf, Store} from "@tsed/core";
import {AuthenticatedMiddleware} from "../../components/AuthenticatedMiddleware";
import {UseBefore} from "./useBefore";

function decorate(options: any) {
return Store.decorate((store: Store) => {
store.set(AuthenticatedMiddleware, options).merge("responses", {"403": {description: "Forbidden"}});

return UseBefore(AuthenticatedMiddleware);
});
}

/**
* Set authentication strategy on your endpoint.
*
Expand All @@ -20,9 +28,24 @@ import {UseBefore} from "./useBefore";
* @decorator
*/
export function Authenticated(options?: any) {
return Store.decorate((store: Store) => {
store.set(AuthenticatedMiddleware, options).merge("responses", {"403": {description: "Forbidden"}});
return <T>(...parameters: DecoratorParameters): TypedPropertyDescriptor<T> | void => {
switch (getDecoratorType(parameters, true)) {
case "method":
return decorate(options)(...parameters);

return UseBefore(AuthenticatedMiddleware);
});
case "class":
const [klass] = parameters;

methodsOf(klass).forEach(({target, propertyKey}) => {
if (target !== classOf(klass)) {
prototypeOf(klass)[propertyKey] = function anonymous(...args: any) {
return prototypeOf(target)[propertyKey].apply(this, args);
};
}

decorate(options)(prototypeOf(klass), propertyKey, descriptorOf(klass, propertyKey));
});
break;
}
};
}
92 changes: 72 additions & 20 deletions packages/common/test/mvc/decorators/method/authenticated.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,86 @@ const {Authenticated} = Proxyquire.load("../../../../src/mvc/decorators/method/a
"./useBefore": {UseBefore: useBeforeStub}
});

class Test {
test() {
}
}

describe("Authenticated", () => {
before(() => {
this.descriptor = {};
this.options = {options: "options"};
describe("when the decorator is used on a method", () => {
it("should set metadata", () => {
// GIVEN

Authenticated(this.options)(Test, "test", descriptorOf(Test, "test"));
this.store = Store.fromMethod(Test, "test");
});
// WHEN
class Test {
@Authenticated({options: "options"})
test() {
}
}

after(() => {
this.store.clear();
});
const store = Store.fromMethod(Test, "test");

it("should set metadata", () => {
expect(this.store.get(AuthenticatedMiddleware)).to.deep.eq(this.options);
// THEN
expect(store.get(AuthenticatedMiddleware)).to.deep.eq({options: "options"});
expect(store.get("responses")).to.deep.eq({"403": {description: "Forbidden"}});
useBeforeStub.should.be.calledWithExactly(AuthenticatedMiddleware);
middleware.should.be.calledWithExactly(Test.prototype, "test", descriptorOf(Test, "test"));
store.clear();
});
});

it("should set responses metadata", () => {
expect(this.store.get("responses")).to.deep.eq({"403": {description: "Forbidden"}});
describe("when the decorator is used on a controller", () => {
it("should set metadata", () => {
// GIVEN

// WHEN
@Authenticated({options: "options"})
class Test {
test() {
}
}

const store = Store.fromMethod(Test, "test");

// THEN
expect(store.get(AuthenticatedMiddleware)).to.deep.eq({options: "options"});
expect(store.get("responses")).to.deep.eq({"403": {description: "Forbidden"}});
useBeforeStub.should.be.calledWithExactly(AuthenticatedMiddleware);
middleware.should.be.calledWithExactly(Test.prototype, "test", descriptorOf(Test, "test"));
store.clear();
});
});

it("should create middleware", () => {
useBeforeStub.should.be.calledWithExactly(AuthenticatedMiddleware);
middleware.should.be.calledWithExactly(Test, "test", descriptorOf(Test, "test"));
describe("when the decorator is used on a controller with class inheritance", () => {
it("should set metadata", () => {
// GIVEN
class Base {
value = "1";

test2(option: string) {
return "test2" + option + this.value;
}
}

// WHEN
@Authenticated({options: "options"})
class Test extends Base {
test() {
}
}

const store1 = Store.fromMethod(Test, "test");
const store2 = Store.fromMethod(Test, "test2");

// THEN
expect(store1.get(AuthenticatedMiddleware)).to.deep.eq({options: "options"});
expect(store1.get("responses")).to.deep.eq({"403": {description: "Forbidden"}});

expect(store2.get(AuthenticatedMiddleware)).to.deep.eq({options: "options"});
expect(store2.get("responses")).to.deep.eq({"403": {description: "Forbidden"}});

expect(new Test().test2("test")).to.eq("test2test1");

useBeforeStub.should.be.calledWithExactly(AuthenticatedMiddleware);
middleware.should.be.calledWithExactly(Test.prototype, "test", descriptorOf(Test, "test"));
store1.clear();
store2.clear();
});
});
});

0 comments on commit fa16810

Please sign in to comment.