cloud-functions/thumbnail/index.js (86 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
'use strict';
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const functions = require('@google-cloud/functions-framework');
const vision = require('@google-cloud/vision');
const {Storage} = require('@google-cloud/storage');
const axios = require('axios');
var fs = require('fs');
functions.cloudEvent('process-thumbnails', async (cloudEvent) => {
console.log(`Event ID: ${cloudEvent.id}`);
console.log(`Event Type: ${cloudEvent.type}`);
const file = cloudEvent.data;
try {
console.log(`Received thumbnail request for file ${file.name} from bucket ${file.bucket}`);
const storage = new Storage();
const bucket = storage.bucket(file.bucket);
const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);
const client = new vision.ImageAnnotatorClient();
const visionRequest = {
image: { source: { imageUri: `gs://${file.bucket}/${file.name}` } },
features: [
{ type: 'LABEL_DETECTION' },
]
};
// We launch the vision call first so we can process the thumbnail while we wait for the response.
const visionPromise = client.annotateImage(visionRequest);
if (!fs.existsSync("/tmp/original")){
fs.mkdirSync("/tmp/original");
}
if (!fs.existsSync("/tmp/thumbnail")){
fs.mkdirSync("/tmp/thumbnail");
}
const originalFile = `/tmp/original/${file.name}`;
const thumbFile = `/tmp/thumbnail/${file.name}`
await bucket.file(file.name).download({
destination: originalFile
});
const originalImageUrl = await bucket.file(file.name).publicUrl()
console.log(`Downloaded picture into ${originalFile}`);
const itemID = parseInt(path.parse(file.name).name);
if (isNaN(itemID)){
return;
}
const resizeCrop = Promise.promisify(imageMagick.crop);
await resizeCrop({
srcPath: originalFile,
dstPath: thumbFile,
width: 400,
height: 400
});
console.log(`Created local thumbnail in ${thumbFile}`);
const thumbnailImage = await thumbBucket.upload(thumbFile);
const thumbnailImageUrl = thumbnailImage[0].publicUrl();
console.log(`Uploaded thumbnail to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}`);
const visionResponse = await visionPromise;
console.log(`Raw vision output for: ${file.name}: ${JSON.stringify(visionResponse[0])}`);
let status = "Failed"
let labels = "";
for (const label of visionResponse[0].labelAnnotations){
status = label.description == "Food" ? "Ready" : status
labels = labels.concat(label.description, ", ");
}
console.log(`\nVision API labels: ${labels}\n`);
console.log(`Menu Item status will be set to: ${status}\n`);
const menuServer = axios.create({
baseURL: process.env.MENU_SERVICE_URL,
headers :{
get: {
"Content-Type": 'application/json'
}
}
})
const item = await menuServer.get(`/menu/${itemID}`);
// Send update call to menu service
const request = await menuServer.put(`/menu/${itemID}`, {
itemImageURL: originalImageUrl,
itemName: item.data.itemName,
itemPrice: item.data.itemPrice,
itemThumbnailURL: thumbnailImageUrl,
spiceLevel: item.data.spiceLevel,
status: status,
tagLine: item.data.tagLine
})
} catch (err) {
console.log(`Error: processing the thumbnail: ${err}`);
}
});