Skip to content

Commit

Permalink
Release 3.27.0
Browse files Browse the repository at this point in the history
  • Loading branch information
TheSlimvReal authored Nov 29, 2023
2 parents 4eba5fe + eea30ff commit 0265a95
Show file tree
Hide file tree
Showing 108 changed files with 2,305 additions and 4,564 deletions.
671 changes: 343 additions & 328 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"flag-icons": "^7.0.2",
"hammerjs": "^2.0.8",
"json-query": "^2.2.2",
"keycloak-angular": "^15.0.0",
"keycloak-js": "^22.0.5",
"leaflet": "^1.9.4",
"lodash-es": "^4.17.21",
Expand Down
13 changes: 8 additions & 5 deletions src/app/app-initializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { ConfigService } from "./core/config/config.service";
import { RouterService } from "./core/config/dynamic-routing/router.service";
import { EntityConfigService } from "./core/entity/entity-config.service";
import { Router } from "@angular/router";
import { SessionService } from "./core/session/session-service/session.service";
import { AnalyticsService } from "./core/analytics/analytics.service";
import { LoginState } from "./core/session/session-states/login-state.enum";
import { LoggingService } from "./core/logging/logging.service";
import { environment } from "../environments/environment";
import { LoginStateSubject } from "./core/session/session-type";
import { CurrentUserSubject } from "./core/user/user";

export const appInitializers = {
provide: APP_INITIALIZER,
Expand All @@ -22,8 +23,9 @@ export const appInitializers = {
routerService: RouterService,
entityConfigService: EntityConfigService,
router: Router,
sessionService: SessionService,
currentUser: CurrentUserSubject,
analyticsService: AnalyticsService,
loginState: LoginStateSubject,
) =>
async () => {
// Re-trigger services that depend on the config when something changes
Expand All @@ -35,9 +37,9 @@ export const appInitializers = {
});

// update the user context for remote error logging and tracking and load config initially
sessionService.loginState.subscribe((newState) => {
loginState.subscribe((newState) => {
if (newState === LoginState.LOGGED_IN) {
const username = sessionService.getCurrentUser().name;
const username = currentUser.value.name;
LoggingService.setLoggingContextUser(username);
analyticsService.setUser(username);
} else {
Expand All @@ -62,8 +64,9 @@ export const appInitializers = {
RouterService,
EntityConfigService,
Router,
SessionService,
CurrentUserSubject,
AnalyticsService,
LoginStateSubject,
],
multi: true,
};
15 changes: 10 additions & 5 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ import {
entityRegistry,
EntityRegistry,
} from "./core/entity/database-entity.decorator";
import { LOCATION_TOKEN, WINDOW_TOKEN } from "./utils/di-tokens";
import {
LOCATION_TOKEN,
NAVIGATOR_TOKEN,
WINDOW_TOKEN,
} from "./utils/di-tokens";
import { AttendanceModule } from "./child-dev-project/attendance/attendance.module";
import { NotesModule } from "./child-dev-project/notes/notes.module";
import { SchoolsModule } from "./child-dev-project/schools/schools.module";
Expand All @@ -76,7 +80,6 @@ import { RouterModule } from "@angular/router";
import { TodosModule } from "./features/todos/todos.module";
import moment from "moment";
import { getLocaleFirstDayOfWeek } from "@angular/common";
import { SessionService } from "./core/session/session-service/session.service";
import { waitForChangeTo } from "./core/session/session-states/session-utils";
import { LoginState } from "./core/session/session-states/login-state.enum";
import { appInitializers } from "./app-initializers";
Expand All @@ -87,6 +90,7 @@ import { BirthdayDashboardWidgetModule } from "./features/dashboard-widgets/birt
import { ConfigSetupModule } from "./features/config-setup/config-setup.module";
import { MarkdownPageModule } from "./features/markdown-page/markdown-page.module";
import { AdminModule } from "./features/admin/admin.module";
import { LoginStateSubject } from "./core/session/session-type";

/**
* Main entry point of the application.
Expand Down Expand Up @@ -147,6 +151,7 @@ import { AdminModule } from "./features/admin/admin.module";
{ provide: EntityRegistry, useValue: entityRegistry },
{ provide: WINDOW_TOKEN, useValue: window },
{ provide: LOCATION_TOKEN, useValue: window.location },
{ provide: NAVIGATOR_TOKEN, useValue: navigator },
{
provide: LOCALE_ID,
useValue:
Expand All @@ -161,12 +166,12 @@ import { AdminModule } from "./features/admin/admin.module";
},
{
provide: SwRegistrationOptions,
useFactory: (session: SessionService) => ({
useFactory: (loginState: LoginStateSubject) => ({
enabled: environment.production,
registrationStrategy: () =>
session.loginState.pipe(waitForChangeTo(LoginState.LOGGED_IN)),
loginState.pipe(waitForChangeTo(LoginState.LOGGED_IN)),
}),
deps: [SessionService],
deps: [LoginStateSubject],
},
appInitializers,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { AttendanceService } from "../../attendance.service";
import { Note } from "../../../notes/model/note";
import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service";
import { RecurringActivity } from "../../model/recurring-activity";
import { SessionService } from "../../../../core/session/session-service/session.service";
import { NoteDetailsComponent } from "../../../notes/note-details/note-details.component";
import { FormDialogService } from "../../../../core/form-dialog/form-dialog.service";
import { AlertService } from "../../../../core/alerts/alert.service";
Expand All @@ -27,6 +26,7 @@ import { NgForOf, NgIf } from "@angular/common";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { ActivityCardComponent } from "../../activity-card/activity-card.component";
import { MatButtonModule } from "@angular/material/button";
import { CurrentUserSubject } from "../../../../core/user/user";

@Component({
selector: "app-roll-call-setup",
Expand Down Expand Up @@ -76,7 +76,7 @@ export class RollCallSetupComponent implements OnInit {
constructor(
private entityMapper: EntityMapperService,
private attendanceService: AttendanceService,
private sessionService: SessionService,
private currentUser: CurrentUserSubject,
private formDialog: FormDialogService,
private alertService: AlertService,
private filerService: FilterService,
Expand Down Expand Up @@ -105,7 +105,7 @@ export class RollCallSetupComponent implements OnInit {
this.visibleActivities = this.allActivities;
} else {
this.visibleActivities = this.allActivities.filter((a) =>
a.isAssignedTo(this.sessionService.getCurrentUser().name),
a.isAssignedTo(this.currentUser.value.name),
);
if (this.visibleActivities.length === 0) {
this.visibleActivities = this.allActivities.filter(
Expand Down Expand Up @@ -155,7 +155,7 @@ export class RollCallSetupComponent implements OnInit {
activity,
this.date,
)) as NoteForActivitySetup;
event.authors = [this.sessionService.getCurrentUser().name];
event.authors = [this.currentUser.value.name];
event.isNewFromActivity = true;
return event;
}
Expand All @@ -175,7 +175,7 @@ export class RollCallSetupComponent implements OnInit {
score += 1;
}

if (assignedUsers.includes(this.sessionService.getCurrentUser().name)) {
if (assignedUsers.includes(this.currentUser.value.name)) {
score += 2;
}

Expand All @@ -189,7 +189,7 @@ export class RollCallSetupComponent implements OnInit {

createOneTimeEvent() {
const newNote = Note.create(new Date());
newNote.authors = [this.sessionService.getCurrentUser().name];
newNote.authors = [this.currentUser.value.name];

this.formDialog
.openFormPopup(newNote, [], NoteDetailsComponent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { UnsavedChangesService } from "../../entity-details/form/unsaved-changes
import { ActivationStart, Router } from "@angular/router";
import { Subscription } from "rxjs";
import { filter } from "rxjs/operators";
import { SessionService } from "../../session/session-service/session.service";
import {
EntitySchemaField,
PLACEHOLDERS,
} from "../../entity/schema/entity-schema-field";
import { isArrayDataType } from "../../basic-datatypes/datatype-utils";
import { CurrentUserSubject } from "../../user/user";

/**
* These are utility types that allow to define the type of `FormGroup` the way it is returned by `EntityFormService.create`
Expand All @@ -40,7 +40,7 @@ export class EntityFormService {
private dynamicValidator: DynamicValidatorsService,
private ability: EntityAbility,
private unsavedChanges: UnsavedChangesService,
private session: SessionService,
private currentUser: CurrentUserSubject,
router: Router,
) {
router.events
Expand Down Expand Up @@ -154,7 +154,7 @@ export class EntityFormService {
newVal = new Date();
break;
case PLACEHOLDERS.CURRENT_USER:
newVal = this.session.getCurrentUser().name;
newVal = this.currentUser.value.name;
break;
default:
newVal = schema.defaultValue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import {
Component,
ViewChild,
Input,
OnChanges,
SimpleChanges,
OnInit,
SimpleChanges,
ViewChild,
} from "@angular/core";
import {
MatPaginator,
MatPaginatorModule,
PageEvent,
} from "@angular/material/paginator";
import { MatTableDataSource } from "@angular/material/table";
import { User } from "../../../user/user";
import { SessionService } from "../../../session/session-service/session.service";
import { CurrentUserSubject, User } from "../../../user/user";
import { EntityMapperService } from "../../../entity/entity-mapper/entity-mapper.service";

@Component({
Expand All @@ -35,7 +34,7 @@ export class ListPaginatorComponent<E> implements OnChanges, OnInit {
pageSize = 10;

constructor(
private sessionService: SessionService,
private currentUser: CurrentUserSubject,
private entityMapperService: EntityMapperService,
) {}

Expand Down Expand Up @@ -83,7 +82,7 @@ export class ListPaginatorComponent<E> implements OnChanges, OnInit {

private async ensureUserIsLoaded(): Promise<boolean> {
if (!this.user) {
const currentUser = this.sessionService.getCurrentUser();
const currentUser = this.currentUser.value;
this.user = await this.entityMapperService
.load(User, currentUser.name)
.catch(() => undefined);
Expand Down
3 changes: 2 additions & 1 deletion src/app/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NgModule } from "@angular/core";
import { ComponentRegistry } from "../dynamic-components";
import { coreComponents } from "./core-components";
import { User } from "./user/user";
import { CurrentUserSubject, User } from "./user/user";
import { Config } from "./config/config";
import { StringDatatype } from "./basic-datatypes/string/string.datatype";
import { DefaultDatatype } from "./entity/default-datatype/default.datatype";
Expand All @@ -25,6 +25,7 @@ import { CommonModule } from "@angular/common";
*/
@NgModule({
providers: [
CurrentUserSubject,
// base dataTypes
{ provide: DefaultDatatype, useClass: StringDatatype, multi: true },
{ provide: DefaultDatatype, useClass: BooleanDatatype, multi: true },
Expand Down
116 changes: 116 additions & 0 deletions src/app/core/database/sync.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { fakeAsync, TestBed, tick } from "@angular/core/testing";

import { SyncService } from "./sync.service";
import { PouchDatabase } from "./pouch-database";
import { Database } from "./database";
import { LoginStateSubject, SyncStateSubject } from "../session/session-type";
import { LoginState } from "../session/session-states/login-state.enum";
import { KeycloakAuthService } from "../session/auth/keycloak/keycloak-auth.service";
import { HttpStatusCode } from "@angular/common/http";
import PouchDB from "pouchdb-browser";

describe("SyncService", () => {
let service: SyncService;
let loginState: LoginStateSubject;
let mockAuthService: jasmine.SpyObj<KeycloakAuthService>;

beforeEach(() => {
mockAuthService = jasmine.createSpyObj(["login", "addAuthHeader"]);
TestBed.configureTestingModule({
providers: [
{ provide: KeycloakAuthService, useValue: mockAuthService },
{ provide: Database, useClass: PouchDatabase },
LoginStateSubject,
SyncStateSubject,
],
});
service = TestBed.inject(SyncService);
loginState = TestBed.inject(LoginStateSubject);
});

it("should be created", () => {
expect(service).toBeTruthy();
});

it("should restart the sync if it fails at one point", fakeAsync(() => {
let errorCallback, pauseCallback;
const syncHandle = {
on: (action, callback) => {
if (action === "error") {
errorCallback = callback;
}
if (action === "paused") {
pauseCallback = callback;
}
return syncHandle;
},
cancel: () => undefined,
};
const syncSpy = jasmine
.createSpy()
.and.returnValues(Promise.resolve("first"), syncHandle, syncHandle);
spyOn(
TestBed.inject(Database) as PouchDatabase,
"getPouchDB",
).and.returnValue({ sync: syncSpy } as any);

service.startSync();
tick(1000);

// error + logged in -> sync should restart
loginState.next(LoginState.LOGGED_IN);
syncSpy.calls.reset();
errorCallback();
expect(syncSpy).toHaveBeenCalled();

// pause -> no restart required
syncSpy.calls.reset();
pauseCallback();
expect(syncSpy).not.toHaveBeenCalled();

// logout + error -> no restart
syncSpy.calls.reset();
loginState.next(LoginState.LOGGED_OUT);
tick();
errorCallback();
expect(syncSpy).not.toHaveBeenCalled();
}));

it("should try auto-login if fetch fails and fetch again", async () => {
// Make sync call pass
spyOn(
TestBed.inject(Database) as PouchDatabase,
"getPouchDB",
).and.returnValues({ sync: () => Promise.resolve() } as any);
spyOn(PouchDB, "fetch").and.returnValues(
Promise.resolve({
status: HttpStatusCode.Unauthorized,
ok: false,
} as Response),
Promise.resolve({ status: HttpStatusCode.Ok, ok: true } as Response),
);
// providing "valid" token on second call
let calls = 0;
mockAuthService.addAuthHeader.and.callFake((headers) => {
headers.Authorization = calls++ === 1 ? "valid" : "invalid";
});
mockAuthService.login.and.resolveTo();
const initSpy = spyOn(service["remoteDatabase"], "initRemoteDB");
await service.startSync();
// taking fetch function from init call
const fetch = initSpy.calls.mostRecent().args[1];

const url = "/db/_changes";
const opts = { headers: {} };
await expectAsync(fetch(url, opts)).toBeResolved();

expect(PouchDB.fetch).toHaveBeenCalledTimes(2);
expect(PouchDB.fetch).toHaveBeenCalledWith(url, opts);
expect(opts.headers).toEqual({ Authorization: "valid" });
expect(mockAuthService.login).toHaveBeenCalled();
expect(mockAuthService.addAuthHeader).toHaveBeenCalledTimes(2);

// prevent live sync call
service["cancelLiveSync"]();
});
});
Loading

0 comments on commit 0265a95

Please sign in to comment.