Skip to content
Permalink
Browse files

feat(repository): add PATCH and DELETE for HasOne relation

  • Loading branch information...
Raphael Dai authored and bajtos committed Feb 17, 2019
1 parent d298ec8 commit 5936fb9c7224a024f7d406e8f05894cce460a4d4
@@ -7,14 +7,14 @@ import {Application} from '@loopback/core';
import {expect, toJSON} from '@loopback/testlab';
import {
ApplicationWithRepositories,
EntityNotFoundError,
Filter,
juggler,
repository,
RepositoryMixin,
Filter,
EntityNotFoundError,
} from '../..';
import {Address} from '../fixtures/models';
import {CustomerRepository, AddressRepository} from '../fixtures/repositories';
import {AddressRepository, CustomerRepository} from '../fixtures/repositories';

describe('hasOne relation', () => {
// Given a Customer and Address models - see definitions at the bottom
@@ -115,6 +115,91 @@ describe('hasOne relation', () => {
).to.be.rejectedWith(EntityNotFoundError);
});

it('can PATCH hasOne instances', async () => {
const address = await controller.createCustomerAddress(existingCustomerId, {
street: '1 Amedee Bonnet',
zipcode: '69740',
city: 'Genas',
province: 'Rhone',
});

const patchObject = {city: 'Lyon-Genas'};
const arePatched = await controller.patchCustomerAddress(
existingCustomerId,
patchObject,
);

expect(arePatched).to.deepEqual({count: 1});
const patchedData = await addressRepo.findById(address.zipcode);
expect(toJSON(patchedData)).to.deepEqual({
customerId: existingCustomerId,
street: '1 Amedee Bonnet',
zipcode: '69740',
city: 'Lyon-Genas',
province: 'Rhone',
});
});

it('patches the related instance only', async () => {
const bob = await customerRepo.create({name: 'Bob'});
await customerRepo.address(bob.id).create({city: 'Paris'});

const alice = await customerRepo.create({name: 'Alice'});
await customerRepo.address(alice.id).create({city: 'London'});

const result = await controller.patchCustomerAddress(alice.id, {
city: 'New York',
});

expect(result).to.deepEqual({count: 1});

const found = await customerRepo.address(bob.id).get();
expect(toJSON(found)).to.containDeep({city: 'Paris'});
});

it('throws an error when PATCH tries to change the foreignKey', async () => {
try {
await expect(
controller.patchCustomerAddress(existingCustomerId, {
customerId: existingCustomerId + 1,
}),
).to.be.rejectedWith(/Property "customerId" cannot be changed!/);
} catch (err) {}
});

it('can DELETE hasOne relation instances', async () => {
await controller.createCustomerAddress(existingCustomerId, {
street: '1 Amedee Bonnet',
zipcode: '69740',
city: 'Genas',
province: 'Rhone',
});

const areDeleted = await controller.deleteCustomerAddress(
existingCustomerId,
);
expect(areDeleted).to.deepEqual({count: 1});

await expect(
controller.findCustomerAddress(existingCustomerId),
).to.be.rejectedWith(EntityNotFoundError);
});

it('deletes the related model instance only', async () => {
const bob = await customerRepo.create({name: 'Bob'});
await customerRepo.address(bob.id).create({city: 'Paris'});

const alice = await customerRepo.create({name: 'Alice'});
await customerRepo.address(alice.id).create({city: 'London'});

const result = await controller.deleteCustomerAddress(alice.id);

expect(result).to.deepEqual({count: 1});

const found = await addressRepo.find();
expect(found).to.have.length(1);
});

/*---------------- HELPERS -----------------*/

class CustomerController {
@@ -142,6 +227,18 @@ describe('hasOne relation', () => {
) {
return await this.customerRepository.address(customerId).get(filter);
}
async patchCustomerAddress(
customerId: number,
addressData: Partial<Address>,
) {
return await this.customerRepository
.address(customerId)
.patch(addressData);
}

async deleteCustomerAddress(customerId: number) {
return await this.customerRepository.address(customerId).delete();
}
}

function givenApplicationWithMemoryDB() {
@@ -4,15 +4,16 @@
// License text available at https://opensource.org/licenses/MIT

import {Getter} from '@loopback/context';
import {DataObject, Options} from '../../common-types';
import {Count, DataObject, Options} from '../../common-types';
import {EntityNotFoundError} from '../../errors';
import {Entity} from '../../model';
import {Filter} from '../../query';
import {
constrainDataObject,
constrainFilter,
constrainWhere,
EntityCrudRepository,
} from '../../repositories';
import {EntityNotFoundError} from '../../errors';

/**
* CRUD operations for a target repository of a HasMany relation
@@ -39,6 +40,21 @@ export interface HasOneRepository<Target extends Entity> {
filter?: Pick<Filter<Target>, Exclude<keyof Filter<Target>, 'where'>>,
options?: Options,
): Promise<Target>;

/**
* Delete the related target model instance
* @param options
* @returns A promise which resolves the deleted target model instances
*/
delete(options?: Options): Promise<Count>;

/**
* Patch the related target model instance
* @param dataObject The target model fields and their new values to patch
* @param options
* @returns A promise which resolves the patched target model instances
*/
patch(dataObject: DataObject<Target>, options?: Options): Promise<Count>;
}

export class DefaultHasOneRepository<
@@ -87,4 +103,23 @@ export class DefaultHasOneRepository<
}
return found[0];
}
async delete(options?: Options): Promise<Count> {
const targetRepository = await this.getTargetRepository();
return targetRepository.deleteAll(
constrainWhere({}, this.constraint),
options,
);
}

async patch(
dataObject: DataObject<TargetEntity>,
options?: Options,
): Promise<Count> {
const targetRepository = await this.getTargetRepository();
return await targetRepository.updateAll(
constrainDataObject(dataObject, this.constraint),
constrainWhere({}, this.constraint),
options,
);
}
}
@@ -4,4 +4,5 @@
// License text available at https://opensource.org/licenses/MIT

export * from './has-one.decorator';
export * from './has-one.repository';
export * from './has-one-repository.factory';

0 comments on commit 5936fb9

Please sign in to comment.
You can’t perform that action at this time.