Skip to content

Commit

Permalink
fix: use order/customer for test/docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Agnes Lin committed Sep 18, 2019
1 parent 4225c24 commit e09dfd9
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 89 deletions.
36 changes: 21 additions & 15 deletions docs/site/BelongsTo-relation.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,11 @@ Use the relation between `Customer` and `Order` we show above, an `Order`
belongs to a `Customer`.

After setting up the relation in the repository class, the inclusion resolver
allows users to retrieve all addresses along with their related customers
through the following code:
allows users to retrieve all orders along with their related customers through
the following code:

```ts
addressRepo.find({include: [{relation: 'customer'}]});
orderRepo.find({include: [{relation: 'customer'}]});
```

### Enable/disable the inclusion resolvers:
Expand All @@ -304,16 +304,16 @@ The following code snippet shows how to register the inclusion resolver for the
belongsTo relation 'customer':

```ts
export class AddressRepository extends DefaultCrudRepository {
customer: BelongsToAccessor<Customer, typeof Address.prototype.id>;
export class OrderRepository extends DefaultCrudRepository {
customer: BelongsToAccessor<Customer, typeof Order.prototype.id>;

constructor(
dataSource: juggler.DataSource,
customerRepositoryGetter: Getter<CustomerRepository>,
) {
super(Address, dataSource);
super(Order, dataSource);

// we already have this line to create a HasManyRepository factory
// we already have this line to create a BelongsToRepository factory
this.customer = this.createBelongsToAccessorFor(
'customer',
customerRepositoryGetter,
Expand All @@ -329,7 +329,7 @@ export class AddressRepository extends DefaultCrudRepository {
`findById()` methods. Example:

```ts
addressRepository.find({include: [{relation: 'customer'}]});
orderRepository.find({include: [{relation: 'customer'}]});
```

which returns:
Expand All @@ -338,30 +338,36 @@ export class AddressRepository extends DefaultCrudRepository {
[
{
id: 1,
street: 'Warden Rd',
city: 'Thrudheim',
province: 'Asgard',
description: 'Mjolnir',
customerId: 1,
customer: {
id: 12,
name: 'Thor',
},
},
{
id: 2,
street: 'AgentOfShield',
city: 'Culver',
province: 'Cali',
description: 'Shield',
customer: {
id: 10,
name: 'Captain',
},
},
{
id: 3,
description: 'Rocket Raccoon',
customerId: 1,
customer: {
id: 12,
name: 'Thor',
},
},
];
```

- You can delete a relation from `inclusionResolvers` to disable the inclusion
for a certain relation. e.g
`addressRepository.inclusionResolvers.delete('customer')`
`orderRepository.inclusionResolvers.delete('customer')`

