samples-ts/functions/backupSiteContent.ts (70 lines of code) (raw):
import * as df from "durable-functions";
import * as fs from "fs/promises";
import * as readdirp from "readdirp";
import * as path from "path";
import { InvocationContext, output } from "@azure/functions";
import {
OrchestrationHandler,
OrchestrationContext,
ActivityHandler,
Task,
} from "durable-functions";
import { Stats } from "fs";
const getFileListActivityName = "getFileList";
const copyFileToBlobActivityName = "copyFileToBlob";
const backupSiteContentOrchestration: OrchestrationHandler = function* (
context: OrchestrationContext
) {
const rootDir: string = context.df.getInput<string>();
if (!rootDir) {
throw new Error("A directory path is required as an input.");
}
const rootDirAbs: string = path.resolve(rootDir);
const files: string[] = yield context.df.callActivity(getFileListActivityName, rootDirAbs);
// Backup Files and save Tasks into array
const tasks: Task[] = [];
for (const file of files) {
const input = {
backupPath: path.relative(rootDirAbs, file).replace("\\", "/"),
filePath: file,
};
tasks.push(context.df.callActivity(copyFileToBlobActivityName, input));
}
// wait for all the Backup Files Activities to complete, sum total bytes
const results: number[] = yield context.df.Task.all(tasks);
const totalBytes: number = results ? results.reduce((prev, curr) => prev + curr, 0) : 0;
// return results;
return totalBytes;
};
df.app.orchestration("backupSiteContent", backupSiteContentOrchestration);
const getFileListActivity: ActivityHandler = async function (
rootDirectory: string,
context: InvocationContext
): Promise<string[]> {
context.log(`Searching for files under '${rootDirectory}'...`);
const allFilePaths = [];
for await (const entry of readdirp(rootDirectory, { type: "files" })) {
allFilePaths.push(entry.fullPath);
}
context.log(`Found ${allFilePaths.length} under ${rootDirectory}.`);
return allFilePaths;
};
df.app.activity(getFileListActivityName, {
handler: getFileListActivity,
});
const blobOutput = output.storageBlob({
path: "backups/{backupPath}",
connection: "StorageConnString",
});
const copyFileToBlobActivity: ActivityHandler = async function (
{ backupPath, filePath }: { backupPath: string; filePath: string },
context: InvocationContext
): Promise<number> {
const outputLocation = `backups/${backupPath}`;
const stats: Stats = await fs.stat(filePath);
context.log(`Copying '${filePath}' to '${outputLocation}'. Total bytes = ${stats.size}.`);
const fileContents = await fs.readFile(filePath);
context.extraOutputs.set(blobOutput, fileContents);
return stats.size;
};
df.app.activity(copyFileToBlobActivityName, {
extraOutputs: [blobOutput],
handler: copyFileToBlobActivity,
});