Skip to content
This repository has been archived by the owner on Mar 20, 2022. It is now read-only.

Added recursive dependencies support for denormalize method #220

Merged
merged 3 commits into from Jan 30, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/__tests__/__snapshots__/index.test.js.snap
Expand Up @@ -33,6 +33,26 @@ Object {
}
`;

exports[`denormalize denormalizes recursive dependencies 1`] = `
Object {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My expected output would be:

{
  title: "Weekly report",
  user: {
    role: 'manager',
    reports: [ 1 ]
  }
}

This is going too many levels deep and isn't properly keeping track of the fact that we've already come across report 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's reasonable. I changed denormalization logic to confirm described expectations. Please, review last commit.

"title": "Weekly report",
"user": Object {
"reports": Array [
Object {
"title": "Weekly report",
"user": Object {
"reports": Array [
1,
],
"role": "manager",
},
},
],
"role": "manager",
},
}
`;

exports[`normalize can use fully custom entity classes 1`] = `
Object {
"entities": Object {
Expand Down
36 changes: 36 additions & 0 deletions src/__tests__/index.test.js
Expand Up @@ -206,4 +206,40 @@ describe('denormalize', () => {
});
expect(() => denormalize('123', article, entities)).not.toThrow();
});

it('denormalizes recursive dependencies', () => {
const user = new schema.Entity('users');
const report = new schema.Entity('reports');

user.define({
reports: [ report ]
});
report.define({
user: user
});

const entities = {
reports: {
1: {
title: 'Weekly report',
user: 1
},
2: {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have all of this extra setup data (report 2, user 2). Can delete since it's not being used.

title: 'Monthly report',
user: 2
}
},
users: {
1: {
role: 'manager',
reports: [ 1 ]
},
2: {
role: 'user',
reports: []
}
}
};
expect(denormalize('1', report, entities)).toMatchSnapshot();
});
});
11 changes: 6 additions & 5 deletions src/index.js
Expand Up @@ -55,22 +55,23 @@ export const normalize = (input, schema) => {
return { entities, result };
};

