optimize song usage

This commit is contained in:
2026-03-15 13:19:20 +01:00
parent d907c89eb6
commit ab535d48b9
21 changed files with 312 additions and 29 deletions

View File

@@ -30,7 +30,7 @@ export class DbCollection<T> {
) {}
public add(data: Partial<T>): Promise<DocumentReference<T>> {
return addDoc(this.ref as CollectionReference<T>, data as WithFieldValue<T>);
return runInInjectionContext(this.environmentInjector, () => addDoc(this.ref as CollectionReference<T>, data as WithFieldValue<T>));
}
public valueChanges(options?: {idField?: string}): Observable<T[]> {
@@ -38,7 +38,7 @@ export class DbCollection<T> {
}
private get ref(): CollectionReference<DocumentData> {
return collection(this.fs, this.path);
return runInInjectionContext(this.environmentInjector, () => collection(this.fs, this.path));
}
}
@@ -50,15 +50,15 @@ export class DbDocument<T> {
) {}
public set(data: Partial<T>): Promise<void> {
return setDoc(this.ref as DocumentReference<T>, data as WithFieldValue<T>);
return runInInjectionContext(this.environmentInjector, () => setDoc(this.ref as DocumentReference<T>, data as WithFieldValue<T>));
}
public update(data: Partial<T>): Promise<void> {
return updateDoc(this.ref, data as Partial<DocumentData>);
return runInInjectionContext(this.environmentInjector, () => updateDoc(this.ref, data as Partial<DocumentData>));
}
public delete(): Promise<void> {
return deleteDoc(this.ref);
return runInInjectionContext(this.environmentInjector, () => deleteDoc(this.ref));
}
public collection<U>(subPath: string): DbCollection<U> {
@@ -73,7 +73,7 @@ export class DbDocument<T> {
}
private get ref(): DocumentReference<DocumentData> {
return doc(this.fs, this.path);
return runInInjectionContext(this.environmentInjector, () => doc(this.fs, this.path));
}
}
@@ -103,7 +103,9 @@ export class DbService {
return this.col(ref).valueChanges({idField: 'id'});
}
const q = query(collection(this.fs, ref), ...queryConstraints);
return runInInjectionContext(this.environmentInjector, () => collectionData(q, {idField: 'id'}) as Observable<T[]>);
return runInInjectionContext(this.environmentInjector, () => {
const q = query(collection(this.fs, ref), ...queryConstraints);
return collectionData(q, {idField: 'id'}) as Observable<T[]>;
});
}
}

View File

