Skip to content

Commit f58f1bc

Browse files
committed
chore: improvement of resource
1 parent 37b6ecf commit f58f1bc

22 files changed

Lines changed: 1029 additions & 244 deletions

examples/sample-server/src/app.module.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,25 @@ import {
1111
USER_ENTITY_KEY,
1212
} from './auth';
1313
import {
14-
createPetResource,
14+
petResource,
1515
PetEntity,
1616
PetTagEntity,
1717
PET_TAG_ENTITY_KEY,
1818
} from './resources/pet';
1919
import {
20-
createPetVaccinationResource,
20+
petVaccinationResource,
2121
PetVaccinationEntity,
2222
} from './resources/pet-vaccination';
23-
import { createTagResource, TagEntity } from './resources/tag';
23+
import { tagResource, TagEntity } from './resources/tag';
2424
import { PetShareEntity, PetShareModule } from './resources/pet-share';
2525
import { PET_SHARE_ENTITY_KEY } from './resources/pet-share/pet-share.constants';
2626
import { PetTransferModule } from './resources/pet-transfer';
2727
import { AdminModule } from './admin';
2828
import {
2929
AppointmentEntity,
3030
ReminderEntity,
31-
createAppointmentResource,
32-
createReminderResource,
31+
appointmentResource,
32+
reminderResource,
3333
} from './resources/appointment';
3434
import { AuditLogEntity, AuditModule } from './audit';
3535
import { AUDIT_LOG_ENTITY_KEY } from './audit/audit-log.constants';
@@ -79,11 +79,11 @@ import { EventsModule } from './events';
7979
// pet and petVaccination entities are auto-contributed by their
8080
// respective defineResource() bundles below.
8181
resources: [
82-
createPetResource(),
83-
createPetVaccinationResource(),
84-
createTagResource(),
85-
createAppointmentResource(),
86-
createReminderResource(),
82+
petResource,
83+
petVaccinationResource,
84+
tagResource,
85+
appointmentResource,
86+
reminderResource,
8787
],
8888
}),
8989
PetShareModule,

examples/sample-server/src/resources/appointment/appointment.resource.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ import { Operation } from '@concepta/nestjs-common';
22
import { defineResource } from '@bitwild/rockets';
33
import { OwnerScopeHook } from '@bitwild/rockets-core';
44
import { AppointmentEntity } from './appointment.entity';
5+
import { ReminderEntity } from './reminder.entity';
56
import {
67
AppointmentCreateDto,
78
AppointmentResponseDto,
89
} from './appointment.dto';
910
import { AppointmentCreateHandler } from './appointment-create.handler';
10-
import {
11-
APPOINTMENT_ENTITY_KEY,
12-
REMINDER_ENTITY_KEY,
13-
} from './appointment.constants';
11+
import { APPOINTMENT_ENTITY_KEY } from './appointment.constants';
1412

