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

Commit

Permalink
added a fallback strategy for denormalization (#422)
Browse files Browse the repository at this point in the history
* added a fallback strategy for denormalization

* changed signature of fallbackStrategy to be (id, schema), no point in passing entity when the fallback is only executed when entity is undefined, + updated tests and documentation

Co-authored-by: Paul Armstrong <[email protected]>
  • Loading branch information
bjartebore and paularmstrong authored Feb 22, 2020
1 parent a8ac881 commit 06c4551
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 2 deletions.
53 changes: 53 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ const normalizedData = normalize(data, myArray);
- `value`: The input value of the entity.
- `parent`: The parent object of the input array.
- `key`: The key at which the input array appears on the parent object.
- `fallbackStrategy(key, schema)`: Strategy to use when denormalizing data structures with id references to missing entities.
- `key`: The key at which the input array appears on the parent object.
- `schema`: The schema of the missing entity

#### Instance Methods

Expand Down Expand Up @@ -253,6 +256,56 @@ normalize(data, [patronsSchema]);
}
```

#### `fallbackStrategy` Usage
```js
const users = [
{ id: '1', name: "Emily", requestState: 'SUCCEEDED' },
{ id: '2', name: "Douglas", requestState: 'SUCCEEDED' }
];
const books = [
{id: '1', name: "Book 1", author: 1 },
{id: '2', name: "Book 2", author: 2 },
{id: '3', name: "Book 3", author: 3 }
]

const authorSchema = new schema.Entity('authors');
const bookSchema = new schema.Entity('books', {
author: authorSchema
}, {
fallbackStrategy: (key, schema) => {
return {
[schema.idAttribute]: key,
name: 'Unknown',
requestState: 'NONE'
};
}
});

```


#### Output
```js
[
{
id: '1',
name: "Book 1",
author: { id: '1', name: "Emily", requestState: 'SUCCEEDED' }
},
{
id: '2',
name: "Book 2",
author: { id: '2', name: "Douglas", requestState: 'SUCCEEDED' },
},
{
id: '3',
name: "Book 3",
author: { id: '3', name: "Unknown", requestState: 'NONE' },
}
]

```

### `Object(definition)`

Define a plain object mapping that has values needing to be normalized into Entities. _Note: The same behavior can be defined with shorthand syntax: `{ ... }`_
Expand Down
7 changes: 6 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ export const normalize = (input, schema) => {
};

const unvisitEntity = (id, schema, unvisit, getEntity, cache) => {
const entity = getEntity(id, schema);
let entity = getEntity(id, schema);

if (entity === undefined && schema instanceof EntitySchema) {
entity = schema.fallback(id, schema);
}

if (typeof entity !== 'object' || entity === null) {
return entity;
}
Expand Down
8 changes: 7 additions & 1 deletion src/schemas/Entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ export default class EntitySchema {
mergeStrategy = (entityA, entityB) => {
return { ...entityA, ...entityB };
},
processStrategy = (input) => ({ ...input })
processStrategy = (input) => ({ ...input }),
fallbackStrategy = (key, schema) => undefined
} = options;

this._key = key;
this._getId = typeof idAttribute === 'function' ? idAttribute : getDefaultGetId(idAttribute);
this._idAttribute = idAttribute;
this._mergeStrategy = mergeStrategy;
this._processStrategy = processStrategy;
this._fallbackStrategy = fallbackStrategy;
this.define(definition);
}

Expand All @@ -48,6 +50,10 @@ export default class EntitySchema {
return this._mergeStrategy(entityA, entityB);
}

fallback(id, schema) {
return this._fallbackStrategy(id, schema);
}

normalize(input, parent, key, visit, addEntity, visitedEntities) {
const id = this.getId(input, parent, key);
const entityType = this.key;
Expand Down
37 changes: 37 additions & 0 deletions src/schemas/__tests__/Entity.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,41 @@ describe(`${schema.Entity.name} denormalization`, () => {
// NOTE: Given how immutable data works, referential equality can't be
// maintained with nested denormalization.
});

test('denormalizes with fallback strategy', () => {
const user = new schema.Entity(
'users',
{},
{
idAttribute: 'userId',
fallbackStrategy: (id, schema) => ({
[schema.idAttribute]: id,
name: 'John Doe'
})
}
);
const report = new schema.Entity('reports', {
draftedBy: user,
publishedBy: user
});

const entities = {
reports: {
'123': {
id: '123',
title: 'Weekly report',
draftedBy: '456',
publishedBy: '456'
}
},
users: {}
};

const denormalizedReport = denormalize('123', report, entities);

expect(denormalizedReport.publishedBy).toBe(denormalizedReport.draftedBy);
expect(denormalizedReport.publishedBy.name).toBe('John Doe');
expect(denormalizedReport.publishedBy.userId).toBe('456');
//
});
});

0 comments on commit 06c4551

Please sign in to comment.