@@ -25,8 +25,8 @@ describe('UserSongUsageService', () => {
sessionSpy.update$.and.resolveTo();
showDataServiceSpy.listRaw$.and.returnValue(
of([
{id: 'show-1', owner: 'user-1'},
{id: 'show-2', owner: 'user-2'},
{id: 'show-1', owner: 'user-1', archived: false},
{id: 'show-2', owner: 'user-2', archived: true},
] as never)
);
showSongDataServiceSpy.list$.and.callFake((showId: string) =>
@@ -66,11 +66,11 @@ describe('UserSongUsageService', () => {
await expectAsync(service.rebuildSongUsage()).toBeResolvedTo({
usersProcessed: 2,
showsProcessed: 2,
showSongsProcessed: 4,
showSongsProcessed: 3,
});
expect(sessionSpy.update$).toHaveBeenCalledWith('user-1', {songUsage: {'song-1': 2, 'song-2': 1}});
expect(sessionSpy.update$).toHaveBeenCalledWith('user-2', {songUsage: {'song-3': 1}});
expect(sessionSpy.update$).toHaveBeenCalledWith('user-2', {songUsage: {}});
});
it('should reject song usage rebuilds for non-admin users', async () => {

View File

@@ -40,6 +40,10 @@ export class UserSongUsageService {
let showSongsProcessed = 0;
for (const show of shows) {
if (show.archived) {
continue;
}
const ownerId = show.owner;
if (!ownerId) {
continue;

View File

@@ -3,11 +3,13 @@ import {of} from 'rxjs';
import {UserService} from './user.service';
import {UserSessionService} from './user-session.service';
import {UserSongUsageService} from './user-song-usage.service';
import {ShowSongIndexService} from '../../modules/shows/services/show-song-index.service';
describe('UserService', () => {
let service: UserService;
let sessionSpy: jasmine.SpyObj<UserSessionService>;
let songUsageSpy: jasmine.SpyObj<UserSongUsageService>;
let showSongIndexSpy: jasmine.SpyObj<ShowSongIndexService>;
beforeEach(async () => {
sessionSpy = jasmine.createSpyObj<UserSessionService>(
@@ -20,6 +22,7 @@ describe('UserService', () => {
}
);
songUsageSpy = jasmine.createSpyObj<UserSongUsageService>('UserSongUsageService', ['incSongCount', 'decSongCount', 'rebuildSongUsage']);
showSongIndexSpy = jasmine.createSpyObj<ShowSongIndexService>('ShowSongIndexService', ['rebuildShowSongIds']);
sessionSpy.currentUser.and.resolveTo({id: 'user-1'} as never);
sessionSpy.getUserbyId.and.resolveTo({id: 'user-2'} as never);
@@ -34,11 +37,13 @@ describe('UserService', () => {
songUsageSpy.incSongCount.and.resolveTo();
songUsageSpy.decSongCount.and.resolveTo();
songUsageSpy.rebuildSongUsage.and.resolveTo({usersProcessed: 1, showsProcessed: 2, showSongsProcessed: 3});
showSongIndexSpy.rebuildShowSongIds.and.resolveTo({showsProcessed: 2, showSongsProcessed: 3});
await TestBed.configureTestingModule({
providers: [
{provide: UserSessionService, useValue: sessionSpy},
{provide: UserSongUsageService, useValue: songUsageSpy},
{provide: ShowSongIndexService, useValue: showSongIndexSpy},
],
});
@@ -100,9 +105,11 @@ describe('UserService', () => {
await service.incSongCount('song-1');
await service.decSongCount('song-2');
await expectAsync(service.rebuildSongUsage()).toBeResolvedTo({usersProcessed: 1, showsProcessed: 2, showSongsProcessed: 3});
await expectAsync(service.rebuildShowSongIds()).toBeResolvedTo({showsProcessed: 2, showSongsProcessed: 3});
expect(songUsageSpy.incSongCount).toHaveBeenCalledWith('song-1');
expect(songUsageSpy.decSongCount).toHaveBeenCalledWith('song-2');
expect(songUsageSpy.rebuildSongUsage).toHaveBeenCalled();
expect(showSongIndexSpy.rebuildShowSongIds).toHaveBeenCalled();
});
});

View File

@@ -3,6 +3,7 @@ import {Observable} from 'rxjs';
import {User} from './user';
import {SongUsageMigrationResult, UserSongUsageService} from './user-song-usage.service';
import {UserSessionService} from './user-session.service';
import {MigrationProgress, ShowSongIndexMigrationResult, ShowSongIndexService} from '../../modules/shows/services/show-song-index.service';
@Injectable({
providedIn: 'root',
@@ -10,6 +11,7 @@ import {UserSessionService} from './user-session.service';
export class UserService {
private session = inject(UserSessionService);
private songUsage = inject(UserSongUsageService);
private showSongIndex = inject(ShowSongIndexService);
public users$ = this.session.users$;
@@ -34,4 +36,6 @@ export class UserService {
public incSongCount = (songId: string): Promise<void | null> => this.songUsage.incSongCount(songId);
public decSongCount = (songId: string): Promise<void | null> => this.songUsage.decSongCount(songId);
public rebuildSongUsage = (): Promise<SongUsageMigrationResult> => this.songUsage.rebuildSongUsage();
public rebuildShowSongIds = (onProgress?: (progress: MigrationProgress) => void): Promise<ShowSongIndexMigrationResult> =>
this.showSongIndex.rebuildShowSongIds(onProgress);
}