UNPKG

@seqera/node-red-seqera

Version:

Node-RED nodes for interacting with the Seqera Platform API

267 lines (235 loc) 10.9 kB
<script type="text/html" data-template-name="seqera-datalink-poll"> <!-- Seqera config and node name --> <div class="form-row"> <label for="node-input-seqera"><i class="icon-globe"></i> Seqera config</label> <input type="text" id="node-input-seqera" data-type="seqera-config" /> </div> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" /> </div> <!-- Polling specific --> <div class="form-row"> <label for="node-input-pollFrequency"><i class="fa fa-clock-o"></i> Poll frequency</label> <input type="text" id="node-input-pollFrequency" style="text-align:end; width:50px !important" /> <select id="node-input-pollUnits" style="width:120px !important"> <option value="seconds">Seconds</option> <option value="minutes">Minutes</option> <option value="hours">Hours</option> <option value="days">Days</option> </select> </div> <!-- Data link parameters (same as list node) --> <div class="form-row"> <label for="node-input-dataLinkName"><i class="fa fa-link"></i> Data link name</label> <input type="text" id="node-input-dataLinkName" /> </div> <div class="form-row"> <label for="node-input-returnType"><i class="fa fa-filter"></i> Return type</label> <select id="node-input-returnType"> <option value="files">Files only</option> <option value="folders">Folders only</option> <option value="all">Everything</option> </select> </div> <div class="form-row"> <label for="node-input-basePath"><i class="fa fa-folder-open-o"></i> Base path</label> <input type="text" id="node-input-basePath" /> </div> <div class="form-row"> <label for="node-input-prefix"><i class="fa fa-filter"></i> Prefix</label> <input type="text" id="node-input-prefix" /> </div> <div class="form-row"> <label for="node-input-pattern"><i class="fa fa-file-code-o"></i> Pattern (regex)</label> <input type="text" id="node-input-pattern" /> </div> <div class="form-row"> <label for="node-input-maxResults"><i class="fa fa-sort-numeric-asc"></i> Max results</label> <input type="text" id="node-input-maxResults" /> </div> <div class="form-row"> <label for="node-input-depth"><i class="fa fa-level-down"></i> Depth</label> <input type="text" id="node-input-depth" /> </div> <div class="form-row"> <label for="node-input-workspaceId"><i class="icon-tasks"></i> Workspace ID</label> <input type="text" id="node-input-workspaceId" /> </div> </script> <!-- prettier-ignore --> <script type="text/markdown" data-help-name="seqera-datalink-poll"> Polls a Seqera Platform Data Explorer link at a fixed interval. ### Inputs : pollFrequency (string) : Poll frequency (default `15 minutes`). Can be configured in seconds, minutes, hours, or days. : dataLinkName (string) : The name of the data explorer link. : basePath (string) : Path within the data link to start browsing. Leave blank for the root. : prefix (string) : Optional prefix filter for results (applies to folders and files) : pattern (string) : Optional regex pattern filter for results (applies to files only) : returnType (string) : Select whether to return files, folders or everything. : maxResults (number) : Maximum number of results to return (default 100). : workspaceId (string) : Override the workspace ID from the config node. All inputs support msg._, flow._, global.\*, env, or JSONata expressions via the **typedInput**. ### Outputs The node has two outputs: 1. All results on every poll. 2. New objects since the previous poll (nothing sent if no new objects). Both outputs have the following properties: : payload (array) : Fle information aggregated from the API (array of objects). : files (array) : File names (array of strings). All typed-input fields are identical to the _List files_ node with the addition of **poll frequency**. </script> <script type="text/javascript"> RED.nodes.registerType("seqera-datalink-poll", { category: "seqera", color: "#A9A1C6", inputs: 0, outputs: 2, icon: "icons/data-explorer.svg", align: "left", paletteLabel: "Poll files", label: function () { return this.name || "Poll files"; }, outputLabels: ["All objects", "Only new objects"], defaults: { name: { value: "" }, seqera: { value: "", type: "seqera-config" }, dataLinkName: { value: "", required: true, }, dataLinkNameType: { value: "str" }, basePath: { value: "" }, basePathType: { value: "str" }, prefix: { value: "" }, prefixType: { value: "str" }, pattern: { value: "" }, patternType: { value: "str" }, maxResults: { value: "100" }, maxResultsType: { value: "num" }, depth: { value: "0" }, depthType: { value: "num" }, workspaceId: { value: "" }, workspaceIdType: { value: "str" }, pollFrequency: { value: "15" }, pollUnits: { value: "minutes" }, returnType: { value: "files" }, }, oneditprepare: function () { function ti(id, val, type, def = "str") { const types = ["str", "num", "msg", "flow", "global", "env", "jsonata"]; if (def === "num") types.splice(types.indexOf("str"), 1); $(id).typedInput({ default: def, types }); $(id).typedInput("value", val); $(id).typedInput("type", type); } ti("#node-input-dataLinkName", this.dataLinkName || "", this.dataLinkNameType || "str"); ti("#node-input-basePath", this.basePath || "", this.basePathType || "str"); ti("#node-input-prefix", this.prefix || "", this.prefixType || "str"); ti("#node-input-pattern", this.pattern || "", this.patternType || "str"); ti("#node-input-maxResults", this.maxResults || "100", this.maxResultsType || "num", "num"); ti("#node-input-depth", this.depth || "0", this.depthType || "num", "num"); ti("#node-input-workspaceId", this.workspaceId || "", this.workspaceIdType || "str"); // Poll frequency – simple number input with units $("#node-input-pollFrequency").val(this.pollFrequency || "15"); $("#node-input-pollUnits").val(this.pollUnits || "minutes"); $("#node-input-returnType").val(this.returnType || "files"); // Add auto-complete for datalink name when type is "str" const setupAutoComplete = () => { const dataLinkNameInput = $("#node-input-dataLinkName"); const currentType = dataLinkNameInput.typedInput("type"); // Ensure our custom CSS is added only once if (!document.getElementById("seqera-autocomplete-style")) { const style = document.createElement("style"); style.id = "seqera-autocomplete-style"; style.innerHTML = `.red-ui-autoComplete li a { display:block; padding:4px 8px; width:100%; box-sizing:border-box; }`; document.head.appendChild(style); } // Get the actual visible input element from the typedInput widget const actualInput = dataLinkNameInput.parent().find(".red-ui-typedInput-input"); // Remove existing auto-complete if any if (actualInput.data("autoCompleteEnabled")) { actualInput.autoComplete("destroy"); actualInput.data("autoCompleteEnabled", false); } if (currentType === "str" && actualInput.length > 0) { // Add a small delay to ensure typedInput is fully initialized setTimeout(() => { actualInput.autoComplete({ search: function (value, done) { // Only search if we have some input if (!value || value.length < 1) { done([]); return; } // Make API call to get datalink names const nodeId = (RED.editor.edit_node && RED.editor.edit_node.id) || (RED.editor.edit_stack && RED.editor.edit_stack.length > 0 && RED.editor.edit_stack[0].id) || "temp-" + Date.now(); const seqeraConfigId = $("#node-input-seqera").val(); if (!seqeraConfigId) { done([]); return; } const params = new URLSearchParams({ search: value, seqeraConfig: seqeraConfigId, }); // Add workspace ID override if configured const workspaceIdOverride = $("#node-input-workspaceId").typedInput("value"); const workspaceIdType = $("#node-input-workspaceId").typedInput("type"); if (workspaceIdType === "str" && workspaceIdOverride && workspaceIdOverride.trim()) { params.append("workspaceId", workspaceIdOverride); } $.ajax({ url: `admin/seqera/datalinks/${nodeId}?${params.toString()}`, method: "GET", success: function (data) { // data is already in the correct format: [{value, label}] done(data || []); }, error: function () { done([]); }, }); }, }); actualInput.data("autoCompleteEnabled", true); }, 100); } }; // Set up auto-complete initially setupAutoComplete(); // Re-setup auto-complete when type changes $("#node-input-dataLinkName").on("change", function (event, type) { if (type) { // Add delay to avoid conflicts with ongoing autocomplete operations setTimeout(setupAutoComplete, 200); } }); // Re-setup auto-complete when Seqera config changes $("#node-input-seqera").on("change", function () { setupAutoComplete(); }); }, oneditsave: function () { function save(id, prop, propType) { this[prop] = $(id).typedInput("value"); this[propType] = $(id).typedInput("type"); } save.call(this, "#node-input-dataLinkName", "dataLinkName", "dataLinkNameType"); save.call(this, "#node-input-basePath", "basePath", "basePathType"); save.call(this, "#node-input-prefix", "prefix", "prefixType"); save.call(this, "#node-input-pattern", "pattern", "patternType"); save.call(this, "#node-input-maxResults", "maxResults", "maxResultsType"); save.call(this, "#node-input-depth", "depth", "depthType"); save.call(this, "#node-input-workspaceId", "workspaceId", "workspaceIdType"); // Save poll frequency and units this.pollFrequency = $("#node-input-pollFrequency").val(); this.pollUnits = $("#node-input-pollUnits").val(); this.returnType = $("#node-input-returnType").val(); }, }); </script>