Skip to content

Commit

Permalink
feat(common): Add @PropertySerialize / @PropertyDeserialize decorator
Browse files Browse the repository at this point in the history
Closes: #572
  • Loading branch information
Romakita committed Jun 21, 2019
1 parent 61d9c2b commit 17403e2
Show file tree
Hide file tree
Showing 13 changed files with 1,511 additions and 1,233 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,4 @@
"packages": "packages",
"test": "test"
}
}
}
10 changes: 5 additions & 5 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@
"url": "https://github.com/TypedProject/ts-express-decorators.git"
},
"dependencies": {
"@types/json-schema": "^6.0.1",
"globby": "^8.0.1",
"@types/json-schema": "^7.0.3",
"globby": "^9.2.0",
"json-schema": "^0.2.3",
"rxjs": "^6.4.0",
"ts-httpexceptions": "^3.0.0",
"rxjs": "^6.5.2",
"ts-httpexceptions": "^4.1.0",
"ts-log-debug": "^5.1.0",
"tslib": "^1.9.0"
"tslib": "^1.10.0"
},
"peerDependencies": {
"@tsed/core": "0.0.0-PLACEHOLDER",
Expand Down
17 changes: 14 additions & 3 deletions packages/common/src/converters/services/ConverterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,20 @@ export class ConverterService {
keys.forEach(propertyKey => {
if (typeof obj[propertyKey] !== "function") {
let propertyMetadata = ConverterService.getPropertyMetadata(properties, propertyKey);

let propertyValue = obj[propertyKey];
propertyMetadata = propertyMetadata || ({} as any);
plainObject[propertyMetadata!.name || propertyKey] = this.serialize(obj[propertyKey], {

propertyValue = this.serialize(propertyValue, {
checkRequiredValue // ,
// TODO revert change
// type: propertyMetadata!.type
});

if (typeof propertyMetadata!.onSerialize === "function") {
propertyValue = propertyMetadata!.onSerialize(propertyValue);
}

plainObject[propertyMetadata!.name || propertyKey] = propertyValue;
}
});

Expand Down Expand Up @@ -228,11 +235,15 @@ export class ConverterService {

propertyMetadata = propertyMetadata || ({} as any);

const propertyValue = obj[propertyMetadata!.name] || obj[propertyName];
let propertyValue = obj[propertyMetadata!.name] || obj[propertyName];
const propertyKey = propertyMetadata!.propertyKey || propertyName;

try {
if (typeof instance[propertyKey] !== "function") {
if (typeof propertyMetadata!.onDeserialize === "function") {
propertyValue = propertyMetadata!.onDeserialize(propertyValue);
}

instance[propertyKey] = this.deserialize(
propertyValue,
propertyMetadata!.isCollection ? propertyMetadata!.collectionType : propertyMetadata!.type,
Expand Down
6 changes: 6 additions & 0 deletions packages/common/src/jsonschema/class/PropertyMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ export class PropertyMetadata extends Storable implements IPropertyOptions {
@Enumerable()
public ignoreProperty: boolean = false;

@Enumerable()
public onSerialize: Function;

@Enumerable()
public onDeserialize: Function;

constructor(target: any, propertyKey: any) {
super(target, propertyKey);
this.createJsonSchema();
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/jsonschema/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export * from "./property";
export * from "./propertyName";
export * from "./propertyType";
export * from "./propertySerialize";
export * from "./propertyDeserialize";
export * from "./default";
export * from "./email";
export * from "./enum";
Expand Down
27 changes: 27 additions & 0 deletions packages/common/src/jsonschema/decorators/propertyDeserialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {PropertyMetadata} from "../class/PropertyMetadata";
import {PropertyFn} from "./property";

/**
* Call the function before property is deserialization.
*
* ## Example
*
* ```typescript
* class Model {
* @PropertyDeserialize(v => v + 1)
* property: string;
* }
* ```
*
* @param {Function} fn
* @returns {Function}
* @decorator
* @converters
* @jsonschema
* @property
*/
export function PropertyDeserialize(fn: (value: any) => any) {
return PropertyFn((propertyMetadata: PropertyMetadata) => {
propertyMetadata.onDeserialize = fn;
});
}
27 changes: 27 additions & 0 deletions packages/common/src/jsonschema/decorators/propertySerialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {PropertyMetadata} from "../class/PropertyMetadata";
import {PropertyFn} from "./property";

/**
* Call the function after property serialization.
*
* ## Example
*
* ```typescript
* class Model {
* @PropertyDeserialize(v => v + 1)
* property: string;
* }
* ```
*
* @param {Function} fn
* @returns {Function}
* @decorator
* @converters
* @jsonschema
* @property
*/
export function PropertySerialize(fn: (value: any) => any) {
return PropertyFn((propertyMetadata: PropertyMetadata) => {
propertyMetadata.onSerialize = fn;
});
}
32 changes: 32 additions & 0 deletions packages/common/test/converters/services/ConverterService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 {Property} from "../../../src/jsonschema/decorators/property";

class JsonFoo5 {
Expand Down Expand Up @@ -217,6 +218,20 @@ describe("ConverterService", () => {
});
});

describe("when onDeserialize is used on property", () => {
it("should use function to Deserialize property", () => {
class Test {
@PropertyDeserialize(v => v + "to")
test: string;
}

const result = converterService.deserialize({test: "test"}, Test);

result.should.instanceof(Test);
result.test.should.eq("testto");
});
});

describe("deserialization error", () => {
it("should emit a BadRequest when the number parsing failed", () => {
assert.throws(() => converterService.deserialize("NK1", Number), "Cast error. Expression value is not a number.");
Expand Down Expand Up @@ -556,5 +571,22 @@ describe("ConverterService", () => {
});
});
});

describe("when onSerialize is used on property", () => {
it("should use function to Deserialize property", () => {
class Test {
@PropertySerialize(v => v + "to")
test: string;
}

const input = new Test();
input.test = "test";

const result = converterService.serialize(input);

result.should.not.instanceof(Test);
result.test.should.eq("testto");
});
});
});
});
Loading

0 comments on commit 17403e2

Please sign in to comment.