Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#800): Get provider info from GlobalProvider when the info is missing in injector #803

Merged
merged 1 commit into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/common/src/converters/ConverterModule.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Module} from "@tsed/di";
import {ArrayConverter, DateConverter, MapConverter, PrimitiveConverter, SetConverter, SymbolConverter} from "./components";
import {ConverterService} from "./services/ConverterService";

@Module({
imports: [ArrayConverter, DateConverter, MapConverter, PrimitiveConverter, SetConverter, SymbolConverter, ConverterService]
imports: [ConverterService]
})
export class ConverterModule {}
export class ConverterModule {
}
10 changes: 4 additions & 6 deletions packages/common/src/converters/decorators/converter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* @module common/converters
*/
/** */

import {Metadata} from "@tsed/core";
import {CONVERTER} from "../constants/index";
import {registerConverter} from "../registries/ConverterRegistries";
Expand All @@ -22,7 +17,10 @@ export function Converter(...classes: any[]): Function {
throw new Error("Converter decorator need at least one type like String, Date, Class, etc...");
}

registerConverter({provide: target, type: "converter"});
registerConverter({
provide: target,
type: "converter"
});

classes.forEach(clazz => Metadata.set(CONVERTER, target, clazz));
};
Expand Down
31 changes: 27 additions & 4 deletions packages/common/src/converters/services/ConverterService.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {Store} from "@tsed/core";
import {Metadata, Store} from "@tsed/core";
import {InjectorService} from "@tsed/di";
import {inject, TestContext} from "@tsed/testing";
import {assert, expect} from "chai";
import {JsonFoo, JsonFoo1, JsonFoo2, JsonFoo3, JsonFoo4} from "../../../../../test/helper/classes";
import {ConverterService} from "../../../src/converters";
import {PropertyDeserialize, PropertySerialize} from "../../../src/jsonschema/decorators";
import {Default, PropertyDeserialize, PropertySerialize, PropertyType} from "../../../src/jsonschema/decorators";
import {Property} from "../../../src/jsonschema/decorators/property";
import {CONVERTER} from "../constants";

class JsonFoo5 {
@Property()
Expand Down Expand Up @@ -335,8 +337,28 @@ describe("ConverterService", () => {
);
});
});
});

