Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(repository): delete & patch operations for HasOne relation #2414

Merged
merged 1 commit into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
});
bajtos marked this conversation as resolved.
Show resolved Hide resolved

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

class CustomerController {
Expand Down Expand Up @@ -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() {
Expand Down
39 changes: 37 additions & 2 deletions packages/repository/src/relations/has-one/has-one.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<
Expand Down Expand Up @@ -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,
);
}
}
1 change: 1 addition & 0 deletions packages/repository/src/relations/has-one/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
// License text available at https://opensource.org/licenses/MIT

export * from './has-one.decorator';
export * from './has-one.repository';
bajtos marked this conversation as resolved.
Show resolved Hide resolved
export * from './has-one-repository.factory';