{% include note.html content="
Inclusion with custom scope:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import {
import {
Customer,
CustomerRepository,
Address,
AddressRepository,
Order,
OrderRepository,
} from '../fixtures/models';
import {givenBoundCrudRepositories} from '../helpers';

Expand All @@ -37,7 +37,7 @@ export function hasManyRelationAcceptance(
describe('BelongsTo inclusion resolvers - acceptance', () => {
before(deleteAllModelsInDefaultDataSource);
let customerRepo: CustomerRepository;
let addressRepo: AddressRepository;
let orderRepo: OrderRepository;
let existingCustomerId: MixedIdType;

before(
Expand All @@ -46,92 +46,84 @@ export function hasManyRelationAcceptance(
// this config for mongo connector to pass the test.
// however real-world applications might have such config for MongoDB
// setting it up to check if it works fine as well
Address.definition.properties.customerId.type = features.idType;
Address.definition.properties.customerId.mongodb = {
Order.definition.properties.customerId.type = features.idType;
Order.definition.properties.customerId.mongodb = {
dataType: 'ObjectID',
};
// this helper should create the inclusion resolvers for us
({customerRepo, addressRepo} = givenBoundCrudRepositories(
({customerRepo, orderRepo} = givenBoundCrudRepositories(
ctx.dataSource,
repositoryClass,
));
// inclusionResolvers should be defined. And resolver for each
// relation should be created by the belongsToFactory at this point.
expect(customerRepo.inclusionResolvers).to.not.be.undefined();
expect(addressRepo.inclusionResolvers).to.not.be.undefined();
expect(
addressRepo.customer!.inclusionResolver,
).to.not.be.undefined();

// inclusionResolvers shouldn't setup yet at this point
expect(customerRepo.inclusionResolvers).to.deepEqual(new Map());

await ctx.dataSource.automigrate([Customer.name, Address.name]);
await ctx.dataSource.automigrate([Customer.name, Order.name]);
}),
);

beforeEach(async () => {
addressRepo.inclusionResolvers.set(
orderRepo.inclusionResolvers.set(
'customer',
addressRepo.customer!.inclusionResolver,
orderRepo.customer!.inclusionResolver,
);
await customerRepo.deleteAll();
await addressRepo.deleteAll();
await orderRepo.deleteAll();
});

it("defines a repository's inclusionResolvers property", () => {
expect(customerRepo.inclusionResolvers).to.not.be.undefined();
expect(addressRepo.inclusionResolvers).to.not.be.undefined();
expect(orderRepo.inclusionResolvers).to.not.be.undefined();
});

it("throws an error if the repository doesn't have such relation names", async () => {
await addressRepo.create({
street: 'home of Thor Rd.',
city: 'Thrudheim',
province: 'Asgard',
zipcode: '8200',
it("a repository's inclusionResolvers should be an empty map before registering any resolvers", () => {
// inclusionResolvers shouldn't setup yet at this point
expect(customerRepo.inclusionResolvers).to.deepEqual(new Map());
});

it('a relation should have its own inclusionResolver built', () => {
expect(orderRepo.customer.inclusionResolver).to.not.be.undefined();
});

it('throws an error if it tries to query not-exists relation names', async () => {
await orderRepo.create({
description: 'shiba',
customerId: existingCustomerId,
});
await expect(
addressRepo.find({include: [{relation: 'home'}]}),
orderRepo.find({include: [{relation: 'shipment'}]}),
).to.be.rejectedWith(
`Invalid "filter.include" entries: {"relation":"home"}`,
`Invalid "filter.include" entries: {"relation":"shipment"}`,
);
});

it('throws error if the target repository does not have the registered resolver', async () => {
await addressRepo.create({
street: 'home of Thor Rd.',
city: 'Thrudheim',
province: 'Asgard',
zipcode: '8200',
await orderRepo.create({
description: 'shiba',
customerId: existingCustomerId,
});
// unregister the resolver
addressRepo.inclusionResolvers.delete('customer');
orderRepo.inclusionResolvers.delete('customer');

await expect(
addressRepo.find({include: [{relation: 'customer'}]}),
orderRepo.find({include: [{relation: 'customer'}]}),
).to.be.rejectedWith(
`Invalid "filter.include" entries: {"relation":"customer"}`,
);
});

it('simple belongs-to relation retrieve via find() method', async () => {
const thor = await customerRepo.create({name: 'Thor'});
const address = await addressRepo.create({
street: 'home of Thor Rd.',
city: 'Thrudheim',
province: 'Asgard',
zipcode: '8200',
const order = await orderRepo.create({
description: 'Mjolnir',
customerId: thor.id,
});
const result = await addressRepo.find({
const result = await orderRepo.find({
include: [{relation: 'customer'}],
});

const expected = {
...address,
...order,
isShipped: features.emptyValue,
// eslint-disable-next-line @typescript-eslint/camelcase
shipment_id: features.emptyValue,
customer: {
id: thor.id,
name: 'Thor',
Expand All @@ -144,36 +136,36 @@ export function hasManyRelationAcceptance(
it('returns related instances to target models via find() method', async () => {
const thor = await customerRepo.create({name: 'Thor'});
const odin = await customerRepo.create({name: 'Odin'});
const addr1 = await addressRepo.create({
street: 'home of Thor Rd.',
city: 'Thrudheim',
province: 'Asgard',
zipcode: '999',
const order1 = await orderRepo.create({
description: 'Mjolnir',
customerId: thor.id,
});
const addr2 = await addressRepo.create({
street: 'home of Odin Rd.',
city: 'Valhalla',
province: 'Asgard',
zipcode: '000',
const order2 = await orderRepo.create({
description: 'Coffee maker',
customerId: odin.id,
});

const result = await addressRepo.find({
const result = await orderRepo.find({
include: [{relation: 'customer'}],
});

const expected = [
{
...addr1,
...order1,
isShipped: features.emptyValue,
// eslint-disable-next-line @typescript-eslint/camelcase
shipment_id: features.emptyValue,
customer: {
id: thor.id,
name: 'Thor',
parentId: features.emptyValue,
},
},
{
...addr2,
...order2,
isShipped: features.emptyValue,
// eslint-disable-next-line @typescript-eslint/camelcase
shipment_id: features.emptyValue,
customer: {
id: odin.id,
name: 'Odin',
Expand All @@ -187,26 +179,23 @@ export function hasManyRelationAcceptance(
it('returns related instances to target models via findById() method', async () => {
const thor = await customerRepo.create({name: 'Thor'});
const odin = await customerRepo.create({name: 'Odin'});
await addressRepo.create({
street: 'home of Thor Rd.',
city: 'Thrudheim',
province: 'Asgard',
zipcode: '999',
await orderRepo.create({
description: 'Mjolnir',
customerId: thor.id,
});
const addr2 = await addressRepo.create({
street: 'home of Odin Rd.',
city: 'Valhalla',
province: 'Asgard',
zipcode: '000',
const order2 = await orderRepo.create({
description: 'Coffee maker',
customerId: odin.id,
});

const result = await addressRepo.findById(addr2.id, {
const result = await orderRepo.findById(order2.id, {
include: [{relation: 'customer'}],
});
const expected = {
...addr2,
...order2,
isShipped: features.emptyValue,
// eslint-disable-next-line @typescript-eslint/camelcase
shipment_id: features.emptyValue,
customer: {
id: odin.id,
name: 'Odin',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,18 @@ export class ProductRepository extends DefaultCrudRepository<
constructor(
dataSource: juggler.DataSource,
categoryRepository?: Getter<CategoryRepository>,
manyfacturerRepository?: Getter<ManufacturerRepository>,
manufacturerRepository?: Getter<ManufacturerRepository>,
) {
super(Product, dataSource);
if (categoryRepository)
this.category = this.createBelongsToAccessorFor(
'category',
categoryRepository,
);
if (manyfacturerRepository)
if (manufacturerRepository)
this.manufacturer = this.createHasOneRepositoryFactoryFor(
'manufacturer',
manyfacturerRepository,
manufacturerRepository,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ import {
} from '../relation.types';
import {resolveBelongsToMetadata} from './belongs-to.helpers';

/**
* Creates InclusiontResolver for BelongsTo relation.
* Notice that this function only generates the inclusionResolver.
* It doesn't register it for the source reoisitory.
*
* @param meta - resolved BelongsToMetadata
* @param getTargetRepo - target repository i.e where related instances are
*/
export function createBelongsToInclusionResolver<
Target extends Entity,
TargetID,
Expand Down
4 changes: 2 additions & 2 deletions packages/repository/src/relations/relation.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,8 @@ export function reduceAsSingleItem<T>(_acc: T | undefined, it: T) {

/**
* Dedupe an array
* @param {Array} input an array
* @returns {Array} an array with unique items
* @param input - an array of sourceIds
* @returns - an array with unique items
*/
export function deduplicate<T>(input: T[]): T[] {
const uniqArray: T[] = [];
Expand Down

0 comments on commit e09dfd9

Please sign in to comment.