Skip to content

Commit

Permalink
Implement ability to generate relation observables for generic relations
Browse files Browse the repository at this point in the history
  • Loading branch information
luisa-beerboom committed Nov 9, 2023
1 parent fe480dd commit 3b9f388
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { auditTime, combineLatest } from 'rxjs';
import { auditTime, combineLatest, filter, iif, map, NEVER, startWith, switchMap } from 'rxjs';
import { Permission } from 'src/app/domain/definitions/permission';
import { PollData } from 'src/app/domain/models/poll/generic-poll';
import {
Expand All @@ -9,13 +9,12 @@ import {
PollTableData,
VotingResult
} from 'src/app/domain/models/poll/poll-constants';
import { MeetingUserRepositoryService } from 'src/app/gateways/repositories/meeting_user';
import { ChartData } from 'src/app/site/pages/meetings/modules/poll/components/chart/chart.component';
import { PollService } from 'src/app/site/pages/meetings/modules/poll/services/poll.service';
import { OperatorService } from 'src/app/site/services/operator.service';
import { ThemeService } from 'src/app/site/services/theme.service';
import { UserControllerService } from 'src/app/site/services/user-controller.service';

import { ViewPoll } from '../../../../../polls';
import { ViewAssignment } from '../../../../view-models';
import { AssignmentPollService } from '../../services/assignment-poll.service';

Expand Down Expand Up @@ -132,20 +131,31 @@ export class AssignmentPollDetailContentComponent implements OnInit {
private pollService: AssignmentPollService,
private cd: ChangeDetectorRef,
private operator: OperatorService,
private themeService: ThemeService,
private userController: UserControllerService,
private meetingUserRepo: MeetingUserRepositoryService
private themeService: ThemeService
) {}

public ngOnInit(): void {
combineLatest([
this.poll.options_as_observable,
this.themeService.currentGeneralColorsSubject,
this.userController.getViewModelListObservable(),
this.meetingUserRepo.getViewModelListObservable()
])
.pipe(auditTime(1))
.subscribe(() => this.setupTableData());
iif(
() => this.poll instanceof ViewPoll,
(this.poll as ViewPoll).options_as_observable.pipe(
map(options =>
options.filter(option => !!option.content_object_id && !!option.content_object_as_observable)
),
filter(options => !!options.length),
switchMap(options =>
combineLatest(
options.map(option =>
option.content_object_as_observable.pipe(filter(content_object => !!content_object))
)
).pipe(auditTime(1))
)
),
NEVER
).pipe(startWith(null)),
this.themeService.currentGeneralColorsSubject
]).subscribe(() => this.setupTableData());
}

private setupTableData(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { marker as _ } from '@colsen1991/ngx-translate-extract-marker';
import { Observable } from 'rxjs';

import { OptionData, OptionTitle } from '../../../../../../domain/models/poll/generic-poll';
import { Option } from '../../../../../../domain/models/poll/option';
Expand Down Expand Up @@ -59,6 +60,7 @@ export class ViewOption<C extends BaseViewModel = any> extends BaseViewModel<Opt

interface IViewOptionRelations<C extends BaseViewModel = any> {
content_object?: C;
content_object_as_observable?: Observable<C>;
votes: ViewVote[];
poll: ViewPoll;
}
Expand Down
65 changes: 60 additions & 5 deletions client/src/app/site/services/relation-manager.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import { filter, map, Observable } from 'rxjs';
import { filter, map, merge, Observable } from 'rxjs';

import { Fqid } from '../../domain/definitions/key-types';
import { BaseModel } from '../../domain/models/base/base-model';
import { Relation, RELATIONS } from '../../infrastructure/definitions/relations';
import { collectionIdFromFqid } from '../../infrastructure/utils/transform-functions';
import { collectionIdFromFqid, idFromFqid } from '../../infrastructure/utils/transform-functions';
import { BaseViewModel } from '../base/base-view-model';
import { CollectionMapperService } from './collection-mapper.service';
import { ViewModelStoreService } from './view-model-store.service';
Expand Down Expand Up @@ -70,13 +70,18 @@ export class RelationManagerService {
}

public getObservableForRelation<M extends BaseModel>(model: M, relation: Relation<M>): Observable<any> {
if (relation.generic) {
return this.getObservableForGenericRelation(model, relation);
} else {
return this.getObservableForNormalRelation(model, relation);
}
}

private getObservableForNormalRelation<M extends BaseModel>(model: M, relation: Relation<M>): Observable<any> {
const foreignRepo = this.collectionMapper.getRepository(relation.foreignViewModel as any);
if (!foreignRepo) {
throw new Error(`Could not find foreignRepo for ${relation}`);
}
if (relation.generic) {
throw new Error(`Generic relations are not yet implemented for detection of changing relations.`);
}
return foreignRepo.getViewModelMapObservable().pipe(
map(modelMap => {
if (!model[relation.ownIdField as keyof BaseModel]) {
Expand All @@ -98,6 +103,56 @@ export class RelationManagerService {
);
}

private getObservableForGenericRelation<M extends BaseModel>(model: M, relation: Relation<M>): Observable<any> {
if (!relation.foreignViewModelPossibilities) {
throw new Error(
`Generic relations without foreignViewModelPossibilities are not implemented for detection of changing relations.`
);
}
const foreignRepos = relation.foreignViewModelPossibilities.mapToObject(foreignModel => ({
[foreignModel.COLLECTION]: this.collectionMapper.getRepository(foreignModel as any)
}));
if (Object.values(foreignRepos).some(repo => !repo)) {
throw new Error(
`Could not find at least one of the foreignRepos for ${relation}: ${Object.keys(foreignRepos)
.filter(collection => !foreignRepos[collection])
.join(`, `)}`
);
}
const otherModelIds: { [collection: string]: string[] } = {};
if (relation.many) {
(model[relation.ownIdField as keyof BaseModel] as unknown as string[])
.map(data => collectionIdFromFqid(data))
.forEach(([collection, id]) => {
if (!otherModelIds[collection]) {
otherModelIds[collection] = [];
}
otherModelIds[collection].push(id.toString());
});
}
return merge(
...Object.entries(foreignRepos).map(([collection, repo]) =>
repo.getViewModelMapObservable().pipe(
map(modelMap => {
if (!model[relation.ownIdField as keyof BaseModel]) {
return true;
}
if (relation.many) {
const modelIds = Object.keys(modelMap);
const modelIdsFromModelMap = modelIds.map(data => data.toString());
return otherModelIds[collection]?.intersect(modelIdsFromModelMap).length > 0;
} else {
return !!modelMap[idFromFqid(model[relation.ownIdField as keyof BaseModel] as string)];
}
})
)
)
).pipe(
filter(hasChanges => hasChanges),
map(() => this.handleGenericRelation(model, relation))
);
}

private loadRelations(): void {
for (const relation of RELATIONS) {
relation.ownIdField = this.ensureIdField(
Expand Down

0 comments on commit 3b9f388

Please sign in to comment.