@pipedream/canva
Version:
Pipedream Canva Components
184 lines (174 loc) • 5.75 kB
JavaScript
import canva from "../../canva.app.mjs";
import constants from "../../common/constants.mjs";
import fs from "fs";
import stream from "stream";
import util from "util";
export default {
key: "canva-export-design",
name: "Export Design",
description: "Starts a new job to export a file from Canva. [See the documentation](https://www.canva.dev/docs/connect/api-reference/exports/create-design-export-job/)",
version: "0.1.1",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: false,
},
type: "action",
props: {
canva,
designId: {
propDefinition: [
canva,
"designId",
],
},
type: {
type: "string",
label: "Format Type",
description: "The desired export format",
options: constants.EXPORT_TYPES,
},
pages: {
type: "integer[]",
label: "Pages",
description: "To specify which pages to export in a multi-page design, provide the page numbers as an array. The first page in a design is page `1`. If pages isn't specified, all the pages are exported.",
optional: true,
},
waitForCompletion: {
propDefinition: [
canva,
"waitForCompletion",
],
},
quality: {
type: "integer",
label: "Quality",
description: "Applicable for JPG exports only. 1 (smallest) to 100 (best quality). Defaults to 80 if not specified.",
optional: true,
},
mp4Quality: {
type: "string",
label: "Video Quality",
description: "Applicable for MP4 exports only. The orientation and resolution of the exported video.",
options: constants.MP4_QUALITY,
optional: true,
},
size: {
type: "string",
label: "Paper Size",
description: "Applicable for PDF exports only. The paper size of the exported file.",
options: constants.PAPER_SIZE,
optional: true,
},
lossless: {
type: "boolean",
label: "Lossless",
description: "Applicable for PNG exports only. When true, uses lossless compression.",
optional: true,
},
asSingleImage: {
type: "boolean",
label: "As Single Image",
description: "Applicable for PNG exports only. When true, merges multi-page designs into a single image.",
optional: true,
},
exportQuality: {
type: "string",
label: "Export Quality",
description: "Applicable for PDF, JPG, PNG, GIF, MP4 exports. Pro export may fail if design contains premium elements.",
options: constants.EXPORT_QUALITY,
optional: true,
},
height: {
type: "integer",
label: "Height",
description: "Applicable for JPG, PNG, GIF exports only. Height in pixels of the exported image.",
optional: true,
},
width: {
type: "integer",
label: "Width",
description: "Applicable for JPG, PNG, GIF exports only. Width in pixels of the exported image.",
optional: true,
},
newFileName: {
type: "string",
label: "File Name",
description: "The file name to save the exported file as under /tmp. Include the extension e.g. design.pdf",
optional: true,
},
syncDir: {
type: "dir",
accessMode: "write",
sync: true,
},
},
async run({ $ }) {
let response = await this.canva.exportDesign({
$,
data: {
design_id: this.designId,
format: {
type: this.type,
pages: this.pages,
...(this.type === "mp4"
? {
quality: this.mp4Quality,
}
: {
quality: this.quality ?? (this.type === "jpg"
? 80
: undefined),
}),
export_quality: this.exportQuality,
size: this.size,
height: this.height,
width: this.width,
lossless: this.lossless,
as_single_image: this.asSingleImage,
},
},
});
if (this.waitForCompletion) {
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
const exportId = response.job.id;
const deadline = Date.now() + 2 * 60 * 1000;
while (response.job.status === "in_progress") {
if (Date.now() >= deadline) {
throw new Error(`Timed out waiting for export job "${exportId}" to complete`);
}
await timer(3000);
response = await this.canva.getDesignExportJob({
$,
exportId,
});
if (response.job.error) {
throw new Error(response.job.error.message);
}
}
const urls = response.job.urls;
if (!urls || urls.length === 0) {
throw new Error("Export completed but no download URL was returned.");
}
const pipeline = util.promisify(stream.pipeline);
const savedPaths = [];
for (let i = 0; i < urls.length; i++) {
const fileResponse = await fetch(urls[i]);
const defaultName = `canva-export-${response.job.id}.${this.type}`;
const baseName = (this.newFileName || defaultName).split("/").pop();
const fileName = urls.length > 1
? `${baseName.replace(/\.[^.]+$/, "")}-${i + 1}${baseName.match(/\.[^.]+$/) || ""}`
: baseName;
const tmpFilePath = `/tmp/${fileName}`;
await pipeline(fileResponse.body, fs.createWriteStream(tmpFilePath));
savedPaths.push(tmpFilePath);
}
$.export("$summary", `Successfully exported design "${this.designId}" and saved to ${savedPaths.join(", ")}`);
return savedPaths.length === 1
? savedPaths[0]
: savedPaths;
}
$.export("$summary", `Successfully started export job for design "${this.designId}"`);
return response;
},
};