UNPKG

@seqera/node-red-seqera

Version:

Node-RED nodes for interacting with the Seqera Platform API

157 lines (136 loc) 6.29 kB
module.exports = function (RED) { function SeqeraDatasetCreateNode(config) { RED.nodes.createNode(this, config); const node = this; // Store typedInput properties node.datasetNameProp = config.datasetName; node.datasetNamePropType = config.datasetNameType; node.fileContentsProp = config.fileContents; node.fileContentsPropType = config.fileContentsType; node.fileType = config.fileType || "csv"; node.descriptionProp = config.description; node.descriptionPropType = config.descriptionType; node.baseUrlProp = config.baseUrl; node.baseUrlPropType = config.baseUrlType; node.workspaceIdProp = config.workspaceId; node.workspaceIdPropType = config.workspaceIdType; // Reference to the shared Seqera config node node.seqeraConfig = RED.nodes.getNode(config.seqera); node.defaultBaseUrl = (node.seqeraConfig && node.seqeraConfig.baseUrl) || "https://api.cloud.seqera.io"; node.credentials = RED.nodes.getCredentials(node.id); const axios = require("axios"); const FormData = require("form-data"); const { apiCall } = require("./_utils"); // Helper to format date as yyyy-mm-dd HH:MM:SS const formatDateTime = () => { const d = new Date(); const pad = (n) => n.toString().padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${d.toLocaleTimeString()}`; }; node.on("input", async function (msg, send, done) { // evaluate properties const evalProp = async (p, t) => { if (t === "jsonata") { const expr = RED.util.prepareJSONataExpression(p, node); return await new Promise((resolve, reject) => { RED.util.evaluateJSONataExpression(expr, msg, (err, value) => { if (err) return reject(err); resolve(value); }); }); } return RED.util.evaluateNodeProperty(p, t, node, msg); }; const datasetName = await evalProp(node.datasetNameProp, node.datasetNamePropType); let fileContents = await evalProp(node.fileContentsProp, node.fileContentsPropType); const description = await evalProp(node.descriptionProp, node.descriptionPropType); const baseUrlOverride = await evalProp(node.baseUrlProp, node.baseUrlPropType); const workspaceIdOverride = await evalProp(node.workspaceIdProp, node.workspaceIdPropType); const baseUrl = baseUrlOverride || (node.seqeraConfig && node.seqeraConfig.baseUrl) || node.defaultBaseUrl; const workspaceId = workspaceIdOverride || (node.seqeraConfig && node.seqeraConfig.workspaceId) || null; // Fallbacks if (fileContents === undefined || fileContents === null) { fileContents = msg.payload; } if (!datasetName) { const err = new Error([ "datasetName not provided", datasetName, node.datasetNameProp, node.datasetNamePropType, ]); if (done) done(err); return; } if (!fileContents) { const err = new Error("fileContents not provided"); if (done) done(err); return; } try { let apiStep = "create"; node.status({ fill: "blue", shape: "ring", text: `creating: ${formatDateTime()}` }); // 1) Create dataset let createUrl = `${baseUrl.replace(/\/$/, "")}/datasets`; if (workspaceId != null) createUrl += `?workspaceId=${workspaceId}`; const createBody = { name: datasetName }; if (description != null && description !== "") createBody.description = description; const createResp = await apiCall(node, "post", createUrl, { headers: { "Content-Type": "application/json" }, data: createBody, }); const datasetId = createResp.data?.dataset?.id || createResp.data?.datasetId || createResp.data?.id || null; if (!datasetId) { const err = new Error("Dataset ID not returned"); err.api_call = "create"; throw err; } // 2) Upload file apiStep = "upload"; node.status({ fill: "yellow", shape: "ring", text: `uploading: ${formatDateTime()}` }); let uploadUrl = `${baseUrl.replace(/\/$/, "")}/datasets/${datasetId}/upload`; if (workspaceId != null) uploadUrl += `?workspaceId=${workspaceId}`; const form = new FormData(); const buffer = Buffer.isBuffer(fileContents) ? fileContents : Buffer.from(typeof fileContents === "string" ? fileContents : JSON.stringify(fileContents)); // Determine MIME type based on selected fileType const mime = node.fileType === "tsv" ? "text/tab-separated-values" : "text/csv"; form.append("file", buffer, { filename: `${datasetName}.${node.fileType}`, contentType: mime }); const uploadHeaders = form.getHeaders(); const uploadResp = await apiCall(node, "post", uploadUrl, { headers: uploadHeaders, data: form }); msg.payload = uploadResp.data; msg.datasetId = datasetId; node.status({ fill: "green", shape: "dot", text: `uploaded: ${formatDateTime()}` }); send(msg); if (done) done(); } catch (err) { if (!err.api_call) err.api_call = err.config && err.config.url && /upload/.test(err.config.url) ? "upload" : "create"; node.status({ fill: "red", shape: "dot", text: `error: ${formatDateTime()}` }); node.error(err); return; } }); } RED.nodes.registerType("seqera-dataset-create", SeqeraDatasetCreateNode, { credentials: { token: { type: "password" } }, defaults: { name: { value: "" }, seqera: { value: "", type: "seqera-config", required: true }, datasetName: { value: "datasetName" }, datasetNameType: { value: "str" }, fileContents: { value: "payload" }, fileContentsType: { value: "msg" }, workspaceId: { value: "workspaceId" }, workspaceIdType: { value: "str" }, description: { value: "description" }, descriptionType: { value: "str" }, baseUrl: { value: "baseUrl" }, baseUrlType: { value: "str" }, token: { value: "token" }, tokenType: { value: "str" }, fileType: { value: "csv" }, }, }); };