Skip to content

Commit

Permalink
fix(json-mapper): fix groups deserialize with CollectionOf
Browse files Browse the repository at this point in the history
Closes: #1723
  • Loading branch information
Romakita committed Feb 1, 2022
1 parent 8710a3d commit 1b4642d
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 28 deletions.
59 changes: 35 additions & 24 deletions packages/specs/json-mapper/src/utils/deserialize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isArray, isEmpty, isNil, MetadataTypes, nameOf, objectKeys, Type} from "@tsed/core";
import {alterIgnore, getProperties, JsonEntityStore, JsonHookContext, JsonSchema} from "@tsed/schema";
import {alterIgnore, getProperties, JsonEntityStore, JsonHookContext, JsonPropertyStore, JsonSchema} from "@tsed/schema";
import "../components";
import {JsonMapperContext} from "../domain/JsonMapperContext";
import {getJsonMapperTypes} from "../domain/JsonMapperTypesContainer";
Expand Down Expand Up @@ -70,6 +70,31 @@ function transformType<T = any>(src: any, options: JsonDeserializerOptions<any,
return types?.get(type)?.deserialize<T>(src, context);
}

function mapItemOptions(propStore: JsonPropertyStore, options: JsonDeserializerOptions) {
const itemOpts: JsonDeserializerOptions = {
...options,
store: undefined,
type: propStore.computedType
};

if (propStore.schema.hasGenerics) {
itemOpts.nestedGenerics = propStore.schema.nestedGenerics;
} else if (propStore.schema.isGeneric && options.nestedGenerics) {
const [genericTypes = [], ...nestedGenerics] = options.nestedGenerics;
const genericLabels = propStore.parent.schema.genericLabels || [];

itemOpts.type = genericTypes[genericLabels.indexOf(propStore.schema.genericType)] || Object;

if (itemOpts.type instanceof JsonSchema) {
itemOpts.type = itemOpts.type.getTarget();
}

itemOpts.nestedGenerics = nestedGenerics;
}

return itemOpts;
}

/**
* Transform given plain object to class.
* @param src
Expand All @@ -80,48 +105,34 @@ export function plainObjectToClass<T = any>(src: any, options: JsonDeserializerO
return src;
}

const {type, store = JsonEntityStore.from(type), ...next} = options;

const {type, store = JsonEntityStore.from(type)} = options;
const propertiesMap = getProperties(store, {...options, withIgnoredProps: true});

let keys = objectKeys(src);
let keys = new Set<any>(objectKeys(src));
const additionalProperties = propertiesMap.size ? !!store.schema.get("additionalProperties") || options.additionalProperties : true;

src = alterBeforeDeserialize(src, store.schema, options);

const out: any = new type(src);

propertiesMap.forEach((propStore) => {
const key = options.useAlias
? propStore.parent.schema.getAliasOf(propStore.propertyName) || propStore.propertyName
: propStore.propertyName;

keys = keys.filter((k) => k !== key);

next.type = propStore.computedType;
keys.delete(key);

if (alterIgnore(propStore.itemSchema, options)) {
if (alterIgnore(propStore.schema, options)) {
return;
}

let value = alterValue(propStore.schema, src[key], {...options, self: src});

if (propStore.schema.hasGenerics) {
next.nestedGenerics = propStore.schema.nestedGenerics;
} else if (propStore.schema.isGeneric && options.nestedGenerics) {
const [genericTypes = [], ...nestedGenerics] = options.nestedGenerics;
const genericLabels = propStore.parent.schema.genericLabels || [];

next.type = genericTypes[genericLabels.indexOf(propStore.schema.genericType)] || Object;

if (next.type instanceof JsonSchema) {
next.type = next.type.getTarget();
}

next.nestedGenerics = nestedGenerics;
}
const itemOptions = mapItemOptions(propStore, options);

value = deserialize(value, {
...next,
type: value === src[key] ? next.type : undefined,
...itemOptions,
type: value === src[key] ? itemOptions.type : undefined,
collectionType: propStore.collectionType
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Groups, Property} from "@tsed/schema";
import {CollectionOf, Groups, Property} from "@tsed/schema";
import {expect} from "chai";
import {deserialize, serialize} from "../../src";

Expand All @@ -14,6 +14,10 @@ class Product {

@Groups("group.extended")
description: string;

@CollectionOf(String)
@Groups("creation")
tags: string[];
}

describe("Groups", () => {
Expand Down Expand Up @@ -58,7 +62,7 @@ describe("Groups", () => {
"label": "label"
});
});
it("should deserialize object with groups restriction (groups.summary)", () => {
it("should deserialize object with groups restriction (groups.summary)", () => {
const product = deserialize({
id: "id",
label: "label",
Expand All @@ -72,7 +76,7 @@ describe("Groups", () => {
price: 100
});
});
it("should deserialize object with groups restriction (group.*)", () => {
it("should deserialize object with groups restriction (group.*)", () => {
const product = deserialize({
id: "id",
label: "label",
Expand All @@ -87,6 +91,21 @@ describe("Groups", () => {
description: "description"
});
});
it("should deserialize object without array data (update)", () => {
class CustomRequest {
@CollectionOf(String)
@Groups("creation")
a?: string[];

@Groups("creation")
b?: string;
}

const req = {a: 'a', b: 'b'}

const res = deserialize(req, { type: CustomRequest, groups: ["update"] });
expect(res).to.deep.eq({})
});
});
describe("serialize", () => {
const product = deserialize({
Expand Down Expand Up @@ -131,4 +150,4 @@ describe("Groups", () => {
});
});
});
});
});

0 comments on commit 1b4642d

Please sign in to comment.