1513
/**
1614
* Create is handled by `AppointmentCreateHandler`, which wraps an
@@ -34,11 +32,7 @@ export const appointmentResource = defineResource({
3432
Operation.Create,
3533
Operation.Delete,
3634
],
37-
relations: [{ target: REMINDER_ENTITY_KEY, propertyName: 'reminders' }],
35+
relations: (relation) => [relation(ReminderEntity, 'reminders')],
3836
hooks: [OwnerScopeHook],
3937
handlers: { create: AppointmentCreateHandler },
4038
});
41-
42-
export function createAppointmentResource(): typeof appointmentResource {
43-
return appointmentResource;
44-
}

examples/sample-server/src/resources/appointment/reminder.resource.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { Operation } from '@concepta/nestjs-common';
22
import { defineResource } from '@bitwild/rockets';
33
import { ReminderEntity } from './reminder.entity';
4+
import { AppointmentEntity } from './appointment.entity';
45
import { ReminderResponseDto } from './appointment.dto';
56
import { ReminderOwnerScopeHook } from './reminder-owner-scope.hook';
67
import { REMINDER_ENTITY_KEY } from './appointment.constants';
78

89
/**
910
* Read-only — reminder lifecycle is owned by `AppointmentCreateHandler`
1011
* and future workflows (mark-sent, reschedule), not direct CRUD.
12+
*
13+
* The `relation()` helper declares the inverse of
14+
* `AppointmentEntity.reminders`. The lazy `() => AppointmentEntity`
15+
* thunk avoids a load-order cycle between `appointment.entity.ts` and
16+
* `reminder.entity.ts`.
1117
*/
1218
export const reminderResource = defineResource({
1319
key: REMINDER_ENTITY_KEY,
@@ -16,9 +22,6 @@ export const reminderResource = defineResource({
1622
tags: ['Reminders'],
1723
dto: { response: ReminderResponseDto },
1824
operations: [Operation.List, Operation.Read],
25+
relations: (relation) => [relation(() => AppointmentEntity, 'appointment')],
1926
hooks: [ReminderOwnerScopeHook],
2027
});
21-
22-
export function createReminderResource(): typeof reminderResource {
23-
return reminderResource;
24-
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export { createPetVaccinationResource } from './pet-vaccination.resource';
1+
export { petVaccinationResource } from './pet-vaccination.resource';
22
export { PetVaccinationEntity } from './pet-vaccination.entity';
3+
export { PET_VACCINATION_ENTITY_KEY } from './pet-vaccination.constants';

examples/sample-server/src/resources/pet-vaccination/pet-vaccination.resource.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Operation } from '@concepta/nestjs-common';
22
import { defineResource } from '@bitwild/rockets';
33
import { PetVaccinationEntity } from './pet-vaccination.entity';
4+
import { PetEntity } from '../pet/pet.entity';
45
import {
56
PetVaccinationCreateDto,
67
PetVaccinationResponseDto,
@@ -13,6 +14,11 @@ import { PET_VACCINATION_ENTITY_KEY } from './pet-vaccination.constants';
1314
* Exposes List/Read/Create/Delete (no Update). Paginated DTO is
1415
* auto-generated from `PetVaccinationResponseDto` — no hand-written
1516
* paginated class is required.
17+
*
18+
* The `relation()` helper declares the inverse of
19+
* `PetEntity.vaccinations`. The lazy `() => PetEntity` thunk avoids a
20+
* load-order cycle between `pet.entity.ts` and
21+
* `pet-vaccination.entity.ts`.
1622
*/
1723
export const petVaccinationResource = defineResource({
1824
key: PET_VACCINATION_ENTITY_KEY,
@@ -29,8 +35,5 @@ export const petVaccinationResource = defineResource({
2935
Operation.Create,
3036
Operation.Delete,
3137
],
38+
relations: (relation) => [relation(() => PetEntity, 'pet')],
3239
});
33-
34-
export function createPetVaccinationResource(): typeof petVaccinationResource {
35-
return petVaccinationResource;
36-
}

examples/sample-server/src/resources/pet/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { createPetResource } from './pet.resource';
1+
export { petResource } from './pet.resource';
22
export { PetEntity } from './pet.entity';
33
export { PetTagEntity } from './pet-tag.entity';
44
export { PET_ENTITY_KEY } from './pet.constants';

examples/sample-server/src/resources/pet/pet.resource.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { PetCreateHandler } from './pet-create.handler';
77
import { PetUpdateHandler } from './pet-update.handler';
88
import { PetOwnerOrSharedHook } from '../pet-share/pet-owner-or-shared.hook';
99
import { AuditLogHook } from '../../audit/audit-log.hook';
10-
import { PET_VACCINATION_ENTITY_KEY } from '../pet-vaccination/pet-vaccination.constants';
11-
import { TAG_ENTITY_KEY } from '../tag/tag.constants';
10+
import { PetVaccinationEntity } from '../pet-vaccination/pet-vaccination.entity';
11+
import { TagEntity } from '../tag/tag.entity';
1212
import { PetTagSyncer } from './pet-tag.syncer';
1313

1414
/**
@@ -36,9 +36,9 @@ export const petResource = defineResource({
3636
Operation.SoftDelete,
3737
Operation.Restore,
3838
],
39-
relations: [
40-
{ target: PET_VACCINATION_ENTITY_KEY, propertyName: 'vaccinations' },
41-
{ target: TAG_ENTITY_KEY, propertyName: 'tags' },
39+
relations: (relation) => [
40+
relation(PetVaccinationEntity, 'vaccinations'),
41+
relation(TagEntity, 'tags'),
4242
],
4343
hooks: [PetOwnerOrSharedHook, AuditLogHook],
4444
handlers: { create: PetCreateHandler, update: PetUpdateHandler },
@@ -50,7 +50,3 @@ export const petResource = defineResource({
5050
},
5151
},
5252
});
53-
54-
export function createPetResource(): typeof petResource {
55-
return petResource;
56-
}

examples/sample-server/src/resources/tag/tag.resource.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { defineResource } from '@bitwild/rockets';
22
import { TagEntity } from './tag.entity';
3+
import { PetEntity } from '../pet/pet.entity';
34
import { TagCreateDto, TagResponseDto, TagUpdateDto } from './tag.dto';
45
import { TAG_ENTITY_KEY } from './tag.constants';
56

67
/**
78
* Shared global catalog — no owner scoping. Authorization for tag use
89
* happens on the Pet side.
10+
*
11+
* The `relation()` helper declares the inverse of `PetEntity.tags`
12+
* (M:N). The lazy `() => PetEntity` thunk avoids a load-order cycle
13+
* between `pet.entity.ts` and `tag.entity.ts`. `include: 'never'` keeps
14+
* the `pets` collection registered for cross-resource validation but
15+
* off the controller surface — clients query pets-by-tag via `/pets`.
916
*/
1017
export const tagResource = defineResource({
1118
key: TAG_ENTITY_KEY,
@@ -17,8 +24,7 @@ export const tagResource = defineResource({
1724
create: TagCreateDto,
1825
update: TagUpdateDto,
1926
},
27+
relations: (relation) => [
28+
relation(() => PetEntity, 'pets', { include: 'never' }),
29+
],
2030
});
21-
22-
export function createTagResource(): typeof tagResource {
23-
return tagResource;
24-
}

packages/rockets-common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,4 @@ export {
158158
} from './utils/error-logging.helper';
159159
export type { ErrorDetails } from './utils/error-logging.helper';
160160
export { createRepositoryContext } from './utils/repository-context.helper';
161+
export { stripUndefined } from './utils/strip-undefined.helper';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Drop keys whose value is `undefined`. Used so a PATCH that only sets
3+
* some fields does not wipe the others back to `undefined` on update.
4+
*/
5+
export function stripUndefined<T extends object>(input: T): Partial<T> {
6+
return Object.fromEntries(
7+
Object.entries(input).filter(([, v]) => v !== undefined),
8+
) as Partial<T>;
9+
}

0 commit comments

Comments
 (0)