Commit eb8b8745 authored by David Hardy's avatar David Hardy

Removed rx from functions

parent 714bce44
......@@ -3,11 +3,11 @@
"scripts": {
"lint": "./node_modules/.bin/tslint -p tslint.json",
"mono:model": "copy -S ../model/src -D .mono/model -E .spec. -H --verbose 2",
"mono:quick": "yarn --ignore-engines mono:model -Q",
"mono:full": "rimraf --ignore-engines .mono && yarn --ignore-engines mono:model",
"mono:quick": "yarn mono:model -Q",
"mono:full": "rimraf .mono && yarn mono:model",
"compile": "./node_modules/.bin/tsc",
"build": "yarn --ignore-engines lint && yarn --ignore-engines compile",
"build:prod": "yarn --ignore-engines build",
"build": "yarn lint && yarn compile",
"build:prod": "yarn build",
"serve": "yarn build && firebase serve --only functions",
"shell": "yarn build && firebase experimental:functions:shell",
"start": "yarn shell",
......@@ -21,11 +21,10 @@
},
"main": "lib/src/index.js",
"dependencies": {
"firebase-admin": "^8.2.0",
"firebase-admin": "^8.3.0",
"cors": "^2.8.5",
"firebase-functions": "^3.1.0",
"jimp": "0.6.4",
"rxjs": "6.5.2"
"firebase-functions": "^3.2.0",
"jimp": "0.6.4"
},
"devDependencies": {
"@endran/copy": "^0.3.2",
......
import {OperatorFunction} from 'rxjs';
export interface Command {
pipeline: OperatorFunction<any, any>[];
execute(...args: any[]): Promise<any>;
}
import {Command} from './command';
import * as admin from "firebase-admin";
export class EchoCommand implements Command {
async execute(id: number): Promise<string> {
const snapshot = await admin.firestore().doc(`test/test${id}`).get();
const count = (snapshot.data() || {count: 0}).count + 1;
await admin.firestore().doc(`test/test${id}`).set({count});
return `{"message": "Hello from Firebase!", "count":${count}, "id":${id}}`;
}
}
import {Command} from './command';
import {OperatorFunction} from 'rxjs';
import {Logger} from '../environment/logger';
import {DatabaseService} from '../environment/database.service';
import {ImageData} from "../../.mono/model/image-data.model";
......@@ -22,6 +21,4 @@ export class ImageDataDeletedCommand implements Command {
this.log.debug(`ImageDataDeletedCommand: Done`);
}
pipeline: OperatorFunction<any, any>[] = [];
}
import {from, MonoTypeOperatorFunction, OperatorFunction} from 'rxjs';
import {ObjectMetadata} from 'firebase-functions/lib/providers/storage';
import {map, switchMap, tap} from 'rxjs/operators';
import {Command} from './command';
import {PlatformEnvironment} from '../environment/platform-environment';
import {DatabaseService} from '../environment/database.service';
......@@ -9,131 +6,66 @@ import {StorageService} from "../environment/storage.service";
export class ImageUploadedCommand implements Command {
private imageId: string;
private sourceRemoteRef: string;
private thumbRemoteRef: string;
private sourceFilePath: string;
private thumbFilePath: string;
static CAN_EXECUTE(object: ObjectMetadata): boolean {
return object.name.split('/')[0] === 'images' && object.name.indexOf('thumb_') < 0;
}
constructor(private object: ObjectMetadata,
private storageService: StorageService,
private databaseService: DatabaseService,
private env: PlatformEnvironment = new PlatformEnvironment()) {
}
get pipeline(): OperatorFunction<any, any>[] {
const imgName = this.env.path.basename(this.object.name);
this.imageId = imgName.split('.')[0];
this.sourceRemoteRef = this.object.name;
this.thumbRemoteRef = this.object.name.replace(imgName, `thumb_${imgName}`);
this.sourceFilePath = this.env.path.join(this.env.os.tmpdir(), imgName);
this.thumbFilePath = this.env.path.join(this.env.os.tmpdir(), `thumb_${imgName}`);
return [
this.logStart(),
this.writeImageRefToFirestore(),
this.downloadImageToFile(),
this.loadFileWithJimp(),
this.createThumbnail(),
this.uploadThumbnailToBucket(),
this.writeThumbRefToFirestore(),
this.freeUpLocalFiles(),
this.getSharableLinkFromGCloudForThumb(),
this.writeThumbLinkToFirestore(),
this.getSharableLinkFromGCloudForOriginalImage(),
this.writeOriginalLinkToFirestore(),
this.logDone()
];
}
private logStart() {
return tap(() => this.env.log.info(`ImageUploadedCommand: start`));
}
private downloadImageToFile() {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: downloadImageToFile`);
return from(this.storageService.downloadFile(this.sourceRemoteRef, this.sourceFilePath));
});
}
private loadFileWithJimp(): OperatorFunction<any, Jimp> {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: loadFileWithJimp`);
return from(Jimp.read(this.sourceFilePath));
});
}
private createThumbnail(): OperatorFunction<Jimp, Jimp> {
return map((jimp: Jimp) => {
this.env.log.debug(`ImageUploadedCommand: createThumbnail`);
return jimp.cover(192, 192, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
.write(this.thumbFilePath);
});
}
private uploadThumbnailToBucket() {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: uploadThumbnailToBucket`);
return from(this.storageService.uploadFile(this.thumbFilePath, this.thumbRemoteRef, {contentType: 'application/pdf'}));
});
};
private freeUpLocalFiles() {
return tap(() => {
this.env.log.debug(`ImageUploadedCommand: freeUpLocalFiles`);
this.env.fs.unlinkSync(this.sourceFilePath);
this.env.fs.unlinkSync(this.thumbFilePath);
});
};
private getSharableLinkFromGCloudForThumb(): OperatorFunction<any, string> {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: getSharableLinkFromGCloudForThumb`);
return from(this.storageService.getPublicUrlToFile(this.thumbRemoteRef));
});
};
private writeThumbLinkToFirestore(): OperatorFunction<any, any> {
return switchMap(url => {
this.env.log.debug(`ImageUploadedCommand: writeThumbLinkToFirestore`);
return from(this.databaseService.mergeImageData(this.imageId, {thumbUrl: url}));
});
};
private getSharableLinkFromGCloudForOriginalImage(): OperatorFunction<any, string> {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: getSharableLinkFromGCloudForOriginalImage`);
return from(this.storageService.getPublicUrlToFile(this.sourceRemoteRef));
});
};
private writeOriginalLinkToFirestore(): OperatorFunction<any, any> {
return switchMap(url => {
this.env.log.debug(`ImageUploadedCommand: writeOriginalLinkToFirestore`);
return from(this.databaseService.mergeImageData(this.imageId, {imgUrl: url}));
});
};
private writeImageRefToFirestore(): OperatorFunction<any, any> {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: writeImageRefToFirestore`);
return from(this.databaseService.mergeImageData(this.imageId, {imgRef: this.sourceRemoteRef}));
});
}
private writeThumbRefToFirestore(): OperatorFunction<any, any> {
return switchMap(() => {
this.env.log.debug(`ImageUploadedCommand: writeThumbRefToFirestore`);
return from(this.databaseService.mergeImageData(this.imageId, {thumbRef: this.thumbRemoteRef}));
});
}
private logDone(): MonoTypeOperatorFunction<any> {
return tap(() => this.env.log.info(`ImageUploadedCommand: done`));
}
constructor(private storageService: StorageService,
private databaseService: DatabaseService,
private env: PlatformEnvironment = new PlatformEnvironment()) {
}
async execute(sourceCloudPath: string): Promise<void> {
this.env.log.info(`ImageUploadedCommand: start`);
if(sourceCloudPath.split('/')[0] !== 'images' || sourceCloudPath.indexOf('thumb_') >= 0) {
this.env.log.debug(`ImageUploadedCommand: no action required for ${sourceCloudPath}`);
return;
}
const imgName = this.env.path.basename(sourceCloudPath);
const imageId = imgName.split('.')[0];
const thumbCloudPath = sourceCloudPath.replace(imgName, `thumb_${imgName}`);
const sourceLocalPath = this.env.path.join(this.env.os.tmpdir(), imgName);
const thumbLocalPath = this.env.path.join(this.env.os.tmpdir(), `thumb_${imgName}`);
this.env.log.debug(`ImageUploadedCommand: imgName=${imgName}`);
this.env.log.debug(`ImageUploadedCommand: imageId=${imageId}`);
this.env.log.debug(`ImageUploadedCommand: thumbCloudPath=${thumbCloudPath}`);
this.env.log.debug(`ImageUploadedCommand: sourceLocalPath=${sourceLocalPath}`);
this.env.log.debug(`ImageUploadedCommand: thumbLocalPath=${thumbLocalPath}`);
this.env.log.debug(`ImageUploadedCommand: writeImageRefToFirestore`);
await this.databaseService.mergeImageData(imageId, {imgRef: sourceCloudPath});
this.env.log.debug(`ImageUploadedCommand: downloadImageToFile`);
await this.storageService.downloadFile(sourceCloudPath, sourceLocalPath);
this.env.log.debug(`ImageUploadedCommand: loadFileWithJimp`);
const jimp = await Jimp.read(sourceLocalPath);
this.env.log.debug(`ImageUploadedCommand: createThumbnail`);
jimp.cover(192, 192, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
.write(thumbLocalPath);
this.env.log.debug(`ImageUploadedCommand: uploadThumbnailToBucket`);
await this.storageService.uploadFile(thumbLocalPath, thumbCloudPath, {contentType: 'image/jpeg'});
this.env.log.debug(`ImageUploadedCommand: writeThumbRefToFirestore`);
await this.databaseService.mergeImageData(imageId, {thumbRef: thumbCloudPath});
this.env.log.debug(`ImageUploadedCommand: freeUpLocalFiles`);
this.env.fs.unlinkSync(sourceLocalPath);
this.env.fs.unlinkSync(thumbLocalPath);
this.env.log.debug(`ImageUploadedCommand: getSharableLinkFromGCloudForThumb`);
const thumbUrl = await this.storageService.getPublicUrlToFile(thumbCloudPath);
this.env.log.debug(`ImageUploadedCommand: writeThumbLinkToFirestore:${thumbUrl}`);
await this.databaseService.mergeImageData(imageId, {thumbUrl});
this.env.log.debug(`ImageUploadedCommand: getSharableLinkFromGCloudForOriginalImage`);
const sourceUrl = await this.storageService.getPublicUrlToFile(sourceCloudPath);
this.env.log.debug(`ImageUploadedCommand: writeOriginalLinkToFirestore:${sourceUrl}`);
await this.databaseService.mergeImageData(imageId, {imgUrl: sourceUrl});
this.env.log.info(`ImageUploadedCommand: done`);
}
}
import {Command} from './command';
import {OperatorFunction} from 'rxjs';
import {Logger} from '../environment/logger';
import {DatabaseService} from '../environment/database.service';
import {VoteContainer} from "../../.mono/model/vote.model";
......@@ -31,8 +30,6 @@ export class VoteCastedCommand implements Command {
this.log.debug(`VoteCastedCommand: Done`);
}
pipeline: OperatorFunction<any, any>[] = [];
private updateVoteAggregate(imageData: ImageData,
voteBefore: VoteContainer,
voteAfter: VoteContainer): { newTotal: number, newScore: number } {
......
import * as functions from "firebase-functions";
import {from} from "rxjs";
import * as admin from "firebase-admin";
import {EchoCommand} from "../commands/echo.command";
const cors = require('cors')({origin: true});
export const httpEchoTrigger = functions
.region('europe-west1')
.https.onRequest((request, response) => {
const id = request.params['id'] || 0;
from(admin.firestore().doc(`test/test${id}`).get())
.subscribe(doc => {
const count = (doc.data() || {count: 0}).count + 1;
admin.firestore().doc(`test/test${id}`).set({count});
cors(request, response, () => {
response.status(200).send(`{"message": "Hello from Firebase!", "count":${count}, "id":${id}}`)
})
});
.https.onRequest(async (request, response) => {
const message = await new EchoCommand().execute(request.params['id'] || 0);
cors(request, response, () => {
response.status(200).send(message)
})
});
......@@ -3,25 +3,15 @@ import {ImageUploadedCommand} from "../commands/image-uploaded.command";
import {StorageService} from "../environment/storage.service";
import {DatabaseService} from "../environment/database.service";
import {PlatformEnvironment} from "../environment/platform-environment";
import {EMPTY, of} from "rxjs";
import {Command} from "../commands/command";
import {take} from "rxjs/operators";
function executeCommand(command: Command): Promise<any> {
return (of(1) as any).pipe(
...command.pipeline,
take(1)
).toPromise();
}
export const storageFinalizeTrigger = functions
.region('europe-west1')
.runWith({timeoutSeconds: 540, memory: '2GB'})
.storage.object().onFinalize((object, context) => {
if (ImageUploadedCommand.CAN_EXECUTE(object)) {
return executeCommand(new ImageUploadedCommand(object, new StorageService(), new DatabaseService(), new PlatformEnvironment()));
} else {
console.log(`No command for object ${object.name}, with eventType ${context.eventType}`);
return EMPTY.toPromise();
}
.storage.object().onFinalize((object) => {
const storageService = new StorageService();
const databaseService = new DatabaseService();
const platformEnvironment = new PlatformEnvironment();
const command = new ImageUploadedCommand(storageService, databaseService, platformEnvironment);
return command.execute(object.name);
});
This diff is collapsed.
......@@ -8,23 +8,23 @@
"clean": "npm run clean:model && npm run clean:app && npm run clean:functions && rimraf ./node_modules",
"install:model": "yarn --cwd ./model install",
"install:app": "yarn --cwd ./app install",
"install:functions": "yarn --ignore-engines --cwd ./functions install",
"install:functions": "yarn --cwd ./functions install",
"postinstall": "yarn install:model && yarn install:app && yarn install:functions",
"lint:model": "yarn --cwd ./model lint",
"lint:app": "yarn --cwd ./app lint",
"lint:functions": "yarn --ignore-engines --cwd ./functions lint",
"lint:functions": "yarn --cwd ./functions lint",
"lint": "yarn lint:model && yarn lint:app && yarn lint:functions",
"test:model": "yarn --cwd ./model test",
"test:app": "yarn --cwd ./app test",
"test:functions": "yarn --ignore-engines --cwd ./functions test",
"test:functions": "yarn --cwd ./functions test",
"test": "yarn test:model && yarn test:app && yarn test:functions",
"build:model": "yarn --cwd ./model build",
"build:app": "yarn --cwd ./app build",
"build:functions": "yarn --ignore-engines --cwd ./functions build",
"build:functions": "yarn --cwd ./functions build",
"build": "yarn build:model && yarn build:app && yarn build:functions",
"build:model:prod": "yarn --cwd ./model build:prod",
"build:app:prod": "yarn --cwd ./app build:prod",
"build:functions:prod": "yarn --ignore-engines --cwd ./functions build:prod",
"build:functions:prod": "yarn --cwd ./functions build:prod",
"build:prod": "yarn build:model:prod && yarn build:app:prod && yarn build:functions:prod",
"deploy": "firebase deploy"
},
......
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