const unvisit = (input, schema, entities) => {
if (typeof schema === 'object' && (!schema.normalize || typeof schema.normalize !== 'function')) {
const unvisit = (input, schema, entities, visitedEntities) => {
if (typeof schema === 'object' && (!schema.denormalize || typeof schema.denormalize !== 'function')) {
let method = ObjectUtils.denormalize;
if (Array.isArray(schema)) {
method = ArrayUtils.denormalize;
}
return method(schema, input, unvisit, entities);
return method(schema, input, unvisit, entities, visitedEntities);
}

return schema.denormalize(input, unvisit, entities);
return schema.denormalize(input, unvisit, entities, visitedEntities);
};

export const denormalize = (input, schema, entities) => {
if (!input) {
return input;
}

return unvisit(input, schema, entities);
const visitedEntities = {};
return unvisit(input, schema, entities, visitedEntities);
};
8 changes: 4 additions & 4 deletions src/schemas/Array.js
Expand Up @@ -21,9 +21,9 @@ export const normalize = (schema, input, parent, key, visit, addEntity) => {
return values.map((value, index) => visit(value, parent, key, schema, addEntity));
};

export const denormalize = (schema, input, unvisit, entities) => {
export const denormalize = (schema, input, unvisit, entities, visitedEntities) => {
schema = validateSchema(schema);
return input.map((entityOrId) => unvisit(entityOrId, schema, entities));
return input.map((entityOrId) => unvisit(entityOrId, schema, entities, visitedEntities));
};

export default class ArraySchema extends PolymorphicSchema {
Expand All @@ -34,7 +34,7 @@ export default class ArraySchema extends PolymorphicSchema {
.filter((value) => value !== undefined && value !== null);
}

denormalize(input, unvisit, entities) {
return input.map((value) => this.denormalizeValue(value, unvisit, entities));
denormalize(input, unvisit, entities, visitedEntities) {
return input.map((value) => this.denormalizeValue(value, unvisit, entities, visitedEntities));
}
}
14 changes: 4 additions & 10 deletions src/schemas/Entity.js
@@ -1,3 +1,5 @@
import { denormalize } from './Object';

export default class EntitySchema {
constructor(key, definition = {}, options = {}) {
if (!key || typeof key !== 'string') {
Expand Down Expand Up @@ -51,16 +53,8 @@ export default class EntitySchema {
return this.getId(input, parent, key);
}

denormalize(entityOrId, unvisit, entities) {
denormalize(entityOrId, unvisit, entities, visitedEntities) {
const entity = typeof entityOrId === 'object' ? entityOrId : entities[this.key][entityOrId];
const entityCopy = { ...entity };
Object.keys(this.schema).forEach((key) => {
if (entityCopy.hasOwnProperty(key)) {
const schema = this.schema[key];
entityCopy[key] = unvisit(entityCopy[key], schema, entities);
}
});

return entityCopy;
return denormalize(this.schema, entity, unvisit, entities, visitedEntities);
}
}
22 changes: 19 additions & 3 deletions src/schemas/Object.js
Expand Up @@ -12,12 +12,28 @@ export const normalize = (schema, input, parent, key, visit, addEntity) => {
return object;
};

export const denormalize = (schema, input, unvisit, entities) => {
const denormalizeItem = (id, schema, unvisit, entities, visitedEntities) => {
if (!visitedEntities[schema.key]) {
visitedEntities[schema.key] = {};
}

if (!visitedEntities[schema.key][id]) {
visitedEntities[schema.key][id] = { ...entities[schema.key][id] };
visitedEntities[schema.key][id] = unvisit(id, schema, entities, visitedEntities);
}

return visitedEntities[schema.key][id];
};

export const denormalize = (schema, input, unvisit, entities, visitedEntities) => {
const object = { ...input };
Object.keys(schema).forEach((key) => {
const localSchema = schema[key];
if (object[key]) {
object[key] = unvisit(object[key], localSchema, entities);
if (Array.isArray(object[key])) {
object[key] = unvisit(object[key], schema[key], entities, visitedEntities);
} else {
object[key] = denormalizeItem(object[key], schema[key], unvisit, entities, visitedEntities);
}
}
});
return object;
Expand Down
4 changes: 2 additions & 2 deletions src/schemas/Polymorphic.js
Expand Up @@ -40,11 +40,11 @@ export default class PolymorphicSchema {
{ id: normalizedValue, schema: this.getSchemaAttribute(value, parent, key) };
}

denormalizeValue(value, unvisit, entities) {
denormalizeValue(value, unvisit, entities, visitedEntities) {
if (!this.isSingleSchema && !value.schema) {
return value;
}
const schema = this.isSingleSchema ? this.schema : this.schema[value.schema];
return unvisit(value.id || value, schema, entities);
return unvisit(value.id || value, schema, entities, visitedEntities);
}
}
4 changes: 2 additions & 2 deletions src/schemas/Union.js
Expand Up @@ -12,7 +12,7 @@ export default class UnionSchema extends PolymorphicSchema {
return this.normalizeValue(input, parent, key, visit, addEntity);
}

denormalize(input, unvisit, entities) {
return this.denormalizeValue(input, unvisit, entities);
denormalize(input, unvisit, entities, visitedEntities) {
return this.denormalizeValue(input, unvisit, entities, visitedEntities);
}
}
4 changes: 2 additions & 2 deletions src/schemas/Values.js
Expand Up @@ -11,12 +11,12 @@ export default class ValuesSchema extends PolymorphicSchema {
}, {});
}

denormalize(input, unvisit, entities) {
denormalize(input, unvisit, entities, visitedEntities) {
return Object.keys(input).reduce((output, key) => {
const entityOrId = input[key];
return {
...output,
[key]: this.denormalizeValue(entityOrId, unvisit, entities)
[key]: this.denormalizeValue(entityOrId, unvisit, entities, visitedEntities)
};
}, {});
}
Expand Down