describe("With model annotation", () => {
it("should convert model", () => {
class Model {
@Default("foo")
@PropertyType(String)
public readonly foo: string = "foo";
}

const injector = new InjectorService();
injector.invoke(ConverterService);

const converter = injector.get<ConverterService>(ConverterService)!;

const obj = {foo: "bar"};
const result = converter.deserialize(obj, Model);

expect(result).to.be.instanceof(Model);
expect(result.foo).to.equal("bar");
});
});
});
describe("serialize()", () => {
describe("primitive", () => {
it("should convert empty string to string", () => {
Expand Down Expand Up @@ -469,7 +491,8 @@ describe("ConverterService", () => {
});

describe("isStrictModelValidation()", () => {
class Test {}
class Test {
}

describe("when model is an Object", () => {
it("should return false", () => {
Expand Down
27 changes: 22 additions & 5 deletions packages/common/src/converters/services/ConverterService.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import {getClass, isArrayOrArrayClass, isEmpty, isPrimitiveOrPrimitiveClass, Metadata, Store, Type} from "@tsed/core";
import {Configuration, Injectable, InjectorService} from "@tsed/di";
import {Configuration, GlobalProviders, Injectable, InjectorService} from "@tsed/di";
import {BadRequest} from "ts-httpexceptions";
import {PropertyMetadata} from "../../jsonschema/class/PropertyMetadata";
import {PropertyRegistry} from "../../jsonschema/registries/PropertyRegistry";
import {
ArrayConverter,
DateConverter,
MapConverter,
PrimitiveConverter,
SetConverter,
SymbolConverter
} from "../components";
import {CONVERTER} from "../constants/index";
import {ConverterDeserializationError} from "../errors/ConverterDeserializationError";
import {ConverterSerializationError} from "../errors/ConverterSerializationError";
import {RequiredPropertyError} from "../errors/RequiredPropertyError";
import {UnknownPropertyError} from "../errors/UnknownPropertyError";
import {IConverter, IConverterOptions, IDeserializer, ISerializer} from "../interfaces/index";

@Injectable()
@Injectable({
imports: [
ArrayConverter,
DateConverter,
MapConverter,
PrimitiveConverter,
SetConverter,
SymbolConverter
]
})
export class ConverterService {
private validationModelStrict = true;

Expand Down Expand Up @@ -62,17 +79,17 @@ export class ConverterService {
const serializer: ISerializer = (o: any, opt?: any) => this.serialize(o, Object.assign({}, options, opt));

if (converter && converter.serialize) {
// deserialize from a custom JsonConverter
// serialize from a custom JsonConverter
return converter.serialize(obj, serializer);
}

if (typeof obj.serialize === "function") {
// deserialize from serialize method
// serialize from serialize method
return obj.serialize(options, this);
}

if (typeof obj.toJSON === "function" && !obj.toJSON.$ignore) {
// deserialize from serialize method
// serialize from serialize method
return obj.toJSON();
}

Expand Down
71 changes: 46 additions & 25 deletions packages/common/src/platform/builder/ControllerBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import * as Sinon from "sinon";
import {stub} from "../../../../../test/helper/tools";
import {EndpointMetadata} from "../../mvc/models/EndpointMetadata";
import {EndpointRegistry} from "../../mvc/registries/EndpointRegistry";
import {ControllerProvider} from "../../platform";
import {ControllerProvider, Platform, PlatformApplication, PlatformHandler} from "../../platform";
import {SendResponseMiddleware} from "../middlewares/SendResponseMiddleware";
import {statusAndHeadersMiddleware} from "../middlewares/statusAndHeadersMiddleware";
import {PlatformDriver} from "../services/PlatformDriver";
import {PlatformRouter} from "../services/PlatformRouter";
import {ControllerBuilder} from "./ControllerBuilder";

function getControllerBuilder({propertyKey = "test", withMiddleware = true}: any = {}) {
class TestCtrl {}
class TestCtrl {
}

const use = Sinon.stub();
const router = {
Expand All @@ -25,22 +26,42 @@ function getControllerBuilder({propertyKey = "test", withMiddleware = true}: any
stub(PlatformRouter.createRawRouter).returns(router);

const injector = new InjectorService();

injector.addProvider(PlatformRouter, {
useClass: PlatformRouter
});
injector.addProvider(PlatformHandler, {
useClass: PlatformHandler
});
injector.addProvider(PlatformApplication, {
useClass: PlatformApplication
});
injector.addProvider(Platform, {
useClass: Platform
});

const provider = new ControllerProvider(TestCtrl);

if (withMiddleware) {
provider.middlewares = {
use: [function controllerUse() {}],
useAfter: [function controllerAfter() {}],
useBefore: [function controllerBefore() {}]
use: [function controllerUse() {
}],
useAfter: [function controllerAfter() {
}],
useBefore: [function controllerBefore() {
}]
};
}

const endpoint = new EndpointMetadata({target: TestCtrl, propertyKey});

if (withMiddleware) {
endpoint.before([function endpointBefore() {}]);
endpoint.after([function endpointAfter() {}]);
endpoint.middlewares = [function endpointUse() {}];
endpoint.before([function endpointBefore() {
}]);
endpoint.after([function endpointAfter() {
}]);
endpoint.middlewares = [function endpointUse() {
}];
}

// @ts-ignore
Expand Down Expand Up @@ -116,15 +137,15 @@ describe("ControllerBuilder", () => {
router.use
.getCall(1)
.should.have.been.calledWithExactly(
"/",
Sinon.match.func,
provider.middlewares.use[0],
endpoint.beforeMiddlewares[0],
endpoint.middlewares[0],
endpoint,
Sinon.match.func,
endpoint.afterMiddlewares[0]
);
"/",
Sinon.match.func,
provider.middlewares.use[0],
endpoint.beforeMiddlewares[0],
endpoint.middlewares[0],
endpoint,
Sinon.match.func,
endpoint.afterMiddlewares[0]
);

router.use.getCall(2).should.have.been.calledWithExactly(provider.middlewares.useAfter[0]); // controller
});
Expand All @@ -144,14 +165,14 @@ describe("ControllerBuilder", () => {
router.use
.getCall(1)
.should.have.been.calledWithExactly(
Sinon.match.func,
provider.middlewares.use[0],
endpoint.beforeMiddlewares[0],
endpoint.middlewares[0],
endpoint,
Sinon.match.func,
endpoint.afterMiddlewares[0]
);
Sinon.match.func,
provider.middlewares.use[0],
endpoint.beforeMiddlewares[0],
endpoint.middlewares[0],
endpoint,
Sinon.match.func,
endpoint.afterMiddlewares[0]
);

router.use.getCall(2).should.have.been.calledWithExactly(provider.middlewares.useAfter[0]); // controller
});
Expand Down
2 changes: 1 addition & 1 deletion packages/di/src/class/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {IProvider} from "../interfaces/IProvider";
import {ProviderType} from "../interfaces/ProviderType";
import {TokenProvider} from "../interfaces/TokenProvider";

export class Provider<T> implements IProvider<T> {
export class Provider<T = any> implements IProvider<T> {
@Enumerable()
public root: boolean = false;

Expand Down
12 changes: 10 additions & 2 deletions packages/di/src/services/InjectorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class InjectorService extends Container {
* @param token
* @param instance
*/
public forkProvider(token: TokenProvider, instance?: any): Provider<any> {
public forkProvider(token: TokenProvider, instance?: any): Provider {
const provider = this.addProvider(token).getProvider(token)!;

if (!instance) {
Expand Down Expand Up @@ -178,7 +178,7 @@ export class InjectorService extends Container {
locals: Map<TokenProvider, any> = new LocalsContainer(),
options: Partial<IInvokeOptions<T>> = {}
): T {
const provider = this.getProvider(token);
const provider = this.ensureProvider(token);
let instance: any;

!locals.has(Configuration) && locals.set(Configuration, this.settings);
Expand Down Expand Up @@ -474,6 +474,14 @@ export class InjectorService extends Container {
};
}

protected ensureProvider(token: TokenProvider): Provider | undefined {
if (!this.hasProvider(token) && GlobalProviders.has(token)) {
this.addProvider(token);
}

return this.getProvider(token)!;
}

/**
* Invoke a class method and inject service.
*
Expand Down