Skip to content

Commit

Permalink
Merge pull request #565 from Darkein/feature/multiPrimaryKeys
Browse files Browse the repository at this point in the history
feat: composite primary key
  • Loading branch information
michaelyali committed Jul 4, 2020
2 parents 68947a8 + eda8b3c commit 644acfd
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 22 deletions.
20 changes: 13 additions & 7 deletions packages/crud-typeorm/src/typeorm-crud.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,16 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
if (returnShallow) {
return saved;
} else {
const primaryParam = this.getPrimaryParam(req.options);
const primaryParams = this.getPrimaryParams(req.options);

/* istanbul ignore if */
if (!primaryParam && /* istanbul ignore next */ isNil(saved[primaryParam])) {
/* istanbul ignore next */
if (!primaryParams.length && primaryParams.some((p) => isNil(saved[p]))) {
return saved;
} else {
req.parsed.search = { [primaryParam]: saved[primaryParam] };
req.parsed.search = primaryParams.reduce(
(acc, p) => ({ ...acc, [p]: saved[p] }),
{},
);
return this.getOneOrFail(req);
}
}
Expand Down Expand Up @@ -212,14 +215,17 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
if (returnShallow) {
return replaced;
} else {
const primaryParam = this.getPrimaryParam(req.options);
const primaryParams = this.getPrimaryParams(req.options);

/* istanbul ignore if */
if (!primaryParam) {
if (!primaryParams.length) {
return replaced;
}

req.parsed.search = { [primaryParam]: replaced[primaryParam] };
req.parsed.search = primaryParams.reduce(
(acc, p) => ({ ...acc, [p]: replaced[p] }),
{},
);
return this.getOneOrFail(req);
}
}
Expand Down
22 changes: 22 additions & 0 deletions packages/crud-typeorm/test/c.basic-crud.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ describe('#crud-typeorm', () => {
constructor(public service: UsersService) {}
}

@Crud({
model: { type: User },
params: {
companyId: { field: 'companyId', type: 'number', primary: true },
profileId: { field: 'profileId', type: 'number', primary: true },
},
})
@Controller('users4')
class UsersController4 {
constructor(public service: UsersService) {}
}

@Crud({
model: { type: Device },
params: {
Expand Down Expand Up @@ -268,6 +280,7 @@ describe('#crud-typeorm', () => {
UsersController,
UsersController2,
UsersController3,
UsersController4,
DevicesController,
],
providers: [
Expand Down Expand Up @@ -406,6 +419,15 @@ describe('#crud-typeorm', () => {
done();
});
});
it('should return an entity with compound key', (done) => {
return request(server)
.get('/users4/1/5')
.end((_, res) => {
expect(res.status).toBe(200);
expect(res.body.id).toBe(5);
done();
});
});
it('should return an entity with and set cache', (done) => {
return request(server)
.get('/companies/1/users/1')
Expand Down
14 changes: 8 additions & 6 deletions packages/crud/src/crud/crud-routes.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class CrudRoutesFactory {
: isObjectFull(CrudConfigService.config.params)
? CrudConfigService.config.params
: {};
const hasPrimary = this.getPrimaryParam();
const hasPrimary = this.getPrimaryParams().length > 0;
if (!hasPrimary) {
this.options.params['id'] = {
field: 'id',
Expand Down Expand Up @@ -283,7 +283,9 @@ export class CrudRoutesFactory {
}

private createRoutes(routesSchema: BaseRoute[]) {
const primaryParam = this.getPrimaryParam();
const primaryParams = this.getPrimaryParams().filter(
(param) => !this.options.params[param].disabled,
);

routesSchema.forEach((route) => {
if (this.canCreateRoute(route.name)) {
Expand All @@ -294,8 +296,8 @@ export class CrudRoutesFactory {
this.setBaseRouteMeta(route.name);
}

if (route.withParams && !this.options.params[primaryParam].disabled) {
route.path = `/:${primaryParam}`;
if (route.withParams && primaryParams.length > 0) {
route.path = primaryParams.map((param) => `/:${param}`).join('');
}
});
}
Expand Down Expand Up @@ -391,8 +393,8 @@ export class CrudRoutesFactory {
}
}

private getPrimaryParam(): string {
return objKeys(this.options.params).find(
private getPrimaryParams(): string[] {
return objKeys(this.options.params).filter(
(param) => this.options.params[param] && this.options.params[param].primary,
);
}
Expand Down
11 changes: 3 additions & 8 deletions packages/crud/src/services/crud-service.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,11 @@ export abstract class CrudService<T> {
* Get primary param name from CrudOptions
* @param options
*/
getPrimaryParam(options: CrudRequestOptions): string {
const param = objKeys(options.params).find(
getPrimaryParams(options: CrudRequestOptions): string[] {
const params = objKeys(options.params).filter(
(n) => options.params[n] && options.params[n].primary,
);

/* istanbul ignore if */
if (!param) {
return undefined;
}

return options.params[param].field;
return params.map((p) => options.params[p].field);
}
}
24 changes: 23 additions & 1 deletion packages/crud/test/crud-request.interceptor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,31 @@ describe('#crud', () => {
}
}

@Crud({
model: { type: TestModel },
params: {
someParam: { field: 'someParam', type: 'number', primary: true },
someParam2: { field: 'someParam2', type: 'number', primary: true },
},
})
@Controller('test5')
class Test5Controller {
constructor(public service: TestService<TestModel>) {}
}

let $: supertest.SuperTest<supertest.Test>;
let app: NestApplication;

beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [TestService],
controllers: [TestController, Test2Controller, Test3Controller, Test4Controller],
controllers: [
TestController,
Test2Controller,
Test3Controller,
Test4Controller,
Test5Controller,
],
}).compile();
app = module.createNestApplication();
await app.init();
Expand Down Expand Up @@ -195,6 +213,10 @@ describe('#crud', () => {
expect(res.body.filter[0].value).toBe(1);
});

it('should parse multiple primary key', async () => {
const res = await $.get('/test5/123/456').expect(200);
});

it('should work like before', async () => {
const res = await $.get('/test2/normal/0').expect(200);
expect(res.body.filter).toHaveLength(1);
Expand Down

0 comments on commit 644acfd

Please sign in to comment.