From 22b22de741eeecada32e3a8ea01c3445fdea17fc Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 31 Jan 2024 12:57:02 +0100 Subject: [PATCH 1/2] deps: upgrade zone.js from 0.14.2 to 0.14.3 (#2207) Co-authored-by: snyk-bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67478a193a..ce38c81f9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "util": "^0.12.5", "uuid": "^9.0.1", "webpack": "5.89.0", - "zone.js": "~0.14.2" + "zone.js": "^0.14.3" }, "devDependencies": { "@angular-devkit/build-angular": "^17.0.3", @@ -29690,9 +29690,9 @@ "dev": true }, "node_modules/zone.js": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.2.tgz", - "integrity": "sha512-X4U7J1isDhoOmHmFWiLhloWc2lzMkdnumtfQ1LXzf/IOZp5NQYuMUTaviVzG/q1ugMBIXzin2AqeVJUoSEkNyQ==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.3.tgz", + "integrity": "sha512-jYoNqF046Q+JfcZSItRSt+oXFcpXL88yq7XAZjb/NKTS7w2hHpKjRJ3VlFD1k75wMaRRXNUt5vrZVlygiMyHbA==", "dependencies": { "tslib": "^2.3.0" } diff --git a/package.json b/package.json index bab0be7e3a..c778cf80d6 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "util": "^0.12.5", "uuid": "^9.0.1", "webpack": "5.89.0", - "zone.js": "~0.14.2" + "zone.js": "~0.14.3" }, "devDependencies": { "@angular-devkit/build-angular": "^17.0.3", From f86ca9d47c37a578936f659937ab19c55caf4f41 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 1 Feb 2024 13:30:54 +0100 Subject: [PATCH 2/2] feat: remove short ID usage (#2148) closes #1526 Co-authored-by: Sebastian --- .../activity-card/activity-card.stories.ts | 9 +- .../roll-call-setup.component.spec.ts | 9 +- .../roll-call-setup.component.ts | 4 +- .../roll-call-setup.stories.ts | 2 +- .../roll-call/roll-call.component.spec.ts | 16 +-- .../attendance-calendar.component.spec.ts | 10 +- .../attendance/attendance.service.spec.ts | 25 ++--- .../attendance/attendance.service.ts | 15 +-- .../demo-activity-events-generator.service.ts | 2 +- .../attendance/model/activity-attendance.ts | 2 +- .../aser/aser-component/aser.component.ts | 27 ++--- .../recent-attendance-blocks.component.ts | 2 +- .../children/children.service.spec.ts | 101 +++++++++--------- .../children/children.service.ts | 36 ++----- .../model/educational-material.ts | 12 ++- .../health-checkup.component.spec.ts | 10 -- .../health-checkup.component.ts | 27 ++--- .../note-details.component.spec.ts | 5 +- .../notes-related-to-entity.component.spec.ts | 22 ++-- .../notes-related-to-entity.component.ts | 10 +- .../child-school-overview.component.spec.ts | 14 ++- .../child-school-overview.component.ts | 10 +- .../display-participants-count.component.ts | 2 +- .../admin-entity-field.component.html | 13 +-- .../admin-entity-field.component.ts | 4 +- .../configurable-enum.datatype.spec.ts | 4 +- .../configurable-enum.service.spec.ts | 2 +- .../configurable-enum.service.ts | 4 +- .../configure-enum-popup.component.ts | 4 +- .../display-entity-array.component.spec.ts | 9 +- .../display-entity-array.stories.ts | 4 +- .../entity-array.datatype.spec.ts | 11 -- .../entity-array/entity-array.datatype.ts | 12 --- .../display-entity.component.ts | 2 +- .../entities-table.component.spec.ts | 4 +- .../entities-table.component.ts | 2 +- .../dynamic-validators.service.spec.ts | 2 +- .../dynamic-validators.service.ts | 2 +- .../entity-form/entity-form.service.spec.ts | 15 +-- .../entity-select.component.spec.ts | 46 +------- .../entity-select/entity-select.component.ts | 30 ++---- src/app/core/config/config-fix.ts | 2 +- .../entity-details.component.spec.ts | 40 +++++-- .../entity-details.component.ts | 11 +- .../entity-details/entity-details.stories.ts | 2 +- .../form/form.component.spec.ts | 2 +- .../entity-details/form/form.component.ts | 2 +- .../related-entities.component.spec.ts | 2 + ...ted-time-period-entities.component.spec.ts | 6 +- .../entity-actions/cascading-entity-action.ts | 6 +- .../entity-delete.service.spec.ts | 14 +-- .../entity-actions/entity-delete.service.ts | 6 +- .../core/entity/entity-config.service.spec.ts | 6 +- .../entity-mapper.service.spec.ts | 75 ++++++------- .../entity-mapper/entity-mapper.service.ts | 2 +- .../mock-entity-mapper-service.ts | 2 +- src/app/core/entity/latest-entity-loader.ts | 6 +- .../core/entity/model/entity-update.spec.ts | 6 +- src/app/core/entity/model/entity.spec.ts | 4 +- src/app/core/entity/model/entity.ts | 5 +- .../core/entity/schema/entity-schema-field.ts | 2 +- .../data-transformation.service.spec.ts | 2 +- .../export-column-config.ts | 2 +- src/app/core/export/query.service.spec.ts | 48 +++------ src/app/core/export/query.service.ts | 36 ++----- src/app/core/filter/filters/entityFilter.ts | 9 +- .../dialog-buttons.component.spec.ts | 2 +- .../dialog-buttons.component.ts | 2 +- src/app/core/import/import.service.spec.ts | 12 +-- src/app/core/import/import.service.ts | 2 +- .../import/import/import-sample-raw-data.ts | 6 +- .../session-manager.service.ts | 3 +- .../core/support/support/support.component.ts | 2 +- src/app/core/ui/search/search.component.ts | 2 +- src/app/core/user/user.spec.ts | 2 +- .../file/couchdb-file.service.spec.ts | 4 +- src/app/features/file/couchdb-file.service.ts | 14 +-- src/app/features/file/mock-file.service.ts | 8 +- .../matching-entities.component.spec.ts | 8 +- .../matching-entities.component.ts | 8 +- .../public-form/public-form.component.spec.ts | 4 +- .../demo-report-config-generator.service.ts | 6 +- .../reporting/reporting.component.ts | 2 +- .../sql-report/sql-report.service.spec.ts | 2 +- .../sql-report/sql-report.service.ts | 2 +- .../model/demo-todo-generator.service.ts | 2 +- .../display-todo-completion.component.html | 2 +- .../display-todo-completion.component.spec.ts | 16 ++- .../display-todo-completion.component.ts | 32 +++++- .../todo-details.component.spec.ts | 2 +- src/app/features/todos/todo.service.ts | 2 +- .../todos-related-to-entity.component.ts | 6 +- src/app/utils/test-utils/custom-matchers.d.ts | 7 -- .../utils/test-utils/entity-matchers.spec.ts | 17 +-- 94 files changed, 430 insertions(+), 587 deletions(-) diff --git a/src/app/child-dev-project/attendance/activity-card/activity-card.stories.ts b/src/app/child-dev-project/attendance/activity-card/activity-card.stories.ts index cf2559419a..a66611951b 100644 --- a/src/app/child-dev-project/attendance/activity-card/activity-card.stories.ts +++ b/src/app/child-dev-project/attendance/activity-card/activity-card.stories.ts @@ -30,7 +30,7 @@ const demoChildren = [ ]; const simpleEvent = Note.create(new Date(), "some meeting"); -demoChildren.forEach((c) => simpleEvent.addChild(c.getId())); +demoChildren.forEach((c) => simpleEvent.addChild(c)); const longEvent = Note.create(new Date(), "another meeting"); longEvent.text = @@ -40,12 +40,11 @@ longEvent.category = { label: "Guardians Meeting", isMeeting: true, }; -demoChildren.forEach((c) => longEvent.addChild(c.getId())); +demoChildren.forEach((c) => longEvent.addChild(c)); const activityEvent = Note.create(new Date(), "Coaching Batch C"); -activityEvent.relatesTo = - RecurringActivity.create("Coaching Batch C").getId(true); -demoChildren.forEach((c) => activityEvent.addChild(c.getId())); +activityEvent.relatesTo = RecurringActivity.create("Coaching Batch C").getId(); +demoChildren.forEach((c) => activityEvent.addChild(c)); export const OneTimeEvent = Template.bind({}); OneTimeEvent.args = { diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts index 3987a30e83..bd5f8760b9 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts @@ -14,6 +14,7 @@ import { AttendanceService } from "../../attendance.service"; import { EventNote } from "../../model/event-note"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; import { TEST_USER } from "../../../../core/user/demo-user-generator.service"; +import { User } from "../../../../core/user/user"; describe("RollCallSetupComponent", () => { let component: RollCallSetupComponent; @@ -64,8 +65,12 @@ describe("RollCallSetupComponent", () => { flush(); expect(component.existingEvents.length).toBe(2); - expect(component.existingEvents[0].authors).toEqual([TEST_USER]); - expect(component.existingEvents[1].authors).toEqual([TEST_USER]); + expect(component.existingEvents[0].authors).toEqual([ + `${User.ENTITY_TYPE}:${TEST_USER}`, + ]); + expect(component.existingEvents[1].authors).toEqual([ + `${User.ENTITY_TYPE}:${TEST_USER}`, + ]); })); it("should only show active activities", fakeAsync(() => { diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.ts index 6f664253d5..6c654af05c 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.ts @@ -148,7 +148,7 @@ export class RollCallSetupComponent implements OnInit { private async createEventForActivity( activity: RecurringActivity, ): Promise { - if (this.existingEvents.find((e) => e.relatesTo === activity.getId(true))) { + if (this.existingEvents.find((e) => e.relatesTo === activity.getId())) { return undefined; } @@ -166,7 +166,7 @@ export class RollCallSetupComponent implements OnInit { let score = 0; const activityAssignedUsers = this.allActivities.find( - (a) => a.getId(true) === event.relatesTo, + (a) => a.getId() === event.relatesTo, )?.assignedTo; // use parent activities' assigned users and only fall back to event if necessary const assignedUsers = activityAssignedUsers ?? event.authors; diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.stories.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.stories.ts index 82faa2ff66..a336c0d2a6 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.stories.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.stories.ts @@ -28,7 +28,7 @@ const demoChildren = [ DemoChildGenerator.generateEntity("2"), DemoChildGenerator.generateEntity("3"), ]; -demoChildren.forEach((c) => demoEvent.addChild(c.getId())); +demoChildren.forEach((c) => demoEvent.addChild(c)); const demoActivities = [ DemoActivityGeneratorService.generateActivityForChildren(demoChildren), diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts index 3208463934..6b2878b523 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts @@ -98,7 +98,7 @@ describe("RollCallComponent", () => { it("should not record attendance if childId does not exist", fakeAsync(() => { const nonExistingChildId = "notExistingChild"; const noteWithNonExistingChild = new Note(); - noteWithNonExistingChild.addChild(participant1.getId()); + noteWithNonExistingChild.addChild(participant1); noteWithNonExistingChild.addChild(nonExistingChildId); component.eventEntity = noteWithNonExistingChild; @@ -113,8 +113,8 @@ describe("RollCallComponent", () => { it("should correctly assign the attendance", fakeAsync(() => { const note = new Note("noteWithAttendance"); - note.addChild(participant1.getId()); - note.addChild(participant2.getId()); + note.addChild(participant1); + note.addChild(participant2); component.eventEntity = note; component.ngOnChanges(dummyChanges); @@ -124,16 +124,16 @@ describe("RollCallComponent", () => { tick(1000); component.markAttendance(ABSENT); - expect(note.getAttendance(participant1.getId()).status).toEqual(PRESENT); - expect(note.getAttendance(participant2.getId()).status).toEqual(ABSENT); + expect(note.getAttendance(participant1).status).toEqual(PRESENT); + expect(note.getAttendance(participant2).status).toEqual(ABSENT); flush(); })); it("should mark roll call as done when all existing children are finished", fakeAsync(() => { const note = new Note(); - note.addChild(participant1.getId()); + note.addChild(participant1); note.addChild("notExistingChild"); - note.addChild(participant2.getId()); + note.addChild(participant2); spyOn(component.complete, "emit"); component.eventEntity = note; @@ -245,7 +245,7 @@ describe("RollCallComponent", () => { ) { const event = new Note(); for (const p of participantsInput) { - event.addChild(p.getId()); + event.addChild(p); } component.eventEntity = event; component.ngOnChanges(dummyChanges); diff --git a/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts b/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts index d48469ec3e..d7bccf74e8 100644 --- a/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts +++ b/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts @@ -83,17 +83,17 @@ describe("AttendanceCalendarComponent", () => { const childWithoutAttendance = new Child("childWithoutAttendance"); const note = new Note(); note.date = new Date(); - note.addChild(attendedChild.getId()); - note.addChild(absentChild.getId()); - note.addChild(childWithoutAttendance.getId()); + note.addChild(attendedChild); + note.addChild(absentChild); + note.addChild(childWithoutAttendance); const presentAttendance = defaultAttendanceStatusTypes.find( (it) => it.id === "PRESENT", ); const absentAttendance = defaultAttendanceStatusTypes.find( (it) => it.id === "ABSENT", ); - note.getAttendance(attendedChild.getId()).status = presentAttendance; - note.getAttendance(absentChild.getId()).status = absentAttendance; + note.getAttendance(attendedChild).status = presentAttendance; + note.getAttendance(absentChild).status = absentAttendance; component.records = [note]; component.selectDay(new Date()); diff --git a/src/app/child-dev-project/attendance/attendance.service.spec.ts b/src/app/child-dev-project/attendance/attendance.service.spec.ts index 923acaae5e..4bfeb1ba7c 100644 --- a/src/app/child-dev-project/attendance/attendance.service.spec.ts +++ b/src/app/child-dev-project/attendance/attendance.service.spec.ts @@ -14,6 +14,7 @@ import { ChildSchoolRelation } from "../children/model/childSchoolRelation"; import { Child } from "../children/model/child"; import { Note } from "../notes/model/note"; import { DatabaseTestingModule } from "../../utils/database-testing.module"; +import { Entity } from "../../core/entity/model/entity"; describe("AttendanceService", () => { let service: AttendanceService; @@ -41,10 +42,10 @@ describe("AttendanceService", () => { activity1 = RecurringActivity.create("activity 1"); activity2 = RecurringActivity.create("activity 2"); - e1_1 = createEvent(moment("2020-01-01").toDate(), activity1.getId(true)); - e1_2 = createEvent(moment("2020-01-02").toDate(), activity1.getId(true)); - e1_3 = createEvent(moment("2020-03-02").toDate(), activity1.getId(true)); - e2_1 = createEvent(moment("2020-01-01").toDate(), activity2.getId(true)); + e1_1 = createEvent(moment("2020-01-01").toDate(), activity1.getId()); + e1_2 = createEvent(moment("2020-01-02").toDate(), activity1.getId()); + e1_3 = createEvent(moment("2020-03-02").toDate(), activity1.getId()); + e2_1 = createEvent(moment("2020-01-01").toDate(), activity2.getId()); TestBed.configureTestingModule({ imports: [DatabaseTestingModule], @@ -186,15 +187,13 @@ describe("AttendanceService", () => { expect(actualAttendences).toHaveSize(2); expectEntitiesToMatch( - actualAttendences.find( - (t) => t.activity.getId(true) === activity1.getId(true), - ).events, + actualAttendences.find((t) => t.activity.getId() === activity1.getId()) + .events, [e1_1, e1_2], ); expectEntitiesToMatch( - actualAttendences.find( - (t) => t.activity.getId(true) === activity2.getId(true), - ).events, + actualAttendences.find((t) => t.activity.getId() === activity2.getId()) + .events, [e2_1], ); @@ -300,7 +299,6 @@ describe("AttendanceService", () => { const event = await service.createEventForActivity(activity, date); expect(mockQueryRelationsOf).toHaveBeenCalledWith( - "school", linkedSchool.getId(), date, ); @@ -319,7 +317,10 @@ describe("AttendanceService", () => { duplicateChildRelation.childId = duplicateChild.getId(); duplicateChildRelation.schoolId = linkedSchool.getId(); const anotherRelation = new ChildSchoolRelation(); - anotherRelation.childId = "another child id"; + anotherRelation.childId = Entity.createPrefixedId( + Child.ENTITY_TYPE, + "another_child_id", + ); anotherRelation.schoolId = linkedSchool.getId(); await entityMapper.saveAll([duplicateChildRelation, anotherRelation]); diff --git a/src/app/child-dev-project/attendance/attendance.service.ts b/src/app/child-dev-project/attendance/attendance.service.ts index 9b289f2970..80638d61bc 100644 --- a/src/app/child-dev-project/attendance/attendance.service.ts +++ b/src/app/child-dev-project/attendance/attendance.service.ts @@ -187,10 +187,7 @@ export class AttendanceService { return attMonth; } - const events = await this.getEventsForActivity( - activity.getId(true), - sinceDate, - ); + const events = await this.getEventsForActivity(activity.getId(), sinceDate); for (const event of events) { const record = getOrCreateAttendancePeriod(event); @@ -232,10 +229,8 @@ export class AttendanceService { childId, ); - const visitedSchools = await this.childrenService.queryActiveRelationsOf( - "child", - childId, - ); + const visitedSchools = + await this.childrenService.queryActiveRelationsOf(childId); for (const currentRelation of visitedSchools) { const activitiesThroughRelation = await this.dbIndexing.queryIndexDocs( RecurringActivity, @@ -266,7 +261,7 @@ export class AttendanceService { date, ); instance.schools = activity.linkedGroups; - instance.relatesTo = activity.getId(true); + instance.relatesTo = activity.getId(); instance.category = activity.type; return instance; } @@ -296,7 +291,7 @@ export class AttendanceService { ): Promise { const childIdPromises = linkedGroups.map((groupId) => this.childrenService - .queryActiveRelationsOf("school", groupId, date) + .queryActiveRelationsOf(groupId, date) .then((relations) => relations.map((r) => r.childId).filter((id) => !!id), ), diff --git a/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.ts b/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.ts index a4a4da24b8..d17571687e 100644 --- a/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.ts +++ b/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.ts @@ -29,7 +29,7 @@ export class DemoActivityEventsGeneratorService extends DemoDataGenerator { { id: "remarks", visibleFrom: "md" }, ]; - constructor( - private childrenService: ChildrenService, - entityMapper: EntityMapperService, - entityRegistry: EntityRegistry, - screenWidthObserver: ScreenWidthObserver, - ) { - super(entityMapper, entityRegistry, screenWidthObserver); - } - override async initData() { - this.data = ( - await this.childrenService.getAserResultsOfChild(this.entity.getId()) - ).sort( - (a, b) => - (b.date ? b.date.valueOf() : 0) - (a.date ? a.date.valueOf() : 0), - ); + super + .initData() + .then(() => + this.data.sort( + (a, b) => + (b.date ? b.date.valueOf() : 0) - (a.date ? a.date.valueOf() : 0), + ), + ); } } diff --git a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts b/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts index baf8aebc62..22b154d910 100644 --- a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts +++ b/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts @@ -6,8 +6,8 @@ import moment from "moment"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; import { - ScreenWidthObserver, ScreenSize, + ScreenWidthObserver, } from "../../../../utils/media/screen-size-observer.service"; import { NgForOf, SlicePipe } from "@angular/common"; import { AttendanceBlockComponent } from "../../../attendance/attendance-block/attendance-block.component"; diff --git a/src/app/child-dev-project/children/children.service.spec.ts b/src/app/child-dev-project/children/children.service.spec.ts index 09e60059ce..d18920e9d8 100644 --- a/src/app/child-dev-project/children/children.service.spec.ts +++ b/src/app/child-dev-project/children/children.service.spec.ts @@ -41,32 +41,27 @@ describe("ChildrenService", () => { it("should list newly saved children", async () => { const childrenBefore = await service.getChildren(); const child = new Child("10"); - await entityMapper.save(child); + await entityMapper.save(child); const childrenAfter = await service.getChildren(); - let find = childrenBefore.find((c) => c.getId() === child.getId()); - expect(find).toBeUndefined(); + expect(childrenBefore).not.toContain(child); - find = childrenAfter.find((c) => c.getId() === child.getId()); - expect(find).toBeDefined(); - expect(find).toHaveId(child.getId()); + expect(childrenAfter).toContain(child); expect(childrenBefore).toHaveSize(childrenAfter.length - 1); }); it("should find a newly saved child", async () => { const child = new Child("10"); - let error; try { await service.getChild(child.getId()); + fail("Child should not be found"); } catch (err) { - error = err; + expect(err).toBeDefined(); } - expect(error).toBeDefined(); - await entityMapper.save(child); + await entityMapper.save(child); const childAfter = await service.getChild(child.getId()); - expect(childAfter).toBeDefined(); - expect(childAfter).toHaveId(child.getId()); + expect(childAfter).toEqual(child); }); // TODO: test getAttendances @@ -131,52 +126,52 @@ describe("ChildrenService", () => { it("should load a single child and add school info", async () => { // no active relation - const child2 = await service.getChild("2"); + const child2 = await service.getChild("Child:2"); expect(child2.schoolClass).toBeUndefined(); expect(child2.schoolId).toBeEmpty(); // one active relation - let child1 = await service.getChild("1"); + let child1 = await service.getChild("Child:1"); expect(child1.schoolClass).toBe("2"); - expect(child1.schoolId).toEqual(["1"]); + expect(child1.schoolId).toEqual(["School:1"]); // multiple active relations const newRelation = new ChildSchoolRelation(); newRelation.childId = child1.getId(); newRelation.start = new Date(); - newRelation.schoolId = "2"; + newRelation.schoolId = "School:2"; newRelation.schoolClass = "3"; await entityMapper.save(newRelation); child1 = await service.getChild(child1.getId()); expect(child1.schoolClass).toBe("3"); - expect(child1.schoolId).toEqual(["2", "1"]); + expect(child1.schoolId).toEqual(["School:2", "School:1"]); // multiple active, no start date on one const noStartDate = new ChildSchoolRelation(); noStartDate.childId = child1.getId(); - noStartDate.schoolId = "2"; + noStartDate.schoolId = "School:2"; noStartDate.schoolClass = "4"; await entityMapper.save(noStartDate); child1 = await service.getChild(child1.getId()); expect(child1.schoolClass).toBe("4"); - expect(child1.schoolId).toEqual(["2", "2", "1"]); + expect(child1.schoolId).toEqual(["School:2", "School:2", "School:1"]); }); it("should load all children with school info", async () => { const children = await service.getChildren(); - const child1 = children.find((child) => child.getId() === "1"); + const child1 = children.find((child) => child.getId() === "Child:1"); expect(child1.schoolClass).toBe("2"); - expect(child1.schoolId).toEqual(["1"]); - const child2 = children.find((child) => child.getId() === "2"); + expect(child1.schoolId).toEqual(["School:1"]); + const child2 = children.find((child) => child.getId() === "Child:2"); expect(child2.schoolClass).toBeUndefined(); expect(child2.schoolId).toBeEmpty(); - const child3 = children.find((child) => child.getId() === "3"); + const child3 = children.find((child) => child.getId() === "Child:3"); expect(child3.schoolClass).toBe("2"); - expect(child3.schoolId).toEqual(["1"]); + expect(child3.schoolId).toEqual(["School:1"]); }); it("should get the relations for a child in sorted order", async () => { - const relations = await service.queryRelationsOf("child", "3"); + const relations = await service.queryRelations("Child:3"); expect(relations).toHaveSize(2); expect(relations[0].start.getTime()).toBeGreaterThanOrEqual( @@ -185,45 +180,47 @@ describe("ChildrenService", () => { }); it("should get all relations for a school", async () => { - const relations = await service.queryRelationsOf("school", "1"); + const relations = await service.queryRelations("School:1"); expect(relations).toHaveSize(2); - const relation1 = relations.find((relation) => relation.getId() === "1"); - expect(relation1.childId).toBe("1"); - const relation2 = relations.find((relation) => relation.getId() === "4"); - expect(relation2.childId).toBe("3"); + const relation1 = relations.find( + (relation) => relation.getId() === "ChildSchoolRelation:1", + ); + expect(relation1.childId).toBe("Child:1"); + const relation2 = relations.find( + (relation) => relation.getId() === "ChildSchoolRelation:4", + ); + expect(relation2.childId).toBe("Child:3"); }); it("should get a active relation which starts today", async () => { const todayRelation = new ChildSchoolRelation("today"); - todayRelation.schoolId = "3"; + todayRelation.schoolId = "School:3"; todayRelation.start = new Date(); await entityMapper.save(todayRelation); - const relations = await service.queryActiveRelationsOf("school", "3"); + const relations = await service.queryActiveRelationsOf("School:3"); expectEntitiesToMatch(relations, [todayRelation]); }); it("should on default only return active relations", async () => { const allRelations = await entityMapper.loadType(ChildSchoolRelation); const activeRelations = allRelations - .filter((rel) => rel.isActive && rel.childId === "3") + .filter((rel) => rel.isActive && rel.childId === "Child:3") .sort(sortByAttribute("start", "desc")); - const result = await service.queryActiveRelationsOf("child", "3"); + const result = await service.queryActiveRelationsOf("Child:3"); expect(result).toEqual(activeRelations); }); it("should return active relations for a given date", async () => { let relations = await service.queryActiveRelationsOf( - "school", - "1", + "School:1", new Date("2010-01-01"), ); expect(relations).toHaveSize(1); relations = await service.queryActiveRelationsOf( - "school", - "1", + "School:1", new Date("2016-10-01"), ); expect(relations).toHaveSize(2); @@ -244,13 +241,13 @@ describe("ChildrenService", () => { n3.addSchool(s2); await entityMapper.saveAll([n1, n2, n3]); - let res = await service.getNotesRelatedTo(c1.getId(true)); + let res = await service.getNotesRelatedTo(c1.getId()); expect(res).toEqual([n1, n2]); - res = await service.getNotesRelatedTo(s1.getId(true)); + res = await service.getNotesRelatedTo(s1.getId()); expect(res).toEqual([n1]); - res = await service.getNotesRelatedTo(s2.getId(true)); + res = await service.getNotesRelatedTo(s2.getId()); expect(res).toEqual([n3]); }); @@ -259,14 +256,14 @@ describe("ChildrenService", () => { const s1 = new School("s1"); const n1 = new Note("n1"); n1.children.push(c1.getId()); - n1.relatedEntities.push(c1.getId(true)); + n1.relatedEntities.push(c1.getId()); n1.schools.push(s1.getId()); await entityMapper.saveAll([n1]); - let res = await service.getNotesRelatedTo(c1.getId(true)); + let res = await service.getNotesRelatedTo(c1.getId()); expect(res).toEqual([n1]); - res = await service.getNotesRelatedTo(s1.getId(true)); + res = await service.getNotesRelatedTo(s1.getId()); expect(res).toEqual([n1]); }); @@ -340,31 +337,31 @@ function generateSchoolEntities(): School[] { function generateChildSchoolRelationEntities(): ChildSchoolRelation[] { const data: ChildSchoolRelation[] = []; const rel1: ChildSchoolRelation = new ChildSchoolRelation("1"); - rel1.childId = "1"; - rel1.schoolId = "1"; + rel1.childId = "Child:1"; + rel1.schoolId = "School:1"; rel1.start = new Date("2016-10-01"); rel1.schoolClass = "2"; data.push(rel1); const rel4: ChildSchoolRelation = new ChildSchoolRelation("2"); - rel4.childId = "3"; - rel4.schoolId = "2"; + rel4.childId = "Child:3"; + rel4.schoolId = "School:2"; rel4.start = new Date("2001-01-01"); rel4.end = new Date("2002-01-01"); rel4.schoolClass = "1"; data.push(rel4); const rel2: ChildSchoolRelation = new ChildSchoolRelation("3"); - rel2.childId = "2"; - rel2.schoolId = "2"; + rel2.childId = "Child:2"; + rel2.schoolId = "School:2"; rel2.start = new Date("2018-05-07"); rel2.end = new Date("2018-05-09"); rel2.schoolClass = "3"; data.push(rel2); const rel3: ChildSchoolRelation = new ChildSchoolRelation("4"); - rel3.childId = "3"; - rel3.schoolId = "1"; + rel3.childId = "Child:3"; + rel3.schoolId = "School:1"; rel3.start = new Date("2010-01-01"); rel3.schoolClass = "2"; data.push(rel3); diff --git a/src/app/child-dev-project/children/children.service.ts b/src/app/child-dev-project/children/children.service.ts index 89a281a693..3ba042cc8b 100644 --- a/src/app/child-dev-project/children/children.service.ts +++ b/src/app/child-dev-project/children/children.service.ts @@ -2,7 +2,6 @@ import { Injectable } from "@angular/core"; import { Child } from "./model/child"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { Note } from "../notes/model/note"; -import { Aser } from "./aser/model/aser"; import { ChildSchoolRelation } from "./model/childSchoolRelation"; import { HealthCheck } from "./health-checkup/model/health-check"; import moment, { Moment } from "moment"; @@ -47,7 +46,7 @@ export class ChildrenService { */ async getChild(id: string): Promise { const child = await this.entityMapper.load(Child, id); - const relations = await this.queryRelations(`${Child.ENTITY_TYPE}:${id}`); + const relations = await this.queryRelations(id); this.extendChildWithSchoolInfo(child, relations); return child; } @@ -73,8 +72,8 @@ export class ChildrenService { return; }; const start = new Date(doc.start || '3000-01-01').getTime(); - emit(["${Child.ENTITY_TYPE}:" + doc.childId, start]); - emit(["${School.ENTITY_TYPE}:" + doc.schoolId, start]); + emit([doc.childId, start]); + emit([doc.schoolId, start]); return; }`, }, @@ -83,7 +82,7 @@ export class ChildrenService { return this.dbIndexing.createIndex(designDoc); } - private queryRelations(prefix: string) { + queryRelations(prefix: string) { const startkey = prefix.endsWith(":") ? [prefix + "\uffff"] : [prefix, {}]; return this.dbIndexing.queryIndexDocs( ChildSchoolRelation, @@ -97,24 +96,14 @@ export class ChildrenService { } queryActiveRelationsOf( - queryType: "child" | "school", id: string, date = new Date(), ): Promise { - return this.queryRelationsOf(queryType, id).then((relations) => + return this.queryRelations(id).then((relations) => relations.filter((rel) => rel.isActiveAt(date)), ); } - queryRelationsOf( - queryType: "child" | "school", - id: string, - ): Promise { - const type = queryType === "child" ? Child.ENTITY_TYPE : School.ENTITY_TYPE; - const prefixed = Entity.createPrefixedId(type, id); - return this.queryRelations(prefixed); - } - /** * Query all notes that have been linked to the given other entity. * @param entityId ID (with prefix!) of the related record @@ -125,7 +114,7 @@ export class ChildrenService { legacyLinkedNotes = await this.dbIndexing.queryIndexDocs( Note, `notes_index/by_${this.inferNoteLinkPropertyFromEntityType(entityId)}`, - Entity.extractEntityIdFromId(entityId), + entityId, ); } @@ -184,11 +173,10 @@ export class ChildrenService { // TODO: filter notes to only include them if the given child is marked "present" for (const entityId of note[noteProperty]) { - const trimmedId = Entity.extractEntityIdFromId(entityId); const daysSinceNote = moment().diff(note.date, "days"); - const previousValue = results.get(trimmedId); + const previousValue = results.get(entityId); if (previousValue > daysSinceNote) { - results.set(trimmedId, daysSinceNote); + results.set(entityId, daysSinceNote); } } } @@ -283,13 +271,7 @@ export class ChildrenService { */ getHealthChecksOfChild(childId: string): Promise { return this.entityMapper - .loadType(HealthCheck) + .loadType(HealthCheck) .then((res) => res.filter((h) => h.child === childId)); } - - getAserResultsOfChild(childId: string): Promise { - return this.entityMapper - .loadType(Aser) - .then((res) => res.filter((o) => o.child === childId)); - } } diff --git a/src/app/child-dev-project/children/educational-material/model/educational-material.ts b/src/app/child-dev-project/children/educational-material/model/educational-material.ts index ffdc8e7510..adf6490993 100644 --- a/src/app/child-dev-project/children/educational-material/model/educational-material.ts +++ b/src/app/child-dev-project/children/educational-material/model/educational-material.ts @@ -19,6 +19,7 @@ import { Entity } from "../../../../core/entity/model/entity"; import { DatabaseEntity } from "../../../../core/entity/database-entity.decorator"; import { DatabaseField } from "../../../../core/entity/database-field.decorator"; import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; +import { Child } from "../../model/child"; @DatabaseEntity("EducationalMaterial") export class EducationalMaterial extends Entity { @@ -26,11 +27,18 @@ export class EducationalMaterial extends Entity { return Object.assign(new EducationalMaterial(), params); } - @DatabaseField() child: string; // id of Child entity + @DatabaseField({ + dataType: "entity", + additional: Child.ENTITY_TYPE, + entityReferenceRole: "composite", + }) + child: string; + @DatabaseField({ label: $localize`:Date on which the material has been borrowed:Date`, }) date: Date; + @DatabaseField({ label: $localize`:The material which has been borrowed:Material`, dataType: "configurable-enum", @@ -40,6 +48,7 @@ export class EducationalMaterial extends Entity { }, }) materialType: ConfigurableEnumValue; + @DatabaseField({ label: $localize`:The amount of the material which has been borrowed:Amount`, defaultValue: 1, @@ -48,6 +57,7 @@ export class EducationalMaterial extends Entity { }, }) materialAmount: number; + @DatabaseField({ label: $localize`:An additional description for the borrowed material:Description`, }) diff --git a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts index 2d44bce897..eb4ebb3916 100644 --- a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts +++ b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts @@ -2,26 +2,16 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { HealthCheckupComponent } from "./health-checkup.component"; import { Child } from "../../model/child"; -import { ChildrenService } from "../../children.service"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; describe("HealthCheckupComponent", () => { let component: HealthCheckupComponent; let fixture: ComponentFixture; - let mockChildrenService: jasmine.SpyObj; const child = new Child(); beforeEach(waitForAsync(() => { - mockChildrenService = jasmine.createSpyObj([ - "getChild", - "getHealthChecksOfChild", - ]); - mockChildrenService.getChild.and.resolveTo(child); - mockChildrenService.getHealthChecksOfChild.and.resolveTo([]); - TestBed.configureTestingModule({ imports: [HealthCheckupComponent, MockedTestingModule.withState()], - providers: [{ provide: ChildrenService, useValue: mockChildrenService }], }).compileComponents(); })); diff --git a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.ts b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.ts index 9212b4a6b8..914f565c3b 100644 --- a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.ts +++ b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.ts @@ -1,14 +1,10 @@ import { Component, Input, OnInit } from "@angular/core"; import { HealthCheck } from "../model/health-check"; -import { ChildrenService } from "../../children.service"; import { Child } from "../../model/child"; import { FormFieldConfig } from "../../../../core/common-components/entity-form/FormConfig"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; import { EntitiesTableComponent } from "../../../../core/common-components/entities-table/entities-table.component"; import { RelatedEntitiesComponent } from "../../../../core/entity-details/related-entities/related-entities.component"; -import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { EntityRegistry } from "../../../../core/entity/database-entity.decorator"; -import { ScreenWidthObserver } from "../../../../utils/media/screen-size-observer.service"; @DynamicComponent("HealthCheckup") @Component({ @@ -44,15 +40,6 @@ export class HealthCheckupComponent }, ]; - constructor( - private childrenService: ChildrenService, - entityMapper: EntityMapperService, - entityRegistry: EntityRegistry, - screenWidthObserver: ScreenWidthObserver, - ) { - super(entityMapper, entityRegistry, screenWidthObserver); - } - private getBMI(healthCheck: HealthCheck): string { const bmi = healthCheck.bmi; if (Number.isNaN(bmi)) { @@ -77,11 +64,13 @@ export class HealthCheckupComponent * implements the health check loading from the children service and is called in the onInit() */ override async initData() { - this.data = ( - await this.childrenService.getHealthChecksOfChild(this.entity.getId()) - ).sort( - (a, b) => - (b.date ? b.date.valueOf() : 0) - (a.date ? a.date.valueOf() : 0), - ); + super + .initData() + .then(() => + this.data.sort( + (a, b) => + (b.date ? b.date.valueOf() : 0) - (a.date ? a.date.valueOf() : 0), + ), + ); } } diff --git a/src/app/child-dev-project/notes/note-details/note-details.component.spec.ts b/src/app/child-dev-project/notes/note-details/note-details.component.spec.ts index cdd80138d5..bb10f9dae1 100644 --- a/src/app/child-dev-project/notes/note-details/note-details.component.spec.ts +++ b/src/app/child-dev-project/notes/note-details/note-details.component.spec.ts @@ -18,9 +18,8 @@ function generateTestNote(forChildren: Child[]) { isMeeting: true, }; for (const child of forChildren) { - testNote.addChild(child.getId()); - testNote.getAttendance(child.getId()).status = - defaultAttendanceStatusTypes[0]; + testNote.addChild(child); + testNote.getAttendance(child).status = defaultAttendanceStatusTypes[0]; } return testNote; } diff --git a/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.spec.ts b/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.spec.ts index 23bdb8f412..3aeeb4c761 100644 --- a/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.spec.ts +++ b/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.spec.ts @@ -73,17 +73,17 @@ describe("NotesRelatedToEntityComponent", () => { component.entity = entity; component.ngOnInit(); note = component.generateNewRecordFactory()(); - expect(note.relatedEntities).toEqual([entity.getId(true)]); + expect(note.relatedEntities).toEqual([entity.getId()]); entity = new ChildSchoolRelation(); - entity["childId"] = "someChild"; - entity["schoolId"] = "someSchool"; + entity["childId"] = `${Child.ENTITY_TYPE}:someChild`; + entity["schoolId"] = `${Child.ENTITY_TYPE}:someSchool`; component.entity = entity; component.ngOnInit(); note = component.generateNewRecordFactory()(); - expect(note.relatedEntities).toContain(entity.getId(true)); - expect(note.children).toEqual(["someChild"]); - expect(note.schools).toEqual(["someSchool"]); + expect(note.relatedEntities).toContain(entity.getId()); + expect(note.children).toEqual([`${Child.ENTITY_TYPE}:someChild`]); + expect(note.schools).toEqual([`${Child.ENTITY_TYPE}:someSchool`]); }); it("should create a new note and fill it with indirectly related references (2-hop) of the types allowed for note.relatedEntities", () => { @@ -105,10 +105,10 @@ describe("NotesRelatedToEntityComponent", () => { } const customEntity = new EntityWithRelations(); customEntity.links = [ - "Child:1", - "School:not-a-type-for-note.relatedEntities", + `${Child.ENTITY_TYPE}:1`, + `${School.ENTITY_TYPE}:not-a-type-for-note.relatedEntities`, ]; - customEntity.childrenLink = "child-without-prefix"; + customEntity.childrenLink = `${Child.ENTITY_TYPE}:child-without-prefix`; Note.schema.get("relatedEntities").additional = [ Child.ENTITY_TYPE, @@ -119,7 +119,7 @@ describe("NotesRelatedToEntityComponent", () => { const newNote = component.generateNewRecordFactory()(); - expect(newNote.relatedEntities).toContain(customEntity.getId(true)); + expect(newNote.relatedEntities).toContain(customEntity.getId()); expect(newNote.relatedEntities).toContain(customEntity.links[0]); expect(newNote.relatedEntities).not.toContain(customEntity.links[1]); expect(newNote.relatedEntities).toContain( @@ -141,7 +141,7 @@ describe("NotesRelatedToEntityComponent", () => { tick(); expect(mockChildrenService.getNotesRelatedTo).toHaveBeenCalledWith( - component.entity.getId(true), + component.entity.getId(), ); expect(component.data).toEqual([n1, n2, n3]); })); diff --git a/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.ts b/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.ts index d0e0951070..85dfa400d1 100644 --- a/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.ts +++ b/src/app/child-dev-project/notes/notes-related-to-entity/notes-related-to-entity.component.ts @@ -69,7 +69,7 @@ export class NotesRelatedToEntityComponent extends RelatedEntitiesComponent { notes.sort((a, b) => { if (!a.date && b.date) { @@ -96,7 +96,7 @@ export class NotesRelatedToEntityComponent extends RelatedEntitiesComponent newNote.relatedEntities.push(e), ); @@ -134,11 +134,7 @@ export class NotesRelatedToEntityComponent extends RelatedEntitiesComponent { inactive.end = moment().subtract("1", "week").toDate(); beforeEach(waitForAsync(() => { - mockChildrenService = jasmine.createSpyObj(["queryRelationsOf"]); - mockChildrenService.queryRelationsOf.and.resolveTo([ + mockChildrenService = jasmine.createSpyObj(["queryRelations"]); + mockChildrenService.queryRelations.and.resolveTo([ new ChildSchoolRelation(), ]); @@ -43,8 +43,7 @@ describe("ChildSchoolOverviewComponent", () => { it("it calls children service with id from passed child", async () => { await component.ngOnInit(); - expect(mockChildrenService.queryRelationsOf).toHaveBeenCalledWith( - "child", + expect(mockChildrenService.queryRelations).toHaveBeenCalledWith( testChild.getId(), ); }); @@ -56,8 +55,7 @@ describe("ChildSchoolOverviewComponent", () => { await component.ngOnInit(); expect(component.mode).toBe("school"); - expect(mockChildrenService.queryRelationsOf).toHaveBeenCalledWith( - "school", + expect(mockChildrenService.queryRelations).toHaveBeenCalledWith( testSchool.getId(), ); }); @@ -66,7 +64,7 @@ describe("ChildSchoolOverviewComponent", () => { const existingRelation = new ChildSchoolRelation(); existingRelation.start = moment().subtract(1, "year").toDate(); existingRelation.end = moment().subtract(1, "week").toDate(); - mockChildrenService.queryRelationsOf.and.resolveTo([existingRelation]); + mockChildrenService.queryRelations.and.resolveTo([existingRelation]); const child = new Child(); component.entity = child; @@ -89,6 +87,6 @@ describe("ChildSchoolOverviewComponent", () => { const newRelation = component.generateNewRecordFactory()(); expect(newRelation).toBeInstanceOf(ChildSchoolRelation); - expect(newRelation.schoolId).toBe("testID"); + expect(newRelation.schoolId).toBe("School:testID"); }); }); diff --git a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts b/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts index 36d2e72fbd..380dfb680e 100644 --- a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts +++ b/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts @@ -66,6 +66,7 @@ export class ChildSchoolOverviewComponent async ngOnInit() { this.mode = this.inferMode(this.entity); + this.showInactive = this.mode === "child"; this.switchRelatedEntityColumnForMode(); await super.ngOnInit(); @@ -93,13 +94,6 @@ export class ChildSchoolOverviewComponent } override async initData() { - if (!this.mode) { - return; - } - - this.data = await this.childrenService.queryRelationsOf( - this.mode, - this.entity.getId(false), - ); + this.data = await this.childrenService.queryRelations(this.entity.getId()); } } diff --git a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts b/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts index 25481090fd..248eb8fad6 100644 --- a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts +++ b/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts @@ -30,7 +30,7 @@ export class DisplayParticipantsCountComponent super.ngOnChanges(); return this._childrenService - .queryActiveRelationsOf("school", this.entity.getId()) + .queryActiveRelationsOf(this.entity.getId()) .then((relations: ChildSchoolRelation[]) => { this.participantRelationsCount.set(relations.length); }) diff --git a/src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html b/src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html index 365854d778..424252bbce 100644 --- a/src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html +++ b/src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html @@ -84,12 +84,7 @@

Configure Field "{{ entitySchemaField.label }}"

- + Type Details (dropdown options set) Configure Field "{{ entitySchemaField.label }}" Configure Field "{{ entitySchemaField.label }}" @@ -134,7 +129,7 @@

Configure Field "{{ entitySchemaField.label }}"

{ it("should also support the name of the enum in the 'additional' field", () => { const data = { - _id: "Test", + _id: TestEntity.ENTITY_TYPE + ":Test", optionInAdditional: "TEST_3", }; const entity = new TestEntity(); @@ -118,7 +118,7 @@ describe("Schema data type: configurable-enum", () => { entitySchemaService.loadDataIntoEntity(entity, data); expect(entity.optionInAdditional).toEqual(TEST_CONFIG[2]); - expect(entity).toHaveId("Test"); + expect(entity.getId()).toEqual(data._id); }); it("should gracefully handle invalid enum ids and show a dummy option to users", () => { diff --git a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.spec.ts b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.spec.ts index 43a1689e01..8ad08284b0 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.spec.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.spec.ts @@ -28,7 +28,7 @@ describe("ConfigurableEnumService", () => { it("should create a new enum if it cannot be found", () => { const newEnum = service.getEnum("new-id"); - expect(newEnum.getId()).toEqual("new-id"); + expect(newEnum.getId(true)).toEqual("new-id"); expect(newEnum.values).toEqual([]); // returns same enum in consecutive calls expect(service.getEnum("new-id")).toBe(newEnum); diff --git a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts index e78205f0f7..5d8d3c3367 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts @@ -2,8 +2,8 @@ import { Injectable } from "@angular/core"; import { ConfigurableEnum } from "./configurable-enum"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { ConfigurableEnumValue } from "./configurable-enum.interface"; -import { Entity } from "../../entity/model/entity"; import { EntityAbility } from "../../permissions/ability/entity-ability"; +import { Entity } from "../../entity/model/entity"; @Injectable({ providedIn: "root" }) export class ConfigurableEnumService { @@ -24,7 +24,7 @@ export class ConfigurableEnumService { } private cacheEnum(entity: ConfigurableEnum) { - return this.enums.set(entity.getId(true), entity); + return this.enums.set(entity.getId(), entity); } getEnumValues( diff --git a/src/app/core/basic-datatypes/configurable-enum/configure-enum-popup/configure-enum-popup.component.ts b/src/app/core/basic-datatypes/configurable-enum/configure-enum-popup/configure-enum-popup.component.ts index 5f5713440c..d9a8f9e60a 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configure-enum-popup/configure-enum-popup.component.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configure-enum-popup/configure-enum-popup.component.ts @@ -92,8 +92,8 @@ export class ConfigureEnumPopupComponent { const schemaFields = [...entity.schema.entries()] .filter( ([_, schema]) => - schema.innerDataType === this.enumEntity.getId() || - schema.additional === this.enumEntity.getId(), + schema.innerDataType === this.enumEntity.getId(true) || + schema.additional === this.enumEntity.getId(true), ) .map(([name]) => name); if (schemaFields.length > 0) { diff --git a/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.component.spec.ts b/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.component.spec.ts index 538db5c04e..2c58eb7dd4 100644 --- a/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.component.spec.ts +++ b/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.component.spec.ts @@ -14,6 +14,10 @@ import { mockEntityMapper } from "../../../entity/entity-mapper/mock-entity-mapp import { School } from "../../../../child-dev-project/schools/model/school"; import { DatabaseField } from "../../../entity/database-field.decorator"; import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { + componentRegistry, + ComponentRegistry, +} from "../../../../dynamic-components"; describe("DisplayEntityArrayComponent", () => { let component: DisplayEntityArrayComponent; @@ -26,6 +30,7 @@ describe("DisplayEntityArrayComponent", () => { await TestBed.configureTestingModule({ imports: [DisplayEntityArrayComponent, HttpClientTestingModule], providers: [ + { provide: ComponentRegistry, useValue: componentRegistry }, { provide: EntityMapperService, useValue: mockEntityMapper(testEntities), @@ -61,7 +66,7 @@ describe("DisplayEntityArrayComponent", () => { } const testEntity = new DisplayEntityTest1(); - testEntity.relatedEntities = expectedEntities.map((e) => e.getId(false)); + testEntity.relatedEntities = expectedEntities.map((e) => e.getId()); component.entity = testEntity; component.id = "relatedEntities"; @@ -85,7 +90,7 @@ describe("DisplayEntityArrayComponent", () => { } const testEntity = new DisplayEntityTest2(); - testEntity.relatedEntities = expectedEntities.map((e) => e.getId(true)); + testEntity.relatedEntities = expectedEntities.map((e) => e.getId()); component.entity = testEntity; component.id = "relatedEntities"; diff --git a/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.stories.ts b/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.stories.ts index 6f41f8cab6..e28ab43f29 100644 --- a/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.stories.ts +++ b/src/app/core/basic-datatypes/entity-array/display-entity-array/display-entity-array.stories.ts @@ -49,10 +49,10 @@ const Template: StoryFn = ( export const FewEntities = Template.bind({}); FewEntities.args = { - value: [child1, child2].map((x) => x.getId(true)), + value: [child1, child2].map((x) => x.getId()), }; export const ManyEntities = Template.bind({}); ManyEntities.args = { - value: [child1, child2, child3, child4, child5].map((x) => x.getId(true)), + value: [child1, child2, child3, child4, child5].map((x) => x.getId()), }; diff --git a/src/app/core/basic-datatypes/entity-array/entity-array.datatype.spec.ts b/src/app/core/basic-datatypes/entity-array/entity-array.datatype.spec.ts index a08fcb1d7d..36b2cd7dae 100644 --- a/src/app/core/basic-datatypes/entity-array/entity-array.datatype.spec.ts +++ b/src/app/core/basic-datatypes/entity-array/entity-array.datatype.spec.ts @@ -49,15 +49,4 @@ describe("Schema data type: entity-array", () => { null, ); }); - - xit("adds prefix to ids when a definite entity type is given in schema", () => { - // TODO discuss whether we want to switch to prefixed ids always (also see #1526) - const data = { - relatedUsers: ["User:1", "2"], - }; - //const loadedEntity = new TestEntity(); - //entitySchemaService.loadDataIntoEntity(loadedEntity, data); - - //expect(loadedEntity.relatedUsers).toEqual(["User:1", "User:2"]); - }); }); diff --git a/src/app/core/basic-datatypes/entity-array/entity-array.datatype.ts b/src/app/core/basic-datatypes/entity-array/entity-array.datatype.ts index c1b6073d3d..d5d9c2a762 100644 --- a/src/app/core/basic-datatypes/entity-array/entity-array.datatype.ts +++ b/src/app/core/basic-datatypes/entity-array/entity-array.datatype.ts @@ -51,18 +51,6 @@ export class EntityArrayDatatype extends ArrayDatatype { { ...schema, innerDataType: EntityDatatype.dataType }, parent, ); - - // TODO: maybe introduce a prefix transformation in the future (also see #1526) - // this is only possible when no indices depend on un-prefixed IDs - /* - if (typeof schemaField.additional === "string") { - // if only one clear EntityType, make sure IDs are prefixed even for legacy data - return value.map((id) => - Entity.createPrefixedId(schemaField.additional, id) - ); - } else { - return value; - }*/ } async anonymize(value, schema: EntitySchemaField, parent) { diff --git a/src/app/core/basic-datatypes/entity/display-entity/display-entity.component.ts b/src/app/core/basic-datatypes/entity/display-entity/display-entity.component.ts index 3de1315099..8b1cdc8ea5 100644 --- a/src/app/core/basic-datatypes/entity/display-entity/display-entity.component.ts +++ b/src/app/core/basic-datatypes/entity/display-entity/display-entity.component.ts @@ -65,7 +65,7 @@ export class DisplayEntityComponent this.router.navigate([ this.entityToDisplay.getConstructor().route, - this.entityToDisplay.getId(), + this.entityToDisplay.getId(true), ]); } } diff --git a/src/app/core/common-components/entities-table/entities-table.component.spec.ts b/src/app/core/common-components/entities-table/entities-table.component.spec.ts index 8a3aecba04..a56a2e91ad 100644 --- a/src/app/core/common-components/entities-table/entities-table.component.spec.ts +++ b/src/app/core/common-components/entities-table/entities-table.component.spec.ts @@ -146,7 +146,7 @@ describe("EntitiesTableComponent", () => { const sortedIds = component.recordsDataSource ._orderData(component.recordsDataSource.data) - .map((c) => c.record.getId()); + .map((c) => c.record.getId(true)); expect(sortedIds).toEqual(["0", "3", "1", "2"]); }); @@ -163,7 +163,7 @@ describe("EntitiesTableComponent", () => { const sortedIds = component.recordsDataSource ._orderData(component.recordsDataSource.data) - .map((note) => note.record.getId()); + .map((note) => note.record.getId(true)); expect(sortedIds).toEqual(["0", "3", "1", "2"]); }); diff --git a/src/app/core/common-components/entities-table/entities-table.component.ts b/src/app/core/common-components/entities-table/entities-table.component.ts index 6cfb7034ba..ebcc3ea109 100644 --- a/src/app/core/common-components/entities-table/entities-table.component.ts +++ b/src/app/core/common-components/entities-table/entities-table.component.ts @@ -276,7 +276,7 @@ export class EntitiesTableComponent implements AfterViewInit { case "navigate": this.router.navigate([ entity.getConstructor().route, - entity.getId(false), + entity.getId(true), ]); break; } diff --git a/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.spec.ts b/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.spec.ts index ff5be7cbe0..023f8bdacc 100644 --- a/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.spec.ts +++ b/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.spec.ts @@ -112,7 +112,7 @@ describe("DynamicValidatorsService", () => { mockedEntityMapper.loadType.and.resolveTo([new User("existing id")]); const validators = service.buildValidators(config).asyncValidators; - await testValidator(validators[0], "new id", "existing id"); + await testValidator(validators[0], "User:new id", "User:existing id"); }); }); diff --git a/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.ts b/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.ts index 0a770733fc..2b7a024aeb 100644 --- a/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.ts +++ b/src/app/core/common-components/entity-form/dynamic-form-validators/dynamic-validators.service.ts @@ -210,7 +210,7 @@ export class DynamicValidatorsService { this.entityMapper .loadType(value) // TODO: extend this to allow checking for any configurable property (e.g. Child.name rather than only id) - .then((entities) => entities.map((entity) => entity.getId(false))), + .then((entities) => entities.map((entity) => entity.getId())), ), async: true, }; diff --git a/src/app/core/common-components/entity-form/entity-form.service.spec.ts b/src/app/core/common-components/entity-form/entity-form.service.spec.ts index 3a04a2b807..05505e8316 100644 --- a/src/app/core/common-components/entity-form/entity-form.service.spec.ts +++ b/src/app/core/common-components/entity-form/entity-form.service.spec.ts @@ -25,6 +25,7 @@ import { Child } from "../../../child-dev-project/children/model/child"; import { DatabaseField } from "../../entity/database-field.decorator"; import { EntitySchemaService } from "../../entity/schema/entity-schema.service"; import { FormFieldConfig } from "./FormConfig"; +import { User } from "../../user/user"; import { TEST_USER } from "../../user/demo-user-generator.service"; describe("EntityFormService", () => { @@ -51,13 +52,13 @@ describe("EntityFormService", () => { }); await expectAsync(service.saveChanges(formGroup, entity)).toBeRejected(); - expect(entity.getId()).not.toBe("newId"); + expect(entity.getId()).not.toBe(`${Entity.ENTITY_TYPE}:newId`); }); it("should update entity if saving is successful", async () => { const entity = new Entity("initialId"); const formGroup = new UntypedFormGroup({ - _id: new UntypedFormControl("newId"), + _id: new UntypedFormControl(`${Entity.ENTITY_TYPE}:newId`), }); TestBed.inject(EntityAbility).update([ { subject: "Entity", action: "create" }, @@ -65,7 +66,7 @@ describe("EntityFormService", () => { await service.saveChanges(formGroup, entity); - expect(entity.getId()).toBe("newId"); + expect(entity.getId()).toBe(`${Entity.ENTITY_TYPE}:newId`); }); it("should throw an error when trying to create a entity with missing permissions", async () => { @@ -246,19 +247,19 @@ describe("EntityFormService", () => { schema.defaultValue = PLACEHOLDERS.NOW; form = service.createFormGroup([{ id: "test" }], new Entity()); - expect(form.get("test").value).toEqual(new Date()); + expect(form.get("test").value).toBeDate(new Date()); schema.defaultValue = PLACEHOLDERS.CURRENT_USER; form = service.createFormGroup([{ id: "test" }], new Entity()); - expect(form.get("test")).toHaveValue(TEST_USER); + expect(form.get("test")).toHaveValue(`${User.ENTITY_TYPE}:${TEST_USER}`); schema.dataType = ArrayDatatype.dataType; form = service.createFormGroup([{ id: "test" }], new Entity()); - expect(form.get("test")).toHaveValue([TEST_USER]); + expect(form.get("test")).toHaveValue([`${User.ENTITY_TYPE}:${TEST_USER}`]); schema.dataType = EntityArrayDatatype.dataType; form = service.createFormGroup([{ id: "test" }], new Entity()); - expect(form.get("test")).toHaveValue([TEST_USER]); + expect(form.get("test")).toHaveValue([`${User.ENTITY_TYPE}:${TEST_USER}`]); Entity.schema.delete("test"); }); diff --git a/src/app/core/common-components/entity-select/entity-select.component.spec.ts b/src/app/core/common-components/entity-select/entity-select.component.spec.ts index b474f8034c..7fd73288a7 100644 --- a/src/app/core/common-components/entity-select/entity-select.component.spec.ts +++ b/src/app/core/common-components/entity-select/entity-select.component.spec.ts @@ -82,20 +82,6 @@ describe("EntitySelectComponent", () => { ); })); - it("accepts initial selection as IDs with and without prefix", fakeAsync(() => { - component.entityType = User.ENTITY_TYPE; - - component.selection = [testUsers[1].getId()]; - fixture.detectChanges(); - tick(); - expect(component.selectedEntities).toEqual([testUsers[1]]); - - component.selection = [testUsers[2].getId(true)]; - fixture.detectChanges(); - tick(); - expect(component.selectedEntities).toEqual([testUsers[2]]); - })); - it("emits whenever a new entity is selected", fakeAsync(() => { spyOn(component.selectionChange, "emit"); component.entityType = User.ENTITY_TYPE; @@ -114,25 +100,6 @@ describe("EntitySelectComponent", () => { tick(); })); - it("emits with prefix the new entity selected", fakeAsync(() => { - component.withPrefix = true; - spyOn(component.selectionChange, "emit"); - component.entityType = User.ENTITY_TYPE; - tick(); - - component.selectEntity(testUsers[0]); - expect(component.selectionChange.emit).toHaveBeenCalledWith([ - testUsers[0].getId(true), - ]); - - component.selectEntity(testUsers[1]); - expect(component.selectionChange.emit).toHaveBeenCalledWith([ - testUsers[0].getId(true), - testUsers[1].getId(true), - ]); - tick(); - })); - it("emits whenever a selected entity is removed", () => { spyOn(component.selectionChange, "emit"); component.selectedEntities = [...testUsers]; @@ -226,21 +193,10 @@ describe("EntitySelectComponent", () => { it("should be able to select entities from different types", fakeAsync(() => { component.entityType = [User.ENTITY_TYPE, Child.ENTITY_TYPE]; - component.selection = [ - testUsers[1].getId(true), - testChildren[0].getId(true), - ]; + component.selection = [testUsers[1].getId(), testChildren[0].getId()]; fixture.detectChanges(); tick(); expect(component.selectedEntities).toEqual([testUsers[1], testChildren[0]]); })); - - it("activates withPrefix automatically when multiple different types are configured", fakeAsync(() => { - component.withPrefix = false; - component.entityType = [User.ENTITY_TYPE, Child.ENTITY_TYPE]; - tick(); - fixture.detectChanges(); - expect(component.withPrefix).toBeTrue(); - })); }); diff --git a/src/app/core/common-components/entity-select/entity-select.component.ts b/src/app/core/common-components/entity-select/entity-select.component.ts index eb9b33b874..fa2237e590 100644 --- a/src/app/core/common-components/entity-select/entity-select.component.ts +++ b/src/app/core/common-components/entity-select/entity-select.component.ts @@ -50,14 +50,6 @@ export class EntitySelectComponent implements OnChanges { readonly separatorKeysCodes: number[] = [ENTER, COMMA]; readonly loadingPlaceholder = $localize`:A placeholder for the input element when select options are not loaded yet:loading...`; - /** - * Handle and emit ids including entity type prefix - default is false. - * If multiple `entityType`s are given, this automatically switches prefixes to be activated. - * - * TODO: make ids including prefix the default everywhere and remove this option (see #1526) - */ - @Input() withPrefix: boolean = false; - /** * The entity-type (e.g. 'Child', 'School', e.t.c.) to set. * @param type The ENTITY_TYPE of a Entity. This affects the entities which will be loaded and the component @@ -65,9 +57,7 @@ export class EntitySelectComponent implements OnChanges { * @throws Error when `type` is not in the entity-map */ @Input() set entityType(type: string | string[]) { - if (Array.isArray(type)) { - this.withPrefix = true; - } else { + if (!Array.isArray(type)) { type = [type]; } this.loadAvailableEntities(type); @@ -88,9 +78,9 @@ export class EntitySelectComponent implements OnChanges { untilDestroyed(this), filter((isLoading) => !isLoading), ) - .subscribe((_) => { + .subscribe(() => { this.selectedEntities = this.allEntities.filter((e) => - sel.find((s) => s === e.getId(true) || s === e.getId()), + sel.find((s) => s === e.getId()), ); }); } @@ -169,9 +159,7 @@ export class EntitySelectComponent implements OnChanges { filter((value) => value === null || typeof value === "string"), // sometimes produces entities map((searchText?: string) => this.filter(searchText)), ) - .subscribe((value) => { - this.filteredEntities = value; - }); + .subscribe((value) => (this.filteredEntities = value)); this.loading.pipe(untilDestroyed(this)).subscribe((isLoading) => { this.inputPlaceholder = isLoading ? this.loadingPlaceholder @@ -267,7 +255,7 @@ export class EntitySelectComponent implements OnChanges { */ unselectEntity(entity: E) { const index = this.selectedEntities.findIndex( - (e) => e.getId(true) === entity.getId(true), + (e) => e.getId() === entity.getId(), ); if (index !== -1) { this.selectedEntities.splice(index, 1); @@ -278,14 +266,10 @@ export class EntitySelectComponent implements OnChanges { } private emitChange() { - this.selectionChange.emit( - this.selectedEntities.map((e) => e.getId(this.withPrefix)), - ); + this.selectionChange.emit(this.selectedEntities.map((e) => e.getId())); } private isSelected(entity: E): boolean { - return this.selectedEntities.some( - (e) => e.getId(true) === entity.getId(true), - ); + return this.selectedEntities.some((e) => e.getId() === entity.getId()); } } diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index 2e29bc48d1..e17a7b7711 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -701,7 +701,7 @@ export const defaultJsonConfig = { "config": { "fieldGroups": [ { "fields": ["title"] }, - { "fields": ["type", "inactive"] }, + { "fields": ["type"] }, { "fields": ["assignedTo"] } ] } diff --git a/src/app/core/entity-details/entity-details/entity-details.component.spec.ts b/src/app/core/entity-details/entity-details/entity-details.component.spec.ts index d8c54400cc..0096d1020b 100644 --- a/src/app/core/entity-details/entity-details/entity-details.component.spec.ts +++ b/src/app/core/entity-details/entity-details/entity-details.component.spec.ts @@ -48,13 +48,9 @@ describe("EntityDetailsComponent", () => { let mockAbility: jasmine.SpyObj; beforeEach(waitForAsync(() => { - mockChildrenService = jasmine.createSpyObj([ - "queryRelationsOf", - "getAserResultsOfChild", - ]); + mockChildrenService = jasmine.createSpyObj(["queryRelations"]); mockEntityRemoveService = jasmine.createSpyObj(["remove"]); - mockChildrenService.queryRelationsOf.and.resolveTo([]); - mockChildrenService.getAserResultsOfChild.and.resolveTo([]); + mockChildrenService.queryRelations.and.resolveTo([]); mockAbility = jasmine.createSpyObj(["cannot", "update", "on"]); mockAbility.cannot.and.returnValue(false); mockAbility.on.and.returnValue(() => true); @@ -90,7 +86,7 @@ describe("EntityDetailsComponent", () => { TestBed.inject(EntityMapperService).save(testChild); tick(); component.creatingNew = false; - component.id = testChild.getId(); + component.id = testChild.getId(true); component.ngOnChanges(simpleChangesFor(component, "id")); tick(); @@ -111,16 +107,42 @@ describe("EntityDetailsComponent", () => { tick(); spyOn(entityMapper, "load").and.callThrough(); - component.id = testChild.getId(); + component.id = testChild.getId(true); component.ngOnChanges(simpleChangesFor(component, "id")); expect(component.isLoading).toBeTrue(); tick(); - expect(entityMapper.load).toHaveBeenCalledWith(Child, testChild.getId()); + expect(entityMapper.load).toHaveBeenCalledWith( + Child, + testChild.getId(true), + ); expect(component.record).toBe(testChild); expect(component.isLoading).toBeFalse(); })); + it("should also support the long ID format", fakeAsync(() => { + const child = new Child(); + const entityMapper = TestBed.inject(EntityMapperService); + entityMapper.save(child); + tick(); + spyOn(entityMapper, "load").and.callThrough(); + + component.id = child.getId(); + component.ngOnChanges(simpleChangesFor(component, "id")); + tick(); + + expect(entityMapper.load).toHaveBeenCalledWith(Child, child.getId()); + expect(component.record).toEqual(child); + + // entity is updated + const childUpdate = child.copy(); + childUpdate.name = "update"; + entityMapper.save(childUpdate); + tick(); + + expect(component.record).toEqual(childUpdate); + })); + it("should call router when user is not permitted to create entities", () => { mockAbility.cannot.and.returnValue(true); const router = fixture.debugElement.injector.get(Router); diff --git a/src/app/core/entity-details/entity-details/entity-details.component.ts b/src/app/core/entity-details/entity-details/entity-details.component.ts index 84ad5e9cef..1dcfe540fb 100644 --- a/src/app/core/entity-details/entity-details/entity-details.component.ts +++ b/src/app/core/entity-details/entity-details/entity-details.component.ts @@ -97,7 +97,7 @@ export class EntityDetailsComponent implements OnChanges { this.entityConstructor = this.entities.get(this.entityType); } if (changes.id) { - this.loadEntity(this.id); + this.loadEntity(); this.subscribeToEntityChanges(); // `initPanels()` is already called inside `loadEntity()` } else if (changes.panels) { @@ -106,19 +106,20 @@ export class EntityDetailsComponent implements OnChanges { } private subscribeToEntityChanges() { + const fullId = Entity.createPrefixedId(this.entityType, this.id); this.changesSubscription?.unsubscribe(); this.changesSubscription = this.entityMapperService .receiveUpdates(this.entityConstructor) .pipe( - filter(({ entity }) => entity.getId() === this.id), + filter(({ entity }) => entity.getId() === fullId), filter(({ type }) => type !== "remove"), untilDestroyed(this), ) .subscribe(({ entity }) => (this.record = entity)); } - private async loadEntity(id: string) { - if (id === "new") { + private async loadEntity() { + if (this.id === "new") { if (this.ability.cannot("create", this.entityConstructor)) { this.router.navigate([""]); return; @@ -129,7 +130,7 @@ export class EntityDetailsComponent implements OnChanges { this.creatingNew = false; this.record = await this.entityMapperService.load( this.entityConstructor, - id, + this.id, ); } this.initPanels(); diff --git a/src/app/core/entity-details/entity-details/entity-details.stories.ts b/src/app/core/entity-details/entity-details/entity-details.stories.ts index 88ee5f5f0f..f22b5d6add 100644 --- a/src/app/core/entity-details/entity-details/entity-details.stories.ts +++ b/src/app/core/entity-details/entity-details/entity-details.stories.ts @@ -54,6 +54,6 @@ const Template: StoryFn = ( export const Primary = Template.bind({}); Primary.args = { - id: demoEntity.getId(false), + id: demoEntity.getId(true), ...config, }; diff --git a/src/app/core/entity-details/form/form.component.spec.ts b/src/app/core/entity-details/form/form.component.spec.ts index 5b4de66acf..715fbb928d 100644 --- a/src/app/core/entity-details/form/form.component.spec.ts +++ b/src/app/core/entity-details/form/form.component.spec.ts @@ -54,7 +54,7 @@ describe("FormComponent", () => { await component.saveClicked(); expect(entityFormService.saveChanges).toHaveBeenCalled(); - expect(router.navigate).toHaveBeenCalledWith(["", testChild.getId()]); + expect(router.navigate).toHaveBeenCalledWith(["", testChild.getId(true)]); }); it("should show an alert when form service rejects saving", async () => { diff --git a/src/app/core/entity-details/form/form.component.ts b/src/app/core/entity-details/form/form.component.ts index 1071f27b70..76aaefcbb3 100644 --- a/src/app/core/entity-details/form/form.component.ts +++ b/src/app/core/entity-details/form/form.component.ts @@ -66,7 +66,7 @@ export class FormComponent implements FormConfig, OnInit { if (this.creatingNew) { await this.router.navigate([ getParentUrl(this.router), - this.entity.getId(), + this.entity.getId(true), ]); } } catch (err) { diff --git a/src/app/core/entity-details/related-entities/related-entities.component.spec.ts b/src/app/core/entity-details/related-entities/related-entities.component.spec.ts index b2c98f7a22..6783e32efa 100644 --- a/src/app/core/entity-details/related-entities/related-entities.component.spec.ts +++ b/src/app/core/entity-details/related-entities/related-entities.component.spec.ts @@ -76,8 +76,10 @@ describe("RelatedEntitiesComponent", () => { await entityMapper.saveAll([c1, r1, rEmpty]); component.entity = c1; + console.log("setting type"); component.entityType = Note.ENTITY_TYPE; component.property = "children"; + console.log("initializing"); await component.ngOnInit(); expect(component.data).toEqual([r1]); diff --git a/src/app/core/entity-details/related-time-period-entities/related-time-period-entities.component.spec.ts b/src/app/core/entity-details/related-time-period-entities/related-time-period-entities.component.spec.ts index 4727633e12..a257504973 100644 --- a/src/app/core/entity-details/related-time-period-entities/related-time-period-entities.component.spec.ts +++ b/src/app/core/entity-details/related-time-period-entities/related-time-period-entities.component.spec.ts @@ -68,8 +68,8 @@ describe("RelatedTimePeriodEntitiesComponent", () => { it("should load correctly filtered data", async () => { const testSchool = new School(); active1.schoolId = testSchool.getId(); - active2.schoolId = "some-other-id"; - inactive.schoolId = "some-other-id"; + active2.schoolId = "School:some-other-id"; + inactive.schoolId = "School:some-other-id"; const loadType = spyOn(entityMapper, "loadType"); loadType.and.resolveTo([active1, active2, inactive]); @@ -126,7 +126,7 @@ describe("RelatedTimePeriodEntitiesComponent", () => { const existingRelation = new ChildSchoolRelation(); existingRelation.start = moment().subtract(1, "year").toDate(); existingRelation.end = moment().subtract(1, "week").toDate(); - existingRelation.childId = child.getId(false); + existingRelation.childId = child.getId(); const loadType = spyOn(entityMapper, "loadType"); loadType.and.resolveTo([existingRelation]); diff --git a/src/app/core/entity/entity-actions/cascading-entity-action.ts b/src/app/core/entity/entity-actions/cascading-entity-action.ts index 177094c56d..a2df010a5a 100644 --- a/src/app/core/entity/entity-actions/cascading-entity-action.ts +++ b/src/app/core/entity/entity-actions/cascading-entity-action.ts @@ -86,10 +86,8 @@ export abstract class CascadingEntityAction { const entities = await this.entityMapper.loadType(refType.entityType); for (const refField of refType.referencingProperties) { - const affectedEntities = entities.filter( - (e) => - asArray(e[refField]).includes(entity.getId()) || - asArray(e[refField]).includes(entity.getId(true)), + const affectedEntities = entities.filter((e) => + asArray(e[refField]).includes(entity.getId()), ); for (const e of affectedEntities) { diff --git a/src/app/core/entity/entity-actions/entity-delete.service.spec.ts b/src/app/core/entity/entity-actions/entity-delete.service.spec.ts index fb475b56c7..9fef2389d4 100644 --- a/src/app/core/entity/entity-actions/entity-delete.service.spec.ts +++ b/src/app/core/entity/entity-actions/entity-delete.service.spec.ts @@ -50,8 +50,7 @@ describe("EntityDeleteService", () => { ) { const result = entity.copy(); result[property] = result[property].filter( - (id) => - id !== referencedEntity.getId() && id !== referencedEntity.getId(true), + (id) => id !== referencedEntity.getId(), ); return result; } @@ -184,21 +183,18 @@ describe("EntityDeleteService", () => { const primary = new Child(); const note = new Note(); note.subject = "test"; - note.children = [primary.getId(), "some-other"]; - note.relatedEntities = [primary.getId(true)]; + note.children = [primary.getId(), "Child:some-other"]; + note.relatedEntities = [primary.getId()]; const originalNote = note.copy(); await entityMapper.save(primary); await entityMapper.save(note); const result = await service.deleteEntity(primary); - const actualNote = entityMapper.get( - Note.ENTITY_TYPE, - note.getId(true), - ) as Note; + const actualNote = entityMapper.get(Note.ENTITY_TYPE, note.getId()) as Note; expect(actualNote.relatedEntities).toEqual([]); - expect(actualNote.children).toEqual(["some-other"]); + expect(actualNote.children).toEqual(["Child:some-other"]); expect(result.originalEntitiesBeforeChange.length).toBe(2); expectEntitiesToMatch(result.originalEntitiesBeforeChange, [ diff --git a/src/app/core/entity/entity-actions/entity-delete.service.ts b/src/app/core/entity/entity-actions/entity-delete.service.ts index 6f28fb8280..55c62bc823 100644 --- a/src/app/core/entity/entity-actions/entity-delete.service.ts +++ b/src/app/core/entity/entity-actions/entity-delete.service.ts @@ -68,11 +68,7 @@ export class EntityDeleteService extends CascadingEntityAction { if (Array.isArray(relatedEntityWithReference[refField])) { relatedEntityWithReference[refField] = relatedEntityWithReference[ refField - ].filter( - (id) => - id !== referencedEntity.getId() && - id !== referencedEntity.getId(true), - ); + ].filter((id) => id !== referencedEntity.getId()); } else { delete relatedEntityWithReference[refField]; } diff --git a/src/app/core/entity/entity-config.service.spec.ts b/src/app/core/entity/entity-config.service.spec.ts index 8d8ec3149e..8f5e21bfe0 100644 --- a/src/app/core/entity/entity-config.service.spec.ts +++ b/src/app/core/entity/entity-config.service.spec.ts @@ -162,12 +162,12 @@ describe("EntityConfigService", () => { expect(dynamicEntity.schema.get("dynamicProperty")).toEqual(schema); const dynamicInstance = new dynamicEntity("someId"); expect(dynamicInstance instanceof Test).toBeTrue(); - expect(dynamicInstance.getId(true)).toBe("DynamicTest:someId"); + expect(dynamicInstance.getId()).toBe("DynamicTest:someId"); // it should overwrite anything in the extended entity expect(Test.schema.has("dynamicProperty")).toBeFalse(); const parentInstance = new Test("otherId"); - expect(parentInstance.getId(true)).toBe("Test:otherId"); + expect(parentInstance.getId()).toBe("Test:otherId"); }); it("should subclass entity if no extension is specified", () => { @@ -187,7 +187,7 @@ describe("EntityConfigService", () => { ]); const dynamicInstance = new dynamicEntity("someId"); expect(dynamicInstance instanceof Entity).toBeTrue(); - expect(dynamicInstance.getId(true)).toBe("NoExtends:someId"); + expect(dynamicInstance.getId()).toBe("NoExtends:someId"); }); }); diff --git a/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts b/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts index 524548e485..4bd176b2c6 100644 --- a/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts +++ b/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts @@ -32,13 +32,11 @@ describe("EntityMapperService", () => { const existingEntity = { _id: "Entity:existing-entity", - entityId: "existing-entity", label: "entity from database", }; const existingEntity2 = { _id: "Entity:existing-entity2", - entityId: "existing-entity2", label: "entity 2 from database", }; @@ -66,18 +64,14 @@ describe("EntityMapperService", () => { }); function expectEntity(actualEntity, expectedEntity) { - expect(actualEntity.getId()).toBe(expectedEntity.entityId); - expect( - Entity.createPrefixedId(actualEntity.getType(), actualEntity.getId()), - ).toBe(expectedEntity._id); - + expect(actualEntity.getId()).toBe(expectedEntity._id); expect(actualEntity).toBeInstanceOf(Entity); } it("loads existing entity", async () => { const loadedEntity = await entityMapper.load( Entity, - existingEntity.entityId, + existingEntity._id, ); expectEntity(loadedEntity, existingEntity); }); @@ -111,16 +105,13 @@ describe("EntityMapperService", () => { it("saves new entity and loads it", async () => { const entity = new Entity("test1"); - await entityMapper.save(entity); - const loadedEntity = await entityMapper.load( - Entity, - entity.getId(), - ); + await entityMapper.save(entity); + const loadedEntity = await entityMapper.load(Entity, entity.getId()); expectEntity(loadedEntity, entity); }); it("rejects promise when saving new entity with existing entityId", async () => { - const duplicateEntity = new Entity(existingEntity.entityId); + const duplicateEntity = new Entity(existingEntity._id); await entityMapper .save(duplicateEntity) @@ -133,24 +124,18 @@ describe("EntityMapperService", () => { }); it("saves new version of existing entity", async () => { - const loadedEntity = await entityMapper.load( - Entity, - existingEntity.entityId, - ); - expect(loadedEntity).toHaveId(existingEntity.entityId); + const loadedEntity = await entityMapper.load(Entity, existingEntity._id); + expect(loadedEntity.getId()).toEqual(existingEntity._id); await entityMapper.save(loadedEntity); }); it("removes existing entity", async () => { - const loadedEntity = await entityMapper.load( - Entity, - existingEntity.entityId, - ); + const loadedEntity = await entityMapper.load(Entity, existingEntity._id); await entityMapper.remove(loadedEntity); await expectAsync( - entityMapper.load(Entity, existingEntity.entityId), + entityMapper.load(Entity, existingEntity._id), ).toBeRejected(); }); @@ -170,25 +155,23 @@ describe("EntityMapperService", () => { const testEntity = new Entity(testId); await entityMapper.save(testEntity); - const loadedByEntityId = await entityMapper.load( + const loadedByShortId = await entityMapper.load( Entity, - testEntity.getId(), + testEntity.getId(true), ); - expect(loadedByEntityId).toBeDefined(); + expect(loadedByShortId).toBeDefined(); + expect(loadedByShortId.getId().startsWith(Entity.ENTITY_TYPE)).toBeTrue(); - expect( - loadedByEntityId.getId(true).startsWith(Entity.ENTITY_TYPE), - ).toBeTrue(); - const loadedByFullId = await entityMapper.load( + const loadedByFullId = await entityMapper.load( Entity, - loadedByEntityId.getId(true), + loadedByShortId.getId(), ); - expect(loadedByFullId.getId(true)).toBe(loadedByEntityId.getId(true)); - expect(loadedByFullId._rev).toBe(loadedByEntityId._rev); + expect(loadedByFullId.getId()).toBe(loadedByShortId.getId()); + expect(loadedByFullId._rev).toBe(loadedByShortId._rev); }); it("publishes updates to any listeners", () => { - const testId = "t1"; + const testId = "Entity:t1"; const testEntity = new Entity(testId); entityMapper .save(testEntity, true) @@ -199,22 +182,22 @@ describe("EntityMapperService", () => { it("publishes when an existing entity is updated", () => { entityMapper - .load(Entity, existingEntity.entityId) + .load(Entity, existingEntity._id) .then((loadedEntity) => entityMapper.save(loadedEntity)); - return receiveUpdatesAndTestTypeAndId("update", existingEntity.entityId); + return receiveUpdatesAndTestTypeAndId("update", existingEntity._id); }); it("publishes when an existing entity is deleted", () => { entityMapper - .load(Entity, existingEntity.entityId) + .load(Entity, existingEntity._id) .then((loadedEntity) => entityMapper.remove(loadedEntity)); - return receiveUpdatesAndTestTypeAndId("remove", existingEntity.entityId); + return receiveUpdatesAndTestTypeAndId("remove", existingEntity._id); }); it("publishes when a new entity is being saved", () => { - const testId = "t1"; + const testId = "Entity:t1"; const testEntity = new Entity(testId); entityMapper.save(testEntity, true); @@ -278,13 +261,17 @@ describe("EntityMapperService", () => { const mockTime1 = 1; jasmine.clock().mockDate(new Date(mockTime1)); - await entityMapper.save(entity); - const createdEntity = await entityMapper.load(Entity, id); + await entityMapper.save(entity); + const createdEntity = await entityMapper.load(Entity, id); expect(createdEntity.created?.at.getTime()).toEqual(mockTime1); - expect(createdEntity.created?.by).toEqual(`User:${TEST_USER}`); + expect(createdEntity.created?.by).toEqual( + `${User.ENTITY_TYPE}:${TEST_USER}`, + ); expect(createdEntity.updated?.at.getTime()).toEqual(mockTime1); - expect(createdEntity.updated?.by).toEqual(`User:${TEST_USER}`); + expect(createdEntity.updated?.by).toEqual( + `${User.ENTITY_TYPE}:${TEST_USER}`, + ); const mockTime2 = mockTime1 + 1; jasmine.clock().mockDate(new Date(mockTime2)); diff --git a/src/app/core/entity/entity-mapper/entity-mapper.service.ts b/src/app/core/entity/entity-mapper/entity-mapper.service.ts index e2b4b5fe26..d386744c6f 100644 --- a/src/app/core/entity/entity-mapper/entity-mapper.service.ts +++ b/src/app/core/entity/entity-mapper/entity-mapper.service.ts @@ -195,7 +195,7 @@ export class EntityMapperService { } protected setEntityMetadata(entity: Entity) { - const newMetadata = new UpdateMetadata(this.currentUser.value?.getId(true)); + const newMetadata = new UpdateMetadata(this.currentUser.value?.getId()); if (entity.isNew) { entity.created = newMetadata; } diff --git a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts index 3ff737d5dd..1d3769c89d 100644 --- a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts +++ b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts @@ -81,7 +81,7 @@ export class MockEntityMapperService extends EntityMapperService { * @param id */ public get(entityType: string, id: string): Entity { - const entityId = Entity.extractEntityIdFromId(id); + const entityId = Entity.createPrefixedId(entityType, id); const result = this.data.get(entityType)?.get(entityId); if (!result) { throw new HttpErrorResponse({ status: 404 }); diff --git a/src/app/core/entity/latest-entity-loader.ts b/src/app/core/entity/latest-entity-loader.ts index 296121fe59..66d7cd0b1d 100644 --- a/src/app/core/entity/latest-entity-loader.ts +++ b/src/app/core/entity/latest-entity-loader.ts @@ -29,10 +29,8 @@ export abstract class LatestEntityLoader { const initialValue = await this.loadOnce(); this.entityMapper .receiveUpdates(this.entityCtor) - .pipe(filter(({ entity }) => entity.getId() === this.entityID)) - .subscribe(({ entity }) => { - this.entityUpdated.next(entity); - }); + .pipe(filter(({ entity }) => entity.getId(true) === this.entityID)) + .subscribe(({ entity }) => this.entityUpdated.next(entity)); return initialValue; } diff --git a/src/app/core/entity/model/entity-update.spec.ts b/src/app/core/entity/model/entity-update.spec.ts index 96b2a55e8d..8a043cfb4f 100644 --- a/src/app/core/entity/model/entity-update.spec.ts +++ b/src/app/core/entity/model/entity-update.spec.ts @@ -29,11 +29,11 @@ describe("entity-update", () => { type: "new", }); expect(newEntities).toHaveSize(existingEntities.length + 1); - expect(newEntities.find((e) => e.getId() === "n6")).toBeDefined(); + expect(newEntities.find((e) => e.getId(true) === "n6")).toBeDefined(); }); it("updates the entity-list when an existing entity should be updated", () => { - const indexOfN2 = existingEntities.findIndex((e) => e.getId() === "n2"); + const indexOfN2 = existingEntities.findIndex((e) => e.getId(true) === "n2"); const newEntities = applyUpdate(existingEntities, { entity: new TestEntity("n2", 2), type: "update", @@ -49,7 +49,7 @@ describe("entity-update", () => { type: "remove", }); expect(newEntities).toHaveSize(oldLength - 1); - expect(newEntities.findIndex((e) => e.getId() === "n2")).toBe(-1); + expect(newEntities.findIndex((e) => e.getId(true) === "n2")).toBe(-1); }); it("does not change the list when the passed updated-entity is illegal", () => { diff --git a/src/app/core/entity/model/entity.spec.ts b/src/app/core/entity/model/entity.spec.ts index b3480503e4..63673b3142 100644 --- a/src/app/core/entity/model/entity.spec.ts +++ b/src/app/core/entity/model/entity.spec.ts @@ -168,8 +168,8 @@ export function testEntitySubclass( const entity = new entityClass(id); // correct ID - expect(entity).toHaveId(id); - expect(Entity.extractEntityIdFromId(entity.getId(true))).toBe(id); + expect(entity.getId()).toEqual(`${entityType}:${id}`); + expect(Entity.extractEntityIdFromId(entity.getId())).toBe(id); // correct Type expect(entity).toBeInstanceOf(entityClass); diff --git a/src/app/core/entity/model/entity.ts b/src/app/core/entity/model/entity.ts index ae0ee720fd..60a93a4858 100644 --- a/src/app/core/entity/model/entity.ts +++ b/src/app/core/entity/model/entity.ts @@ -280,9 +280,8 @@ export class Entity { * * @returns {string} the unique id of this entity */ - public getId(withPrefix: boolean = false): string { - if (withPrefix) return this._id; - return this.entityId; + public getId(withoutPrefix = false): string { + return withoutPrefix ? this.entityId : this._id; } /** diff --git a/src/app/core/entity/schema/entity-schema-field.ts b/src/app/core/entity/schema/entity-schema-field.ts index f0683a9dae..4558ca3f28 100644 --- a/src/app/core/entity/schema/entity-schema-field.ts +++ b/src/app/core/entity/schema/entity-schema-field.ts @@ -46,7 +46,7 @@ export interface EntitySchemaField { * * @todo not implemented yet */ - generateIndex?: boolean; // TODO: implement index support in EntitySchema + generateIndex?: boolean; /** * If set to `true`, the entity can be found in the global search by entering this value diff --git a/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts b/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts index 8a494ed4e4..f025a88fc7 100644 --- a/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts +++ b/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts @@ -205,7 +205,7 @@ describe("DataTransformationService", () => { { participant: "child with school", Name: school.name, - school_id: school.getId(), + school_id: school.getId(true), }, ]); }); diff --git a/src/app/core/export/data-transformation-service/export-column-config.ts b/src/app/core/export/data-transformation-service/export-column-config.ts index 0c9f72e572..7eec5a4fe5 100644 --- a/src/app/core/export/data-transformation-service/export-column-config.ts +++ b/src/app/core/export/data-transformation-service/export-column-config.ts @@ -19,7 +19,7 @@ export interface ExportColumnConfig { * each object in the parent query result will lead to its own row in the final export (extending one object into multiple export rows); * each query in the subQueries will lead to one (or recursively more) columns in the export rows. * - * e.g. `{ query: ".participants:toEntities(Child)", subQueries: [ {query: "name"}, {query: "phone"} ] }` + * e.g. `{ query: ".participants:toEntities(Child), subQueries: [ {query: "name"}, {query: "phone"} ] }` * => parent query (not output in export): [{..child1}, {..child2}] * => overall result: two export rows: [{ name: "child1", phone: "123"}, {name: "child2", phone: "567"}] */ diff --git a/src/app/core/export/query.service.spec.ts b/src/app/core/export/query.service.spec.ts index f70d399a05..06799e63ae 100644 --- a/src/app/core/export/query.service.spec.ts +++ b/src/app/core/export/query.service.spec.ts @@ -90,8 +90,8 @@ describe("QueryService", () => { const maleChildrenOnPrivateSchoolsQuery = ` ${School.ENTITY_TYPE}:toArray[*privateSchool=true] :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) - [*isActive=true].childId:addPrefix(${Child.ENTITY_TYPE}):unique - :toEntities:filterByObjectAttribute(gender, id, M)`; + [*isActive=true].childId:unique:toEntities(${Child.ENTITY_TYPE}) + :filterByObjectAttribute(gender, id, M)`; const maleChildrenOnPrivateSchools = await queryData( maleChildrenOnPrivateSchoolsQuery, @@ -101,7 +101,7 @@ describe("QueryService", () => { const childrenVisitingAnySchoolQuery = ` ${School.ENTITY_TYPE}:toArray :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) - [*isActive=true].childId:addPrefix(${Child.ENTITY_TYPE}):unique:toEntities`; + [*isActive=true].childId:unique:toEntities(${Child.ENTITY_TYPE})`; const childrenVisitingAnySchool = await queryData( childrenVisitingAnySchoolQuery, ); @@ -176,7 +176,7 @@ describe("QueryService", () => { const childrenThatAttendedSomethingQuery = ` ${EventNote.ENTITY_TYPE}:toArray[*date > ?] :getParticipantsWithAttendance(PRESENT) - :addPrefix(${Child.ENTITY_TYPE}):unique:toEntities`; + :unique:toEntities(${Child.ENTITY_TYPE})`; const childrenThatAttendedSomething = await queryData( childrenThatAttendedSomethingQuery, moment().subtract(1, "week").toDate(), @@ -218,8 +218,8 @@ describe("QueryService", () => { ${School.ENTITY_TYPE}:toArray[*privateSchool=true] :getRelated(${RecurringActivity.ENTITY_TYPE}, linkedGroups) :getRelated(${EventNote.ENTITY_TYPE}, relatesTo) - :getParticipantsWithAttendance(PRESENT):addPrefix(${Child.ENTITY_TYPE}):unique - :toEntities:filterByObjectAttribute(gender, id, F)`; + :getParticipantsWithAttendance(PRESENT):unique + :toEntities(${Child.ENTITY_TYPE}):filterByObjectAttribute(gender, id, F)`; const femaleParticipantsInPrivateSchools = await queryData( femaleParticipantsPrivateSchoolQuery, ); @@ -231,8 +231,8 @@ describe("QueryService", () => { ${School.ENTITY_TYPE}:toArray[*privateSchool!=true] :getRelated(${RecurringActivity.ENTITY_TYPE}, linkedGroups) :getRelated(${EventNote.ENTITY_TYPE}, relatesTo) - :getParticipantsWithAttendance(PRESENT):addPrefix(${Child.ENTITY_TYPE}):unique - :toEntities`; + :getParticipantsWithAttendance(PRESENT):unique + :toEntities(${Child.ENTITY_TYPE})`; const participantsNotPrivateSchool = await queryData( participantsNotPrivateSchoolQuery, ); @@ -240,8 +240,8 @@ describe("QueryService", () => { const attendedParticipantsQuery = ` ${EventNote.ENTITY_TYPE}:toArray - :getParticipantsWithAttendance(PRESENT):addPrefix(${Child.ENTITY_TYPE}):unique - :toEntities`; + :getParticipantsWithAttendance(PRESENT):unique + :toEntities(${Child.ENTITY_TYPE})`; const attendedParticipants = await queryData(attendedParticipantsQuery); expectEntitiesToMatch(attendedParticipants, [ femalePrivatePresent, @@ -361,9 +361,9 @@ describe("QueryService", () => { ); expect(allEventsLastWeek).toEqual( jasmine.arrayWithExactContents([ - oneWeekAgo.getId(), - threeDaysAgo.getId(), - today.getId(), + oneWeekAgo.getId(true), + threeDaysAgo.getId(true), + today.getId(true), ]), ); }); @@ -445,28 +445,6 @@ describe("QueryService", () => { expectEntitiesToMatch(eventsWithNotes, [note1, note2, eventNote]); }); - it("should do addPrefix as part of toEntities if optional parameter is given", async () => { - const child1 = await createChild(); - const child2 = await createChild(); - const child3 = await createChild(); - await createSchool([child1, child3, child2]); - - const queryWithAddPrefix = ` - ${School.ENTITY_TYPE}:toArray - :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) - .childId:addPrefix(${Child.ENTITY_TYPE}):toEntities.gender`; - const queryWithoutAddPrefix = ` - ${School.ENTITY_TYPE}:toArray - :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) - .childId:toEntities(${Child.ENTITY_TYPE}).gender`; - - const resultWithAddPrefix = await queryData(queryWithAddPrefix); - const resultWithoutAddPrefix = await queryData(queryWithoutAddPrefix); - - expect(resultWithoutAddPrefix).toHaveSize(3); - expect(resultWithoutAddPrefix).toEqual(resultWithAddPrefix); - }); - it("should create an attendance array with the current school", async () => { const presentTwiceWithSchool = await createChild(); const presentOnceWithoutSchool = await createChild(); diff --git a/src/app/core/export/query.service.ts b/src/app/core/export/query.service.ts index 52c2b3ca2c..62640491d9 100644 --- a/src/app/core/export/query.service.ts +++ b/src/app/core/export/query.service.ts @@ -99,7 +99,6 @@ export class QueryService { count: this.count, sum: this.sum, avg: this.avg, - addPrefix: this.addPrefix, toEntities: this.toEntities.bind(this), getRelated: this.getRelated.bind(this), filterByObjectAttribute: this.filterByObjectAttribute, @@ -150,9 +149,9 @@ export class QueryService { .receiveUpdates(ENTITY_TYPE) .subscribe(({ entity, type }) => { if (type === "remove") { - delete this.entities[ENTITY_TYPE][entity.getId(true)]; + delete this.entities[ENTITY_TYPE][entity.getId()]; } else { - this.entities[ENTITY_TYPE][entity.getId(true)] = entity; + this.entities[ENTITY_TYPE][entity.getId()] = entity; } }); }); @@ -192,20 +191,10 @@ export class QueryService { this.entities[entityClass.ENTITY_TYPE] = {}; entities.forEach( (entity) => - (this.entities[entityClass.ENTITY_TYPE][entity.getId(true)] = entity), + (this.entities[entityClass.ENTITY_TYPE][entity.getId()] = entity), ); } - /** - * Adds the prefix and a colon (":") to each string in a array. Does nothing if the string already starts with the prefix. - * @param ids a string of ids - * @param prefix the prefix which should be added to the string - * @returns a list where every string has the prefix - */ - private addPrefix(ids: string[], prefix: string): string[] { - return ids.map((id) => Entity.createPrefixedId(prefix, id)); - } - /** * Creates an array containing the value of each key of the object. * e.g. `{a: 1, b: 2} => [1,2]` @@ -271,18 +260,17 @@ export class QueryService { /** * Turns a list of ids (with the entity prefix) into a list of entities * @param ids the array of ids with entity prefix - * @param entityPrefix (Optional) entity type prefix that should be added to the given ids where prefix is still missing + * @param entityPrefix indicate the type of entity that should be loaded. This is required for pre-loading the required entities. * @returns a list of entity objects */ - private toEntities(ids: string[], entityPrefix?: string): Entity[] { + private toEntities(ids: string[], entityPrefix: string): Entity[] { + if (!entityPrefix) { + throw new Error("Entity type not defined"); + } if (!ids) { return []; } - if (entityPrefix) { - ids = this.addPrefix(ids, entityPrefix); - } - return ids .map((id) => { const prefix = id.split(":")[0]; @@ -313,15 +301,11 @@ export class QueryService { Array.isArray(targetEntities[0][relationKey]) ) { return targetEntities.filter((entity) => - (entity[relationKey] as Array).some((id) => - srcIds.includes(id.split(":").pop()), - ), + (entity[relationKey] as string[]).some((id) => srcIds.includes(id)), ); } else { return targetEntities.filter((entity) => - entity[relationKey] - ? srcIds.includes(entity[relationKey].split(":").pop()) - : false, + entity[relationKey] ? srcIds.includes(entity[relationKey]) : false, ); } } diff --git a/src/app/core/filter/filters/entityFilter.ts b/src/app/core/filter/filters/entityFilter.ts index d26af48246..5bd54c31c4 100644 --- a/src/app/core/filter/filters/entityFilter.ts +++ b/src/app/core/filter/filters/entityFilter.ts @@ -2,11 +2,10 @@ import { Entity } from "../../entity/model/entity"; import { FilterSelectionOption, SelectableFilter } from "./filters"; export class EntityFilter extends SelectableFilter { - constructor(name: string, label: string, filterEntities) { + constructor(name: string, label: string, filterEntities: Entity[]) { filterEntities.sort((a, b) => a.toString().localeCompare(b.toString())); - const options: FilterSelectionOption[] = []; - options.push( - ...filterEntities.map((filterEntity) => ({ + const options: FilterSelectionOption[] = filterEntities.map( + (filterEntity) => ({ key: filterEntity.getId(), label: filterEntity.toString(), filter: { @@ -15,7 +14,7 @@ export class EntityFilter extends SelectableFilter { { [name]: { $elemMatch: { $eq: filterEntity.getId() } } }, ], }, - })), + }), ); super(name, options, label); } diff --git a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts index 796192eac0..aa3f0c19f7 100644 --- a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts +++ b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts @@ -90,7 +90,7 @@ describe("DialogButtonsComponent", () => { component.ngOnInit(); - expect(component.detailsRoute).toBe(`${Child.route}/${child.getId()}`); + expect(component.detailsRoute).toBe(`${Child.route}/${child.getId(true)}`); }); it("should close the dialog if a entity is deleted", async () => { diff --git a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.ts b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.ts index b7567487aa..a0957547df 100644 --- a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.ts +++ b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.ts @@ -75,7 +75,7 @@ export class DialogButtonsComponent implements OnInit { route && this.router.config.some((r) => "/" + r.path === route + "/:id") ) { - this.detailsRoute = route + "/" + this.entity.getId(); + this.detailsRoute = route + "/" + this.entity.getId(true); } } diff --git a/src/app/core/import/import.service.spec.ts b/src/app/core/import/import.service.spec.ts index 34b196179a..31d1c7cfb1 100644 --- a/src/app/core/import/import.service.spec.ts +++ b/src/app/core/import/import.service.spec.ts @@ -49,7 +49,7 @@ describe("ImportService", () => { expect(entityMapper.save).toHaveBeenCalledWith( jasmine.objectContaining({ - ids: testEntities.map((e) => e.getId(true)), + ids: testEntities.map((e) => e.getId()), config: testImportSettings, }), ); @@ -125,20 +125,20 @@ describe("ImportService", () => { entityType: "Child", columnMapping: undefined, additionalActions: [ - { type: "RecurringActivity", id: "3" }, - { type: "School", id: "4" }, + { type: "RecurringActivity", id: "RecurringActivity:3" }, + { type: "School", id: "School:4" }, ], }; await service.executeImport(testEntities, testImportSettings); const createRelations = await entityMapper.loadType(ChildSchoolRelation); const expectedRelations = [ - { childId: "1", schoolId: "4" }, - { childId: "2", schoolId: "4" }, + { childId: "Child:1", schoolId: "School:4" }, + { childId: "Child:2", schoolId: "School:4" }, ].map((e) => Object.assign(new ChildSchoolRelation(), e)); expectEntitiesToMatch(createRelations, expectedRelations, true); - expect(activity.participants).toEqual(["1", "2"]); + expect(activity.participants).toEqual(["Child:1", "Child:2"]); }); it("should allow to remove entities and links", async () => { diff --git a/src/app/core/import/import.service.ts b/src/app/core/import/import.service.ts index 82790d991b..8f310856ac 100644 --- a/src/app/core/import/import.service.ts +++ b/src/app/core/import/import.service.ts @@ -63,7 +63,7 @@ export class ImportService { ) { const importMeta = new ImportMetadata(); importMeta.config = settings; - importMeta.ids = savedEntities.map((entity) => entity.getId(true)); + importMeta.ids = savedEntities.map((entity) => entity.getId()); await this.entityMapper.save(importMeta); return importMeta; } diff --git a/src/app/core/import/import/import-sample-raw-data.ts b/src/app/core/import/import/import-sample-raw-data.ts index cfc3cd3a66..0b287a54f2 100644 --- a/src/app/core/import/import/import-sample-raw-data.ts +++ b/src/app/core/import/import/import-sample-raw-data.ts @@ -47,9 +47,9 @@ export const IMPORT_SAMPLE_LINKABLE_DATA: Entity[] = [ export const IMPORT_SAMPLE_ADDITIONAL_ACTIONS: AdditionalImportAction[] = [ { type: "School", - id: IMPORT_SAMPLE_LINKABLE_DATA.find( - (e) => e.getType() === "School", - ).getId(), + id: IMPORT_SAMPLE_LINKABLE_DATA.find((e) => e.getType() === "School").getId( + true, + ), }, { type: "RecurringActivity", diff --git a/src/app/core/session/session-service/session-manager.service.ts b/src/app/core/session/session-service/session-manager.service.ts index 22259249e5..245c7c3076 100644 --- a/src/app/core/session/session-service/session-manager.service.ts +++ b/src/app/core/session/session-service/session-manager.service.ts @@ -101,7 +101,6 @@ export class SessionManagerService { this.sessionInfo.next(user); this.loginStateSubject.next(LoginState.LOGGED_IN); - // TODO allow generic entities with fallback to User entity this.entityMapper .load(User, user.name) .then((res) => this.currentUser.next(res)) @@ -111,7 +110,7 @@ export class SessionManagerService { .pipe( filter( ({ entity }) => - entity.getId(true) === user.name || entity.getId() === user.name, + entity.getId() === user.name || entity.getId(true) === user.name, ), ) .subscribe(({ entity }) => this.currentUser.next(entity)); diff --git a/src/app/core/support/support/support.component.ts b/src/app/core/support/support/support.component.ts index aaeb0d9e32..f29da86cf9 100644 --- a/src/app/core/support/support/support.component.ts +++ b/src/app/core/support/support/support.component.ts @@ -133,7 +133,7 @@ export class SupportComponent implements OnInit { user: { name: this.sessionInfo.name }, level: "debug", extra: { - currentUser: this.currentUser.getId(true), + currentUser: this.currentUser.getId(), currentSyncState: this.currentSyncState, lastSync: this.lastSync, lastRemoteLogin: this.lastRemoteLogin, diff --git a/src/app/core/ui/search/search.component.ts b/src/app/core/ui/search/search.component.ts index 66c9e4f50d..010f1a3f31 100644 --- a/src/app/core/ui/search/search.component.ts +++ b/src/app/core/ui/search/search.component.ts @@ -103,7 +103,7 @@ export class SearchComponent { async clickOption(optionElement) { await this.router.navigate([ optionElement.value.getConstructor().route, - optionElement.value.getId(), + optionElement.value.getId(true), ]); this.formControl.setValue(""); } diff --git a/src/app/core/user/user.spec.ts b/src/app/core/user/user.spec.ts index 8752f8efc2..a408dbaae4 100644 --- a/src/app/core/user/user.spec.ts +++ b/src/app/core/user/user.spec.ts @@ -31,7 +31,7 @@ describe("User", () => { user.name = "test-name"; expect(user.name).toBe("test-name"); - expect(user.getId()).toBe("test-name"); + expect(user.getId()).toBe(`${User.ENTITY_TYPE}:test-name`); expect(() => (user.name = "another-name")).toThrowError(); }); diff --git a/src/app/features/file/couchdb-file.service.spec.ts b/src/app/features/file/couchdb-file.service.spec.ts index 09fa1f4c43..ec4d8c477b 100644 --- a/src/app/features/file/couchdb-file.service.spec.ts +++ b/src/app/features/file/couchdb-file.service.spec.ts @@ -199,10 +199,10 @@ describe("CouchdbFileService", () => { tick(); expect(mockHttp.get).toHaveBeenCalledWith( - jasmine.stringContaining(entity.getId(true)), + jasmine.stringContaining(entity.getId()), ); expect(mockHttp.delete).toHaveBeenCalledWith( - jasmine.stringContaining(`/${entity.getId(true)}?rev=someRev`), + jasmine.stringContaining(`/${entity.getId()}?rev=someRev`), ); })); diff --git a/src/app/features/file/couchdb-file.service.ts b/src/app/features/file/couchdb-file.service.ts index 80cb6d1ab1..6e73518d9c 100644 --- a/src/app/features/file/couchdb-file.service.ts +++ b/src/app/features/file/couchdb-file.service.ts @@ -61,7 +61,7 @@ export class CouchdbFileService extends FileService { this.runFileUpload(file, entity, property), ); this.reportProgress($localize`Uploading "${file.name}"`, obs); - this.cache[`${entity.getId(true)}/${property}`] = obs.pipe( + this.cache[`${entity.getId()}/${property}`] = obs.pipe( last(), map(() => URL.createObjectURL(file)), shareReplay(), @@ -70,7 +70,7 @@ export class CouchdbFileService extends FileService { } private runFileUpload(file: File, entity: Entity, property: string) { - const attachmentPath = `${this.attachmentsUrl}/${entity.getId(true)}`; + const attachmentPath = `${this.attachmentsUrl}/${entity.getId()}`; return this.getAttachmentsDocument(attachmentPath).pipe( concatMap(({ _rev }) => this.http.put(`${attachmentPath}/${property}?rev=${_rev}`, file, { @@ -104,9 +104,9 @@ export class CouchdbFileService extends FileService { } private runFileRemoval(entity: Entity, property: string) { - const path = `${entity.getId(true)}/${property}`; + const path = `${entity.getId()}/${property}`; return this.http - .get<{ _rev: string }>(`${this.attachmentsUrl}/${entity.getId(true)}`) + .get<{ _rev: string }>(`${this.attachmentsUrl}/${entity.getId()}`) .pipe( concatMap(({ _rev }) => this.http.delete(`${this.attachmentsUrl}/${path}?rev=${_rev}`), @@ -127,7 +127,7 @@ export class CouchdbFileService extends FileService { } private runAllFilesRemoval(entity: Entity) { - const attachmentPath = `${this.attachmentsUrl}/${entity.getId(true)}`; + const attachmentPath = `${this.attachmentsUrl}/${entity.getId()}`; return this.http .get<{ _rev: string }>(attachmentPath) .pipe( @@ -139,7 +139,7 @@ export class CouchdbFileService extends FileService { showFile(entity: Entity, property: string) { const obs = this.http - .get(`${this.attachmentsUrl}/${entity.getId(true)}/${property}`, { + .get(`${this.attachmentsUrl}/${entity.getId()}/${property}`, { responseType: "blob", reportProgress: true, observe: "events", @@ -160,7 +160,7 @@ export class CouchdbFileService extends FileService { } loadFile(entity: Entity, property: string): Observable { - const path = `${entity.getId(true)}/${property}`; + const path = `${entity.getId()}/${property}`; if (!this.cache[path]) { this.cache[path] = this.http .get(`${this.attachmentsUrl}/${path}`, { diff --git a/src/app/features/file/mock-file.service.ts b/src/app/features/file/mock-file.service.ts index 8e95c39020..bae210f5a7 100644 --- a/src/app/features/file/mock-file.service.ts +++ b/src/app/features/file/mock-file.service.ts @@ -28,7 +28,7 @@ export class MockFileService extends FileService { } removeFile(entity: Entity, property: string): Observable { - this.fileMap.delete(`${entity.getId(true)}:${property}`); + this.fileMap.delete(`${entity.getId()}:${property}`); return of({ ok: true }); } @@ -37,18 +37,18 @@ export class MockFileService extends FileService { } showFile(entity: Entity, property: string): void { - const url = this.fileMap.get(`${entity.getId(true)}:${property}`); + const url = this.fileMap.get(`${entity.getId()}:${property}`); window.open(url, "_blank"); } loadFile(entity: Entity, property: string): Observable { - const url = this.fileMap.get(`${entity.getId(true)}:${property}`); + const url = this.fileMap.get(`${entity.getId()}:${property}`); return of(this.sanitizer.bypassSecurityTrustUrl(url)); } uploadFile(file: File, entity: Entity, property: string): Observable { const fileURL = URL.createObjectURL(file); - this.fileMap.set(`${entity.getId(true)}:${property}`, fileURL); + this.fileMap.set(`${entity.getId()}:${property}`, fileURL); return of({ ok: true }); } } diff --git a/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts b/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts index 3be050af4e..36ab39963e 100644 --- a/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts +++ b/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts @@ -216,8 +216,8 @@ describe("MatchingEntitiesComponent", () => { expect(saveSpy).toHaveBeenCalledWith( jasmine.objectContaining({ - schoolId: testEntity.getId(false), - childId: matchedEntity.getId(false), + schoolId: testEntity.getId(), + childId: matchedEntity.getId(), name: "ChildSchoolRelation " + testEntity.toString() + " - matched child", } as Partial), @@ -250,8 +250,8 @@ describe("MatchingEntitiesComponent", () => { expect(saveSpy).toHaveBeenCalledWith( jasmine.objectContaining({ - schools: [testEntity.getId(false)], - children: [child1.getId(false), child2.getId(false)], + schools: [testEntity.getId()], + children: [child1.getId(), child2.getId()], name: "Note " + testEntity.toString() + diff --git a/src/app/features/matching-entities/matching-entities/matching-entities.component.ts b/src/app/features/matching-entities/matching-entities/matching-entities.component.ts index 81d030b54d..eb77601a8b 100644 --- a/src/app/features/matching-entities/matching-entities/matching-entities.component.ts +++ b/src/app/features/matching-entities/matching-entities/matching-entities.component.ts @@ -281,12 +281,12 @@ export class MatchingEntitiesComponent implements OnInit { newMatchEntity[this.onMatch.newEntityMatchPropertyLeft] = this .sideDetails[0].multiSelect - ? leftMatch.map((e) => e.getId(false)) - : leftMatch[0].getId(false); + ? leftMatch.map((e) => e.getId()) + : leftMatch[0].getId(); newMatchEntity[this.onMatch.newEntityMatchPropertyRight] = this .sideDetails[1].multiSelect - ? rightMatch.map((e) => e.getId(false)) - : rightMatch[0].getId(false); + ? rightMatch.map((e) => e.getId()) + : rightMatch[0].getId(); // best guess properties (if they do not exist on the specific entity, the values will be discarded during save newMatchEntity["date"] = new Date(); diff --git a/src/app/features/public-form/public-form.component.spec.ts b/src/app/features/public-form/public-form.component.spec.ts index dcd11fb592..9f7996a71f 100644 --- a/src/app/features/public-form/public-form.component.spec.ts +++ b/src/app/features/public-form/public-form.component.spec.ts @@ -35,7 +35,9 @@ describe("PublicFormComponent", () => { { provide: ActivatedRoute, useValue: { - snapshot: { paramMap: new Map([["id", testFormConfig.getId()]]) }, + snapshot: { + paramMap: new Map([["id", testFormConfig.getId(true)]]), + }, }, }, ], diff --git a/src/app/features/reporting/demo-report-config-generator.service.ts b/src/app/features/reporting/demo-report-config-generator.service.ts index 4d525d851c..c3be07c144 100644 --- a/src/app/features/reporting/demo-report-config-generator.service.ts +++ b/src/app/features/reporting/demo-report-config-generator.service.ts @@ -47,7 +47,7 @@ const demoReports: Partial[] = [ query: `[*privateSchool!=true]`, }, { - query: `[*privateSchool!=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId:addPrefix(${Child.ENTITY_TYPE}):unique:toEntities`, + query: `[*privateSchool!=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(${Child.ENTITY_TYPE})`, label: $localize`:Label for report query:Children attending a governmental school`, groupBy: ["gender"], }, @@ -56,7 +56,7 @@ const demoReports: Partial[] = [ query: `[*privateSchool=true]`, }, { - query: `[*privateSchool=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId:addPrefix(${Child.ENTITY_TYPE}):unique:toEntities`, + query: `[*privateSchool=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(${Child.ENTITY_TYPE})`, label: $localize`:Label for report query:Children attending a private school`, groupBy: ["gender"], }, @@ -73,7 +73,7 @@ const demoReports: Partial[] = [ label: $localize`:Label for a report query:Events`, aggregations: [ { - query: `:getParticipantsWithAttendance(PRESENT):unique:addPrefix(${Child.ENTITY_TYPE}):toEntities`, + query: `:getParticipantsWithAttendance(PRESENT):unique:toEntities(${Child.ENTITY_TYPE})`, groupBy: ["gender"], label: $localize`:Label for a report query:Participants`, }, diff --git a/src/app/features/reporting/reporting/reporting.component.ts b/src/app/features/reporting/reporting/reporting.component.ts index a7060e19e6..e446047cba 100644 --- a/src/app/features/reporting/reporting/reporting.component.ts +++ b/src/app/features/reporting/reporting/reporting.component.ts @@ -45,7 +45,7 @@ export class ReportingComponent { private entityMapper: EntityMapperService, ) { this.entityMapper.loadType(ReportEntity).then((res) => { - this.reports = res.sort((a, b) => a.title.localeCompare(b.title)); + this.reports = res.sort((a, b) => a.title?.localeCompare(b.title)); }); } diff --git a/src/app/features/reporting/sql-report/sql-report.service.spec.ts b/src/app/features/reporting/sql-report/sql-report.service.spec.ts index dc1b041154..3be198d2b5 100644 --- a/src/app/features/reporting/sql-report/sql-report.service.spec.ts +++ b/src/app/features/reporting/sql-report/sql-report.service.spec.ts @@ -53,7 +53,7 @@ describe("SqlReportService", () => { ); expect(mockHttpClient.post).toHaveBeenCalledWith( - `${SqlReportService.QUERY_PROXY}/report/app/${report.getId(true)}`, + `${SqlReportService.QUERY_PROXY}/report/app/${report.getId()}`, { from: "2023-01-01", to: "2024-01-01", diff --git a/src/app/features/reporting/sql-report/sql-report.service.ts b/src/app/features/reporting/sql-report/sql-report.service.ts index 18d8b9040c..255ce40c38 100644 --- a/src/app/features/reporting/sql-report/sql-report.service.ts +++ b/src/app/features/reporting/sql-report/sql-report.service.ts @@ -35,7 +35,7 @@ export class SqlReportService { await this.updateSchemaIfNecessary(); return firstValueFrom( this.http.post( - `${SqlReportService.QUERY_PROXY}/report/app/${report.getId(true)}`, + `${SqlReportService.QUERY_PROXY}/report/app/${report.getId()}`, { from: moment(from).format("YYYY-MM-DD"), to: moment(to).format("YYYY-MM-DD"), diff --git a/src/app/features/todos/model/demo-todo-generator.service.ts b/src/app/features/todos/model/demo-todo-generator.service.ts index e8a2c704dd..da3afa8a27 100644 --- a/src/app/features/todos/model/demo-todo-generator.service.ts +++ b/src/app/features/todos/model/demo-todo-generator.service.ts @@ -75,7 +75,7 @@ export class DemoTodoGeneratorService extends DemoDataGenerator { { probability: 0.5 }, ); - todo.relatedEntities = [entity.getId(true)]; + todo.relatedEntities = [entity.getId()]; todo.assignedTo = [ faker.helpers.arrayElement(this.demoUsers.entities).getId(), diff --git a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.html b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.html index 874d62e7a6..c834ad6a38 100644 --- a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.html +++ b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.html @@ -4,6 +4,6 @@ completed
- by {{ value.completedBy }} on {{ value.completedAt | date }} + by {{ completedBy?.toString() }} on {{ value.completedAt | date }}
diff --git a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts index 7d4ed9f47b..1b175e8a87 100644 --- a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts +++ b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts @@ -1,6 +1,10 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { DisplayTodoCompletionComponent } from "./display-todo-completion.component"; +import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; +import { mockEntityMapper } from "../../../../core/entity/entity-mapper/mock-entity-mapper-service"; +import { User } from "../../../../core/user/user"; +import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; describe("DisplayTodoCompletionComponent", () => { let component: DisplayTodoCompletionComponent; @@ -8,11 +12,21 @@ describe("DisplayTodoCompletionComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [DisplayTodoCompletionComponent], + imports: [DisplayTodoCompletionComponent, FontAwesomeTestingModule], + providers: [ + { + provide: EntityMapperService, + useValue: mockEntityMapper([new User("test")]), + }, + ], }).compileComponents(); fixture = TestBed.createComponent(DisplayTodoCompletionComponent); component = fixture.componentInstance; + component.value = { + completedBy: `${User.ENTITY_TYPE}:test`, + completedAt: new Date(), + }; fixture.detectChanges(); }); diff --git a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.ts b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.ts index 85d61f1294..b88f662b54 100644 --- a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.ts +++ b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.ts @@ -1,9 +1,12 @@ -import { Component } from "@angular/core"; -import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; -import { ViewDirective } from "../../../../core/entity/default-datatype/view.directive"; +import { Component, OnInit } from "@angular/core"; import { TodoCompletion } from "../../model/todo-completion"; import { DatePipe, NgIf } from "@angular/common"; import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; +import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; +import { Entity } from "../../../../core/entity/model/entity"; +import { User } from "../../../../core/user/user"; +import { ViewDirective } from "../../../../core/entity/default-datatype/view.directive"; +import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; @DynamicComponent("DisplayTodoCompletion") @Component({ @@ -13,4 +16,25 @@ import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; standalone: true, imports: [NgIf, FontAwesomeModule, DatePipe], }) -export class DisplayTodoCompletionComponent extends ViewDirective {} +export class DisplayTodoCompletionComponent + extends ViewDirective + implements OnInit +{ + completedBy: Entity; + + constructor(private entityMapper: EntityMapperService) { + super(); + } + + ngOnInit() { + if (this.value?.completedBy) { + const entityId = this.value.completedBy; + const entityType = entityId.includes(":") + ? Entity.extractTypeFromId(entityId) + : User; + this.entityMapper + .load(entityType, entityId) + .then((res) => (this.completedBy = res)); + } + } +} diff --git a/src/app/features/todos/todo-details/todo-details.component.spec.ts b/src/app/features/todos/todo-details/todo-details.component.spec.ts index 42c90bec3f..9d54d9974e 100644 --- a/src/app/features/todos/todo-details/todo-details.component.spec.ts +++ b/src/app/features/todos/todo-details/todo-details.component.spec.ts @@ -64,7 +64,7 @@ describe("TodoDetailsComponent", () => { const savedEntity = await TestBed.inject( EntityMapperService, - ).load(Todo, component.entity.getId(true)); + ).load(Todo, component.entity.getId()); expect(savedEntity.subject).toBe("123"); expect(savedEntity.completed).toBeTruthy(); }); diff --git a/src/app/features/todos/todo.service.ts b/src/app/features/todos/todo.service.ts index 2752bb0fb9..599432d83c 100644 --- a/src/app/features/todos/todo.service.ts +++ b/src/app/features/todos/todo.service.ts @@ -21,7 +21,7 @@ export class TodoService { todo.completed = { completedBy: this.currentUser.value.getId(), completedAt: new Date(), - nextRepetition: nextTodo?.getId(true), + nextRepetition: nextTodo?.getId(), }; await this.entityMapper.save(todo); diff --git a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts index cc585d69d2..d4e58385f8 100644 --- a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts +++ b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts @@ -35,7 +35,7 @@ export class TodosRelatedToEntityComponent extends RelatedEntitiesComponent custom filter component or some kind of variable interpolation? @@ -66,7 +66,7 @@ export class TodosRelatedToEntityComponent extends RelatedEntitiesComponent { @@ -84,7 +84,7 @@ export class TodosRelatedToEntityComponent extends RelatedEntitiesComponent Todo { return () => { const newEntry = new Todo(); - newEntry.relatedEntities = [this.entity.getId(true)]; + newEntry.relatedEntities = [this.entity.getId()]; return newEntry; }; } diff --git a/src/app/utils/test-utils/custom-matchers.d.ts b/src/app/utils/test-utils/custom-matchers.d.ts index 7ecd3b8cf5..a07d520068 100644 --- a/src/app/utils/test-utils/custom-matchers.d.ts +++ b/src/app/utils/test-utils/custom-matchers.d.ts @@ -27,13 +27,6 @@ declare namespace jasmine { */ toHaveKey(key: any): void; - /** - * expects an entity to have the given ID - * The ID is computed via `Entity#getId()` - * @param id - */ - toHaveId(id: string): void; - /** * expects an entity to have a given type * The type of entity is equal to the static `ENTITY_TYPE`. diff --git a/src/app/utils/test-utils/entity-matchers.spec.ts b/src/app/utils/test-utils/entity-matchers.spec.ts index 19b837320f..57fda230a5 100644 --- a/src/app/utils/test-utils/entity-matchers.spec.ts +++ b/src/app/utils/test-utils/entity-matchers.spec.ts @@ -2,19 +2,6 @@ import { Entity } from "../../core/entity/model/entity"; import { makeCustomMatcher } from "./custom-matcher-utils"; const entityMatchers: jasmine.CustomMatcherFactories = { - toHaveId: (util) => { - return makeCustomMatcher( - (entity: Entity, id: string) => entity.getId() === id, - (entity, id) => - `Expected entity ${util.pp( - entity, - )} to have ID '${id}' but it has ID ${entity.getId()}`, - (entity, id) => - `Expected entity ${util.pp( - entity, - )} not to have ID '${id}' but it actually has ID ${entity.getId()}`, - ); - }, toHaveType: (util) => { return makeCustomMatcher( (entity: Entity, type: string) => entity.getType() === type, @@ -25,7 +12,9 @@ const entityMatchers: jasmine.CustomMatcherFactories = { (entity, type) => `Expected entity ${util.pp( entity, - )} not to have type '${type}' but it actually has type ${entity.getId()}`, + )} not to have type '${type}' but it actually has type ${entity.getId( + true, + )}`, ); }, };