Commit 714bce44 authored by David Hardy's avatar David Hardy

Reworked 2 functions to async

parent 409bb8ee
import {Command} from './command';
import {from, OperatorFunction} from 'rxjs';
import {switchMap, tap} from 'rxjs/operators';
import {OperatorFunction} from 'rxjs';
import {Logger} from '../environment/logger';
import {DatabaseService} from '../environment/database.service';
import {ImageData} from "../../.mono/model/image-data.model";
......@@ -8,23 +7,21 @@ import {StorageService} from "../environment/storage.service";
export class ImageDataDeletedCommand implements Command {
public static PATH = 'images/{imageId}';
constructor(private storageService: StorageService, private databaseService: DatabaseService, private log: Logger) {
}
constructor(private imageData: ImageData, private storageService: StorageService, private databaseService: DatabaseService, private log: Logger) {
}
async execute(imageData: ImageData): Promise<void> {
this.log.debug(`ImageDataDeletedCommand: Deleting Votes`);
await this.databaseService.deleteVoting(imageData.id);
pipeline: OperatorFunction<any, any>[] = [
tap(() => this.log.debug(`ImageDataDeletedCommand: Begin`)),
this.log.debug(`ImageDataDeletedCommand: Deleting:${imageData.imgRef}`);
await this.storageService.deleteFile(imageData.imgRef);
tap(() => this.log.debug(`ImageDataDeletedCommand: Votes`)),
switchMap(() => this.databaseService.deleteVoting(this.imageData.id)),
this.log.debug(`ImageDataDeletedCommand: Deleting:${imageData.thumbRef}`);
await this.storageService.deleteFile(imageData.thumbRef);
tap(() => this.log.debug(`ImageDataDeletedCommand: Deleting:${this.imageData.imgRef}`)),
switchMap(() => from(this.storageService.deleteFile(this.imageData.imgRef))),
this.log.debug(`ImageDataDeletedCommand: Done`);
}
tap(() => this.log.debug(`ImageDataDeletedCommand: Deleting:${this.imageData.thumbRef}`)),
switchMap(() => from(this.storageService.deleteFile(this.imageData.thumbRef))),
tap(() => this.log.debug(`ImageDataDeletedCommand: Done`))
];
pipeline: OperatorFunction<any, any>[] = [];
}
import {Command} from './command';
import {OperatorFunction} from 'rxjs';
import {map, switchMap, tap} from 'rxjs/operators';
import {Logger} from '../environment/logger';
import {DatabaseService} from '../environment/database.service';
import {VoteContainer} from "../../.mono/model/vote.model";
......@@ -8,36 +7,37 @@ import {ImageData} from "../../.mono/model/image-data.model";
export class VoteCastedCommand implements Command {
public static PATH = 'images/{imageId}/votes/{voteId}';
constructor(private params: VoteCastedCommandData,
private voteBefore: VoteContainer,
private voteAfter: VoteContainer,
private databaseService: DatabaseService,
constructor(private databaseService: DatabaseService,
private log: Logger) {
}
pipeline: OperatorFunction<any, any>[] = [
tap(() => this.log.debug(`VoteCastedCommand: Begin, params=${JSON.stringify(this.params)}, voteBefore=${JSON.stringify(this.voteBefore)}, voteAfter=${JSON.stringify(this.voteAfter)}`)),
async execute(params: VoteCastedCommandData,
voteBefore: VoteContainer,
voteAfter: VoteContainer): Promise<void> {
this.log.debug(`VoteCastedCommand: Begin, params=${JSON.stringify(params)}, voteBefore=${JSON.stringify(voteBefore)}, voteAfter=${JSON.stringify(voteAfter)}`);
this.log.debug(`VoteCastedCommand: databaseService getImageVoteMetaData ${params.imageId}`);
const imageData = await this.databaseService.getImageData(params.imageId);
tap(() => this.log.debug(`VoteCastedCommand: databaseService getImageVoteMetaData$1 ${this.params.imageId}`)),
switchMap(() => this.databaseService.getImageData$1(this.params.imageId)),
this.log.debug(`VoteCastedCommand: updateVoteAggregate old score: ${imageData.score}`);
const voteAggregate = this.updateVoteAggregate(imageData, voteBefore, voteAfter);
tap(imageData => this.log.debug(`VoteCastedCommand: updateMetaData`)),
map(imageData => this.updateVoteAggregate(imageData)),
this.log.debug(`VoteCastedCommand: databaseService setImageVoteMetaData ${params.imageId} ${JSON.stringify(voteAggregate)}`);
await this.databaseService.mergeImageData(params.imageId, {
totalVotes: voteAggregate.newTotal,
score: voteAggregate.newScore
});
tap(votes => this.log.debug(`VoteCastedCommand: databaseService setImageVoteMetaData ${this.params.imageId} ${JSON.stringify(votes)}`)),
switchMap(([totalVotes, score]) => this.databaseService.mergeImageData(this.params.imageId, {
totalVotes,
score
})),
this.log.debug(`VoteCastedCommand: Done`);
}
tap(() => this.log.debug(`VoteCastedCommand: Done`))
];
pipeline: OperatorFunction<any, any>[] = [];
private updateVoteAggregate(imageData: ImageData): [number, number] {
const before = this.voteBefore ? this.voteBefore.vote : 'BLANK';
const after = this.voteAfter ? this.voteAfter.vote : 'BLANK';
private updateVoteAggregate(imageData: ImageData,
voteBefore: VoteContainer,
voteAfter: VoteContainer): { newTotal: number, newScore: number } {
const before = voteBefore ? voteBefore.vote : 'BLANK';
const after = voteAfter ? voteAfter.vote : 'BLANK';
let scoreMod = 0;
......@@ -66,7 +66,7 @@ export class VoteCastedCommand implements Command {
const newTotal = oldTotal + totalMod;
const newScore = oldScore + scoreMod;
return [newTotal, newScore];
return {newTotal, newScore};
}
}
......
import * as admin from 'firebase-admin';
import {from, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {ImageData} from "../../.mono/model/image-data.model";
export class DatabaseService {
getImageData$1(imageId: string): Observable<ImageData> {
return from(admin.firestore().doc(`images/${imageId}`).get()).pipe(map(s => s.data()));
async getImageData(imageId: string): Promise<ImageData> {
const snapshot = await admin.firestore().doc(`images/${imageId}`).get();
return snapshot.data();
}
mergeImageData(imageId: string, data: ImageData): Observable<any> {
return from(admin.firestore().doc(`images/${imageId}`).set(data, {merge: true}));
async mergeImageData(imageId: string, data: ImageData): Promise<any> {
return admin.firestore().doc(`images/${imageId}`).set(data, {merge: true});
}
deleteVoting(imageId: string): Observable<any> {
async deleteVoting(imageId: string) {
const ref = admin.firestore().collection(`images/${imageId}/meta`);
const batchSize = 100;
const query = ref.limit(batchSize);
return from(new Promise((resolve, reject) =>
this.deleteQueryBatch(admin.firestore(), query, batchSize, resolve, reject)
));
return this.deleteQueryBatch(admin.firestore(), query);
}
private deleteQueryBatch(db: admin.firestore.Firestore, query, batchSize, resolve, reject): void {
query.get()
.then((snapshot) => {
// When there are no documents left, we are done
if (snapshot.size === 0) {
return 0;
}
// Delete documents in a batch
const batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
return batch.commit().then(() => {
return snapshot.size;
});
}).then((numDeleted) => {
if (numDeleted === 0) {
resolve();
return;
}
// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
this.deleteQueryBatch(db, query, batchSize, resolve, reject);
});
})
.catch(reject);
private async deleteQueryBatch(db: admin.firestore.Firestore, query): Promise<any> {
const snapshot = await query.get();
if (snapshot.size === 0) {
return;
}
const batch = db.batch();
await Promise.all[snapshot.docs.map((doc) => {
batch.delete(doc.ref);
})];
const {numDeleted} = await batch.commit().then(() => {
return snapshot.size;
});
if (numDeleted === 0) {
return;
}
return await this.deleteQueryBatch(db, query);
}
}
......@@ -3,25 +3,14 @@ import {VoteCastedCommand, VoteCastedCommandData} from "../../commands/vote-cast
import {VoteContainer} from "../../../.mono/model/vote.model";
import {DatabaseService} from "../../environment/database.service";
import {Logger} from "../../environment/logger";
import {Command} from "../../commands/command";
import {of} from "rxjs";
import {take} from "rxjs/operators";
function executeCommand(command: Command): Promise<any> {
return (of(1) as any).pipe(
...command.pipeline,
take(1)
).toPromise();
}
export const firestoreImageVoteWriteTrigger = functions
.region('europe-west1')
.firestore.document(VoteCastedCommand.PATH).onWrite((snapshot, context) => {
return executeCommand(new VoteCastedCommand(
context.params as VoteCastedCommandData,
snapshot.before.data() as VoteContainer,
snapshot.after.data() as VoteContainer,
new DatabaseService(),
new Logger()
));
});
.firestore.document('images/{imageId}/votes/{voteId}').onWrite(async (snapshot, context) => {
const databaseService = new DatabaseService();
const logger = new Logger();
const command = new VoteCastedCommand(databaseService, logger);
await command.execute(context.params as VoteCastedCommandData, snapshot.before.data() as VoteContainer, snapshot.after.data() as VoteContainer);
});
......@@ -3,19 +3,16 @@ import {ImageDataDeletedCommand} from "../../commands/image-data-deleted.command
import {StorageService} from "../../environment/storage.service";
import {DatabaseService} from "../../environment/database.service";
import {Logger} from "../../environment/logger";
import {Command} from "../../commands/command";
import {of} from "rxjs";
import {take} from "rxjs/operators";
function executeCommand(command: Command): Promise<any> {
return (of(1) as any).pipe(
...command.pipeline,
take(1)
).toPromise();
}
export const firestoreImageDeletedTrigger = functions
.region('europe-west1')
.firestore.document(ImageDataDeletedCommand.PATH).onDelete((snapshot) => {
return executeCommand(new ImageDataDeletedCommand(snapshot.data(), new StorageService(), new DatabaseService(), new Logger()));
.firestore.document('images/{imageId}').onDelete(async snapshot => {
const storageService = new StorageService();
const databaseService = new DatabaseService();
const logger = new Logger();
const command = new ImageDataDeletedCommand(storageService, databaseService, logger);
await command.execute(snapshot.data());
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment