antfarm
Version:
File automation framework for ants.
278 lines (225 loc) • 8.45 kB
text/typescript
import {Environment} from "../environment/environment";
import {FileJob} from "./fileJob";
import {Job} from "./job";
import {FolderJob} from "./folderJob";
import {JobProperty} from "./jobProperty";
const tmp = require("tmp"),
fs = require("fs"),
path = require("path"),
JSZip = require("jszip"),
_ = require("lodash"),
Reflect = require("reflect-metadata");
export class PackedJob extends FileJob {
protected e: Environment;
protected _job: Job;
constructor(e: Environment, job: Job) {
// let job_name = job.name;
super(e, job.name);
let pj = this;
pj.e = e;
pj.job = job;
}
protected get job() {
return this._job;
}
protected set job(job: Job) {
this._job = job;
}
/**
* Makes job ticket and returns the _path to the temporary file.
* @param job
* @returns {string}
*/
protected getJobTicket(job: Job) {
let pj = this;
// Make job ticket
let json = job.getJSON();
let tmpobj = tmp.dirSync();
let dir = tmpobj.name;
let file_name = dir + path.sep + "ticket.json";
try {
fs.writeFileSync(file_name, json, "utf8");
} catch (err) {
pj.e.log(3, `Error writing job ticket to temporary file`, pj);
}
return file_name;
}
protected buildZip(zip: any, callback) {
// Save out zip
let pj = this;
let job = this.job;
let tmpobj = tmp.dirSync();
let dir = tmpobj.name;
let file_name = job.name + ".antpack.zip";
let file_path = dir + path.sep + file_name;
zip
.generateNodeStream({type: "nodebuffer", streamFiles: true})
.pipe(fs.createWriteStream(file_path))
.on("finish", function () {
// JSZip generates a readable stream with a "end" event,
// but is piped here in a writable stream which emits a "_finish" event.
pj.path = file_path;
pj.name = file_name;
callback();
});
}
/**
* Packs the related job on construction.
*/
public execPack(done) {
let pj = this;
let job = pj.job;
let ticketPath = pj.getJobTicket(job);
let zip = new JSZip();
// Add ticket to zip
fs.readFile(ticketPath, function(err, data) {
if (err) throw err;
zip.file("_ticket/ticket.json", data);
if (job.isFile()) {
fs.readFile(job.path, (err, data) => {
if (err) throw err;
zip.file("_asset/" + job.name, data);
pj.buildZip(zip, () => {
done();
});
});
} else if (job.isFolder()) {
job.files.forEach(file => {
fs.readFile(file.path, function(err, data) {
if (err) throw err;
zip.file("_asset" + path.sep + job.nameProper + path.sep + file.name, data);
pj.buildZip(zip, () => {
done();
});
});
});
} else {
pj.buildZip(zip, () => {
done();
});
}
});
}
protected restoreJobTicket(jsonTicket) {
let pj = this;
let jobObject, job;
try {
jobObject = JSON.parse(jsonTicket);
if (jobObject._type === "file") {
job = new FileJob(pj.e, jobObject._id);
} else if (jobObject._type === "folder") {
job = new FolderJob(pj.e, jobObject._id);
} else {
pj.e.log(3, `Cannot unpack this type of job: ${jobObject._type}`, pj);
}
} catch (err) {
pj.e.log(3, `Unpack ticket parse error: ${err}.`, pj);
pj.e.log(3, `Unparsable ticket: ${jsonTicket}.`, pj);
}
// Restore property values
let props = {};
_.each(jobObject._properties, prop => {
props[prop._key] = new JobProperty(prop._key, prop._value);
});
job.propertyValues = props;
// Restore lifecycle
job.lifeCycle = jobObject._lifeCycle;
return job;
}
public execUnpack(done) {
// console.log("unpacking");
let pj = this;
let job = pj.job;
// Read the zip to a buffer
fs.readFile(job.path, (err, data) => {
if (err) {
pj.e.log(3, `Unpacking readFile error: ${err}`, pj);
}
// Open the zip in JSZip
JSZip.loadAsync(data).then((zip) => {
// Restore job ticket and create job
zip.folder("_ticket").forEach((relativePath, file) => {
zip.file(`_ticket${path.sep}${relativePath}`).async("string")
.then((content) => {
// Restore old job ticket
job = pj.restoreJobTicket(content);
// Restore _files
pj.restoreFiles(job, zip, (unpackedJob) => {
done(unpackedJob);
});
});
});
});
});
}
protected restoreFiles(job: Job, zip: any, callback) {
let pj = this;
// Check for valid pack format
if (zip.folder("_asset").length > 1) {
pj.e.log(2, `Restored job did not contain any file assets.`, pj, [job]);
}
if (job.isFolder()) {
pj.extractFiles(zip, false, "_asset/", (folderPath, folderName) => {
job.path = folderPath;
job.rename(folderName);
callback(job);
});
} else if (job.isFile()) {
pj.extractFiles(zip, true, "_asset/", (filePath, fileName) => {
job.path = filePath;
job.rename(fileName);
callback(job);
});
}
}
protected extractFiles(zip: any, single: boolean, zipPath: string, callback: any, totalFiles?: number) {
let pj = this;
let tmpobj = tmp.dirSync();
let tempPath = tmpobj.name;
let fileNumber = 1;
if (!totalFiles) {
totalFiles = 0;
zip.folder(zipPath).forEach((asset) => totalFiles++ );
}
if (single === true) {
zip.folder(zipPath).forEach((relativePath, asset) => {
if (fileNumber > 1) {
pj.e.log(3, `More than 1 files found when extracting a file job.`, pj);
} else {
let newRelPath = zipPath + relativePath;
if (asset.dir === "true") {
totalFiles--;
pj.extractFiles(zip, single, newRelPath, callback, totalFiles);
} else {
zip.file(newRelPath).async("nodebuffer")
.then((content) => {
fileNumber++;
let filePath = tempPath + path.sep + relativePath;
fs.writeFileSync(filePath, content);
callback(filePath, relativePath);
});
}
}
});
} else {
zip.folder(zipPath).forEach((relativePath, asset) => {
let newRelPath = zipPath + relativePath;
if (asset.dir === true) {
totalFiles--;
pj.extractFiles(zip, single, newRelPath, callback, totalFiles);
} else {
zip.file(newRelPath).async("nodebuffer")
.then((content) => {
let filePath = tempPath + path.sep + relativePath;
fs.writeFileSync(filePath, content);
if (totalFiles === fileNumber) {
let rootFolderName = newRelPath.split(path.sep)[1];
callback(tempPath, rootFolderName);
}
fileNumber++;
});
}
});
}
}
}