Skip to content

Commit

Permalink
feat: enable login for other entities (#2143)
Browse files Browse the repository at this point in the history
closes: #2132 #1685 #1513

Co-authored-by: Sebastian <[email protected]>
  • Loading branch information
TheSlimvReal and sleidig authored Feb 8, 2024
1 parent 2249a88 commit 081a23f
Show file tree
Hide file tree
Showing 37 changed files with 520 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class RollCallSetupComponent implements OnInit {
} else {
// TODO implement a generic function that finds the property where a entity has relations to another entity type (e.g. `authors` for `Note` when looking for `User`) to allow dynamic checks
this.visibleActivities = this.allActivities.filter((a) =>
a.isAssignedTo(this.currentUser.value.getId()),
a.isAssignedTo(this.currentUser.value?.getId()),
);
if (this.visibleActivities.length === 0) {
this.visibleActivities = this.allActivities.filter(
Expand Down Expand Up @@ -156,7 +156,9 @@ export class RollCallSetupComponent implements OnInit {
activity,
this.date,
)) as NoteForActivitySetup;
event.authors = [this.currentUser.value.getId()];
if (this.currentUser.value) {
event.authors = [this.currentUser.value.getId()];
}
event.isNewFromActivity = true;
return event;
}
Expand All @@ -176,7 +178,7 @@ export class RollCallSetupComponent implements OnInit {
score += 1;
}

if (assignedUsers.includes(this.currentUser.value.getId())) {
if (assignedUsers.includes(this.currentUser.value?.getId())) {
score += 2;
}

Expand All @@ -190,7 +192,9 @@ export class RollCallSetupComponent implements OnInit {

createOneTimeEvent() {
const newNote = Note.create(new Date());
newNote.authors = [this.currentUser.value.getId()];
if (this.currentUser.value) {
newNote.authors = [this.currentUser.value.getId()];
}

this.formDialog
.openFormPopup(newNote, [], NoteDetailsComponent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,16 @@ describe("DisplayEntityArrayComponent", () => {

expect(component.entities).toEqual(expectedEntities);
});

it("should load entities of not configured type", async () => {
component.config = School.ENTITY_TYPE;
const existingChild = testEntities.find(
(e) => e.getType() === Child.ENTITY_TYPE,
);
component.value = [existingChild.getId()];

await component.ngOnInit();

expect(component.entities).toEqual([existingChild]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ export class DisplayEntityArrayComponent
const entityIds: string[] = this.value || [];
if (entityIds.length < this.aggregationThreshold) {
const entityPromises = entityIds.map((entityId) => {
const type =
typeof this.config === "string"
? this.config
: Entity.extractTypeFromId(entityId);
const type = entityId.includes(":")
? Entity.extractTypeFromId(entityId)
: this.config;
return this.entityMapper.load(type, entityId);
});
this.entities = await Promise.all(entityPromises);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@ import {
componentRegistry,
ComponentRegistry,
} from "../../../../dynamic-components";
import {
mockEntityMapper,
MockEntityMapperService,
} from "../../../entity/entity-mapper/mock-entity-mapper-service";
import { LoggingService } from "../../../logging/logging.service";

describe("DisplayEntityComponent", () => {
let component: DisplayEntityComponent;
let fixture: ComponentFixture<DisplayEntityComponent>;
let mockEntityMapper: jasmine.SpyObj<EntityMapperService>;
let entityMapper: MockEntityMapperService;
let mockRouter: jasmine.SpyObj<Router>;

beforeEach(async () => {
mockEntityMapper = jasmine.createSpyObj(["load"]);
mockEntityMapper.load.and.resolveTo(new Child());
entityMapper = mockEntityMapper();
mockRouter = jasmine.createSpyObj(["navigate"]);
await TestBed.configureTestingModule({
imports: [DisplayEntityComponent],
providers: [
{ provide: EntityMapperService, useValue: mockEntityMapper },
{ provide: EntityMapperService, useValue: entityMapper },
{ provide: EntityRegistry, useValue: entityRegistry },
{ provide: ComponentRegistry, useValue: componentRegistry },
{ provide: Router, useValue: mockRouter },
Expand All @@ -48,7 +52,7 @@ describe("DisplayEntityComponent", () => {

it("should use the block component when available", async () => {
const school = new School();
mockEntityMapper.load.and.resolveTo(school);
entityMapper.add(school);

component.entity = new ChildSchoolRelation();
component.id = "schoolId";
Expand All @@ -57,10 +61,6 @@ describe("DisplayEntityComponent", () => {
await component.ngOnInit();

expect(component.entityBlockComponent).toEqual(School.getBlockComponent());
expect(mockEntityMapper.load).toHaveBeenCalledWith(
school.getType(),
school.getId(),
);
expect(component.entityToDisplay).toEqual(school);
});

Expand All @@ -71,4 +71,29 @@ describe("DisplayEntityComponent", () => {

expect(mockRouter.navigate).toHaveBeenCalledWith(["/child", "1"]);
});

it("should show entities which are not of the configured type", async () => {
const child = new Child();
entityMapper.add(child);
component.entityId = child.getId();
component.config = School.ENTITY_TYPE;

await component.ngOnInit();

expect(component.entityToDisplay).toEqual(child);
});

it("should log a warning if entity cannot be loaded", async () => {
const warnSpy = spyOn(TestBed.inject(LoggingService), "warn");
const child = new Child("not_existing");
component.entityId = child.getId();
component.config = School.ENTITY_TYPE;

await component.ngOnInit();

expect(warnSpy).toHaveBeenCalledWith(
jasmine.stringContaining(child.getId()),
);
expect(component.entityToDisplay).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { EntityMapperService } from "../../../entity/entity-mapper/entity-mapper
import { Router } from "@angular/router";
import { NgClass, NgIf } from "@angular/common";
import { DynamicComponentDirective } from "../../../config/dynamic-components/dynamic-component.directive";
import { LoggingService } from "../../../logging/logging.service";

@DynamicComponent("DisplayEntity")
@Component({
Expand Down Expand Up @@ -35,21 +36,30 @@ export class DisplayEntityComponent
constructor(
private entityMapper: EntityMapperService,
private router: Router,
private logger: LoggingService,
) {
super();
}

async ngOnInit() {
if (!this.entityToDisplay) {
this.entityType = this.entityType ?? this.config;
this.entityId = this.entityId ?? this.value;
this.entityType = this.entityId.includes(":")
? Entity.extractTypeFromId(this.entityId)
: this.entityType ?? this.config;
if (!this.entityType || !this.entityId) {
return;
}
this.entityToDisplay = await this.entityMapper.load(
this.entityType,
this.entityId,
);
try {
this.entityToDisplay = await this.entityMapper.load(
this.entityType,
this.entityId,
);
} catch (e) {
this.logger.warn(
`[DISPLAY_ENTITY] Could not find entity with ID: ${this.entityId}: ${e}`,
);
}
}
if (this.entityToDisplay) {
this.entityBlockComponent = this.entityToDisplay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import { ChildSchoolRelation } from "../../../../child-dev-project/children/mode
import { School } from "../../../../child-dev-project/schools/model/school";
import { MockedTestingModule } from "../../../../utils/mocked-testing.module";
import { FormControl } from "@angular/forms";
import { Child } from "../../../../child-dev-project/children/model/child";
import { LoggingService } from "../../../logging/logging.service";

describe("EditSingleEntityComponent", () => {
let component: EditSingleEntityComponent;
let fixture: ComponentFixture<EditSingleEntityComponent>;
let loadTypeSpy: jasmine.Spy;
let entityMapper: EntityMapperService;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [EditSingleEntityComponent, MockedTestingModule.withState()],
providers: [EntityFormService],
}).compileComponents();
loadTypeSpy = spyOn(TestBed.inject(EntityMapperService), "loadType");
entityMapper = TestBed.inject(EntityMapperService);
}));

beforeEach(() => {
Expand All @@ -45,11 +47,39 @@ describe("EditSingleEntityComponent", () => {
it("should load all entities of the given type as options", async () => {
const school1 = School.create({ name: "First School" });
const school2 = School.create({ name: "Second School " });
loadTypeSpy.and.resolveTo([school1, school2]);
await entityMapper.saveAll([school1, school2]);
component.formFieldConfig.additional = School.ENTITY_TYPE;

await component.ngOnInit();

expect(loadTypeSpy).toHaveBeenCalled();
expect(component.entities).toEqual([school1, school2]);
expect(component.entities).toEqual(
jasmine.arrayWithExactContents([school1, school2]),
);
});

it("should add selected entity of a not-configured type to available entities", async () => {
const someSchools = [new School(), new School()];
const selectedChild = new Child();
await entityMapper.saveAll(someSchools.concat(selectedChild));
component.formFieldConfig.additional = School.ENTITY_TYPE;
component.formControl.setValue(selectedChild.getId());

await component.ngOnInit();

expect(component.entities).toEqual(
jasmine.arrayWithExactContents(someSchools.concat(selectedChild)),
);
});

it("should log warning if entity is selected that cannot be found", async () => {
const warnSpy = spyOn(TestBed.inject(LoggingService), "warn");
component.formFieldConfig.additional = Child.ENTITY_TYPE;
component.formControl.setValue("missing_child");

await component.ngOnInit();

expect(warnSpy).toHaveBeenCalledWith(
jasmine.stringContaining("missing_child"),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { MatFormFieldModule } from "@angular/material/form-field";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { NgIf } from "@angular/common";
import { ErrorHintComponent } from "../../../common-components/error-hint/error-hint.component";
import { LoggingService } from "../../../logging/logging.service";

@DynamicComponent("EditSingleEntity")
@Component({
Expand All @@ -34,15 +35,30 @@ export class EditSingleEntityComponent
entities: Entity[] = [];
entityToId = (e: Entity) => e?.getId();

constructor(private entityMapperService: EntityMapperService) {
constructor(
private entityMapper: EntityMapperService,
private logger: LoggingService,
) {
super();
}

async ngOnInit() {
super.ngOnInit();
this.entities = await this.entityMapperService.loadType(
this.formFieldConfig.additional,
const availableEntities = await this.entityMapper.loadType(this.additional);
const selected = this.formControl.value;
if (selected && !availableEntities.some((e) => e.getId() === selected)) {
try {
const type = Entity.extractTypeFromId(selected);
const entity = await this.entityMapper.load(type, selected);
availableEntities.push(entity);
} catch (e) {
this.logger.warn(
`[EDIT_SINGLE_ENTITY] Could not find entity with ID: ${selected}: ${e}`,
);
}
}
this.entities = availableEntities.sort((e1, e2) =>
e1.toString().localeCompare(e2.toString()),
);
this.entities.sort((e1, e2) => e1.toString().localeCompare(e2.toString()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ interface SelectableOption<O, V> {
selected: boolean;
}

/** Custom `MatFormFieldControl` for telephone number input. */
@Component({
selector: "app-basic-autocomplete",
templateUrl: "basic-autocomplete.component.html",
Expand Down
Loading

0 comments on commit 081a23f

Please sign in to comment.