diff --git a/components/pipedream_utils/README.md b/components/pipedream_utils/README.md new file mode 100644 index 0000000000000..44e08e9766911 --- /dev/null +++ b/components/pipedream_utils/README.md @@ -0,0 +1,14 @@ +# Overview + +The Pipedream Utils app is a set of pre-built functions that streamline common tasks in your workflows. It acts like a Swiss Army knife for developers, providing essential tools such as format conversion, date manipulation, and text processing. By leveraging these functions, you can reduce the boilerplate code needed for routine operations, speeding up the development of intricate automations. The Helper Functions API can be a game changer when it comes to tasks like parsing dates in user-friendly formats, encoding and decoding data, or generating UUIDs, making them more efficient and less error-prone. + +# Example Use Cases + +- **Format User Input for Database Storage** + In an app where users submit data through forms, the Helper Functions API can be used to sanitize and format user input before it is stored in a database, such as Airtable. This ensures that data is clean and uniform, simplifying retrieval and analysis. + +- **Process Webhook Payloads** + When dealing with incoming webhooks from apps like GitHub, the Helper Functions API can parse and transform JSON payloads. This allows you to extract specific data points and reformat them for use in other apps like Slack for team notifications or JIRA for creating issues. + +- **Automate Content Publication Workflow** + A content calendar on Google Sheets can trigger a workflow that uses Helper Functions to parse dates and format post titles. The workflow could then use this data to automatically schedule and publish content on platforms like WordPress or social media apps. diff --git a/components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs b/components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs new file mode 100644 index 0000000000000..fd476ecb742e4 --- /dev/null +++ b/components/pipedream_utils/actions/add-subtract-time/add-subtract-time.mjs @@ -0,0 +1,91 @@ +import { ConfigurationError } from "@pipedream/platform"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import commonDateTime from "../../common/date-time/commonDateTime.mjs"; +import { + DATE_FORMAT_PARSE_MAP, DEFAULT_FORMAT_VALUE, +} from "../../common/date-time/dateFormats.mjs"; +import { DATE_TIME_UNITS } from "../../common/date-time/dateTimeUnits.mjs"; +import sugar from "sugar"; +const OPERATION_OPTIONS = { + ADD: "Add", + SUBTRACT: "Subtract", +}; +export default { + ...commonDateTime, + name: "Formatting - [Date/Time] Add/Subtract Time", + description: "Add or subtract time from a given input", + key: "pipedream_utils-add-subtract-time", + version: "0.0.5", + type: "action", + props: { + ...commonDateTime.props, + operation: { + label: "Operation", + description: "Whether to add or subtract time.", + type: "string", + options: Object.values(OPERATION_OPTIONS), + }, + duration: { + label: "Duration", + description: "The duration for the operation. You can use the shorthand duration, for example: `1s`, `1m`, `1h`, `1d`, `1w`, `1y` equal one second, minute, hour, day, week, and year respectively", + type: "string", + }, + outputFormat: { + propDefinition: [ + pipedream_utils, + "outputFormat", + ], + }, + }, + methods: { + ...commonDateTime.methods, + getOperationMilliseconds(str) { + let result = 0; + const { + second, minute, hour, day, week, year, + } = DATE_TIME_UNITS; + Object.entries({ + s: second, + m: minute, + h: hour, + d: day, + w: week, + y: year, + }).forEach(([ + identifier, + multiplier, + ]) => { + const substr = str.match(new RegExp(`[0-9]+\\s*${identifier}`))?.[0]; + if (substr) { + const value = Number(substr.match(/[0-9]+/)); + result += value * multiplier; + } + }); + return result; + }, + }, + async run({ $ }) { + const { + operation, duration, outputFormat, + } = this; + const dateObj = this.getDateFromInput(); + const value = dateObj.valueOf(); + let amount = this.getOperationMilliseconds(duration); + if (operation === OPERATION_OPTIONS.SUBTRACT) + amount *= -1; + const result = value + amount; + const format = outputFormat ?? this.inputFormat ?? DEFAULT_FORMAT_VALUE; + try { + const { outputFn } = DATE_FORMAT_PARSE_MAP.get(format); + const output = outputFn(sugar.Date.create(result)); + $.export("$summary", `Successfully ${operation === OPERATION_OPTIONS.SUBTRACT + ? "subtracted" + : "added"} time`); + return output; + } + catch (err) { + console.log("Error parsing date", err); + throw new ConfigurationError("**Parse error** - check your input and if the selected format is correct."); + } + }, +}; diff --git a/components/pipedream_utils/actions/base64-decode-string/base64-decode-string.mjs b/components/pipedream_utils/actions/base64-decode-string/base64-decode-string.mjs new file mode 100644 index 0000000000000..6381007377179 --- /dev/null +++ b/components/pipedream_utils/actions/base64-decode-string/base64-decode-string.mjs @@ -0,0 +1,20 @@ +// legacy_hash_id: a_0Mio28 +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-base64-decode-string", + name: "Helper Functions - Base64 Decode String", + description: "Accepts a base64-encoded string, returns a decoded UTF-8 string", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + data: { + type: "string", + }, + }, + async run({ $ }) { + const buffer = Buffer.from(this.data, "base64"); + $.export("data", buffer.toString("utf8")); + }, +}; diff --git a/components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs b/components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs new file mode 100644 index 0000000000000..5020bbfd6adfe --- /dev/null +++ b/components/pipedream_utils/actions/compare-arrays/compare-arrays.mjs @@ -0,0 +1,92 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-compare-arrays", + name: "Helper Functions - Compare Arrays", + description: "Get the difference, intersection, union, or symetric difference of two arrays/sets.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + array1: { + type: "string[]", + label: "Array 1", + description: "Array to compare to second array", + default: [], + }, + array2: { + type: "string[]", + label: "Array 2", + description: "Array to be compared with first array", + default: [], + }, + actionType: { + type: "string", + label: "Compare Action", + description: "Type of action to perform on the arrays", + options: [ + "difference", + "union", + "intersection", + "symmetric difference", + ], + }, + }, + methods: { + getDifference(set1, set2) { + return new Set([ + ...set1, + ].filter((x) => !set2.has(x))); + }, + getIntersection(set1, set2) { + return new Set([ + ...set1, + ].filter((x) => set2.has(x))); + }, + getUnion(set1, set2) { + for (const elem of set2) { + set1.add(elem); + } + return set1; + }, + getSymmetricDifference(set1, set2) { + for (const elem of set2) { + if (set1.has(elem)) { + set1.delete(elem); + } else { + set1.add(elem); + } + } + return set1; + }, + }, + run() { + const set1 = new Set(this.array1); + const set2 = new Set(this.array2); + + let results; + + switch (this.actionType) { + case "difference": { + results = this.getDifference(set1, set2); + break; + } + case "union": { + results = this.getUnion(set1, set2); + break; + } + case "intersection": { + results = this.getIntersection(set1, set2); + break; + } + case "symmetric difference": { + results = this.getSymmetricDifference(set1, set2); + break; + } + default: + throw new Error(`Unknown action type: ${this.actionType}`); + } + + return Array.from(results); + }, +}; diff --git a/components/pipedream_utils/actions/compare-dates/compare-dates.mjs b/components/pipedream_utils/actions/compare-dates/compare-dates.mjs new file mode 100644 index 0000000000000..38adff5613c9e --- /dev/null +++ b/components/pipedream_utils/actions/compare-dates/compare-dates.mjs @@ -0,0 +1,63 @@ +import commonDateTime from "../../common/date-time/commonDateTime.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import { DATE_TIME_UNITS } from "../../common/date-time/dateTimeUnits.mjs"; +export default { + ...commonDateTime, + name: "Formatting - [Date/Time] Compare Dates", + description: "Get the duration between two dates in days, hours, minutes, and seconds along with checking if they are the same.", + key: "pipedream_utils-compare-dates", + version: "0.0.5", + type: "action", + props: { + ...commonDateTime.props, + inputDate: { + propDefinition: [ + pipedream_utils, + "inputDate", + ], + label: "Start Date", + description: "Enter start date string, in the format defined in `Input Format`. If the start date is after the end date, these dates will be swapped and in the output `datesSwapped` will be set to `true`.", + }, + endDate: { + propDefinition: [ + pipedream_utils, + "inputDate", + ], + label: "End Date", + description: "Enter end date string, in the format defined in `Input Format`. Timezone is assumed the same for both dates if not explicitly set.", + }, + }, + async run({ $ }) { + const startDateObj = this.getDateFromInput(this.inputDate); + const endDateObj = this.getDateFromInput(this.endDate); + const startValue = startDateObj.valueOf(); + const endValue = endDateObj.valueOf(); + const datesSwapped = startValue > endValue; + let result = "equal"; + let remainingValue = Math.abs(endValue - startValue); + if (remainingValue) { + const arrResults = []; + const arrUnits = Object.entries(DATE_TIME_UNITS).sort((a, b) => b[1] - a[1]); + for (const [ + word, + unit, + ] of arrUnits) { + const amount = Math.floor(remainingValue / unit); + if (amount) { + arrResults.push(`${amount} ${amount === 1 + ? word + : `${word}s`}`); + remainingValue %= unit; + if (!remainingValue) + break; + } + } + result = arrResults.join(", "); + } + $.export("$summary", "Successfully compared dates"); + return { + datesSwapped, + result, + }; + }, +}; diff --git a/components/pipedream_utils/actions/convert-currency/convert-currency.mjs b/components/pipedream_utils/actions/convert-currency/convert-currency.mjs new file mode 100644 index 0000000000000..ccdc4da9534fd --- /dev/null +++ b/components/pipedream_utils/actions/convert-currency/convert-currency.mjs @@ -0,0 +1,70 @@ +import { axios } from "@pipedream/platform"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-convert-currency", + name: "Helper Functions - Convert Currency", + description: "Convert an amount between currencies. [See the documentation](https://www.frankfurter.app/docs/)", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + fromCurrency: { + type: "string", + label: "From Currency", + description: "The currency to convert from", + async options() { + return this.getCurrencyOptions(); + }, + }, + toCurrency: { + type: "string", + label: "To Currency", + description: "The currency to convert to", + async options() { + return this.getCurrencyOptions(); + }, + }, + value: { + type: "string", + label: "Value", + description: "The value to convert", + }, + }, + methods: { + async getCurrencyOptions() { + const currencies = await this.getCurrencies(); + const options = []; + for (const [ + key, + value, + ] of Object.entries(currencies)) { + options.push({ + value: key, + label: value, + }); + } + return options; + }, + getCurrencies($ = this) { + return axios($, { + url: "https://api.frankfurter.app/currencies", + }); + }, + convertCurrency($ = this) { + return axios($, { + url: "https://api.frankfurter.app/latest", + params: { + from: this.fromCurrency, + to: this.toCurrency, + amount: this.value, + }, + }); + }, + }, + async run({ $ }) { + const response = await this.convertCurrency($); + $.export("$summary", `${this.value} ${this.fromCurrency} = ${response.rates[this.toCurrency]} ${this.toCurrency}`); + return response; + }, +}; diff --git a/components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs b/components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs new file mode 100644 index 0000000000000..1f63845972955 --- /dev/null +++ b/components/pipedream_utils/actions/convert-html-to-markdown/convert-html-to-markdown.mjs @@ -0,0 +1,26 @@ +import { parseHTML } from "linkedom"; +import showdown from "showdown"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Convert HTML to Markdown", + description: "Convert valid HTML to Markdown text", + key: "pipedream_utils-convert-html-to-markdown", + version: "0.0.6", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "HTML string to be converted to Markdown", + type: "string", + }, + }, + async run({ $ }) { + const { input } = this; + const converter = new showdown.Converter(); + const dom = parseHTML(""); + const result = converter.makeMarkdown(input, dom.window.document); + $.export("$summary", "Successfully converted to Markdown"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs b/components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs new file mode 100644 index 0000000000000..57b804b4bb6ef --- /dev/null +++ b/components/pipedream_utils/actions/convert-html-to-slack-mrkdwn/convert-html-to-slack-mrkdwn.mjs @@ -0,0 +1,21 @@ +// legacy_hash_id: a_eliYz4 +import slackifyHtml from "slackify-html"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-convert-html-to-slack-mrkdwn", + name: "Helper Functions - Convert HTML to Slack mrkdwn format", + description: "Converts an HTML string to the Slack mrkdwn format using", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + html: { + type: "string", + label: "HTML", + }, + }, + async run() { + return slackifyHtml(this.html); + }, +}; diff --git a/components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs b/components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs new file mode 100644 index 0000000000000..d43f458a2b117 --- /dev/null +++ b/components/pipedream_utils/actions/convert-html-to-text/convert-html-to-text.mjs @@ -0,0 +1,23 @@ +import { convert } from "html-to-text"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Convert HTML to text", + description: "Convert valid HTML to text", + key: "pipedream_utils-convert-html-to-text", + version: "0.0.4", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "HTML string to be converted to text", + type: "string", + }, + }, + async run({ $ }) { + const { input } = this; + const result = convert(input); + $.export("$summary", "Successfully converted to text"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs b/components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs new file mode 100644 index 0000000000000..3f26f70c3d29d --- /dev/null +++ b/components/pipedream_utils/actions/convert-json-to-string/convert-json-to-string.mjs @@ -0,0 +1,26 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Data] Convert JSON to String", + description: "Convert an object to a JSON format string", + key: "pipedream_utils-convert-json-to-string", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "An object to be serialized to a JSON format string.", + type: "object", + }, + }, + async run({ $ }) { + try { + const result = JSON.stringify(this.input); + $.export("$summary", "Successfully convert object to JSON string"); + return result; + } + catch (err) { + throw new Error("Error serializing object to JSON string: " + err.toString()); + } + }, +}; diff --git a/components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs b/components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs new file mode 100644 index 0000000000000..90c4b35b021e9 --- /dev/null +++ b/components/pipedream_utils/actions/convert-markdown-to-html/convert-markdown-to-html.mjs @@ -0,0 +1,26 @@ +import showdown from "showdown"; +import { parseHTML } from "linkedom"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Convert Markdown to HTML", + description: "Convert Markdown text to HTML", + key: "pipedream_utils-convert-markdown-to-html", + version: "0.0.6", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Markdown string to be converted to HTML", + type: "string", + }, + }, + async run({ $ }) { + const { input } = this; + const converter = new showdown.Converter(); + const dom = parseHTML(""); + const result = converter.makeHtml(input, dom.window.document); + $.export("$summary", "Successfully converted to HTML"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs b/components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs new file mode 100644 index 0000000000000..5d811cea14ba0 --- /dev/null +++ b/components/pipedream_utils/actions/convert-object-to-json-string/convert-object-to-json-string.mjs @@ -0,0 +1,20 @@ +// legacy_hash_id: a_a4i80O +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-convert-object-to-json-string", + name: "Helper Functions - Convert JavaScript Object to JSON String", + description: "Accepts a JavaScript object, returns that object converted to a JSON string", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + object: { + type: "string", + description: "The JavaScript object you'd like to convert to a JSON string", + }, + }, + async run() { + return JSON.stringify(this.object); + }, +}; diff --git a/components/pipedream_utils/actions/csv-file-to-objects/csv-file-to-objects.mjs b/components/pipedream_utils/actions/csv-file-to-objects/csv-file-to-objects.mjs new file mode 100644 index 0000000000000..064b32970797e --- /dev/null +++ b/components/pipedream_utils/actions/csv-file-to-objects/csv-file-to-objects.mjs @@ -0,0 +1,81 @@ +import { readFileSync } from "fs"; +import path from "path"; +import { parse } from "csv-parse/sync"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-csv-file-to-objects", + name: "Helper Functions - CSV File To Objects", + description: "Convert a CSV file to an array of objects.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + filePath: { + type: "string", + label: "CSV File Path", + description: "The path to the file saved to the `/tmp` directory (e.g. `/tmp/example.csv`). [See the documentation](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory).", + }, + hasHeaders: { + type: "boolean", + label: "File Contains Headers", + description: "Set to `true` if the first row of the CSV contains headers. If there are headers in the file, the keys of the objects will be the header values. If there are no headers, each object will be an array of values.", + optional: true, + default: false, + }, + skipEmptyLines: { + type: "boolean", + label: "Skip Empty Lines", + description: "Set to `true` to skip empty lines in the file.", + optional: true, + default: true, + }, + skipRecordsWithEmptyValues: { + type: "boolean", + label: "Skip Records With Empty Values", + description: "Set to `true` to skip records with empty values. Don't generate records for lines containing empty values, empty Buffer or equals to `null` and `undefined` if their value was casted.", + optional: true, + default: false, + }, + skipRecordsWithError: { + type: "boolean", + label: "Skip Records With Error", + description: "Set to `true` to skip records with errors. Tolerates parsing errors. It skips the records containing an error inside and directly go process the next record.", + optional: true, + default: false, + }, + }, + async run({ $ }) { + const { + filePath, + hasHeaders, + skipEmptyLines, + skipRecordsWithEmptyValues, + skipRecordsWithError, + } = this; + + let fileContent; + try { + fileContent = readFileSync(path.resolve(filePath), "utf8"); + } catch (error) { + console.error("Error reading file:", error); + throw error; + } + + try { + const records = parse(fileContent, { + columns: hasHeaders, + skip_empty_lines: skipEmptyLines, + skip_records_with_empty_values: skipRecordsWithEmptyValues, + skip_records_with_error: skipRecordsWithError, + }); + + $.export("$summary", `Converted ${records.length} records from CSV to objects.`); + return records; + + } catch (error) { + console.error("Error converting CSV to objects:", error); + throw error; + } + }, +}; diff --git a/components/pipedream_utils/actions/date-time-format/date-time-format.mjs b/components/pipedream_utils/actions/date-time-format/date-time-format.mjs new file mode 100644 index 0000000000000..0b2d04e14c19e --- /dev/null +++ b/components/pipedream_utils/actions/date-time-format/date-time-format.mjs @@ -0,0 +1,39 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { DATE_FORMAT_PARSE_MAP } from "../../common/date-time/dateFormats.mjs"; +import commonDateTime from "../../common/date-time/commonDateTime.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import sugar from "sugar"; +export default { + ...commonDateTime, + name: "Formatting - [Date/Time] Format", + description: "Format a date string to another date string. For more examples on formatting, see the [Sugar Date Format](https://sugarjs.com/dates/#/Formatting) documentation.", + key: "pipedream_utils-date-time-format", + version: "0.0.6", + type: "action", + props: { + ...commonDateTime.props, + outputFormat: { + propDefinition: [ + pipedream_utils, + "outputFormat", + ], + description: "The format to convert the date to. For more examples on formatting, see the [Sugar Date Format](https://sugarjs.com/dates/#/Formatting) documentation.", + optional: false, + }, + }, + async run({ $ }) { + const { outputFormat } = this; + const dateObj = this.getDateFromInput(); + try { + const response = DATE_FORMAT_PARSE_MAP.get(outputFormat); + const output = response + ? response.outputFn(dateObj) + : sugar.Date.format(dateObj, outputFormat); + $.export("$summary", "Successfully formatted date/time"); + return output; + } + catch (err) { + throw new ConfigurationError("**Parse error** - check your input and if the selected format is correct."); + } + }, +}; diff --git a/components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs b/components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs new file mode 100644 index 0000000000000..ae87ddc34592d --- /dev/null +++ b/components/pipedream_utils/actions/download-file-to-tmp/download-file-to-tmp.mjs @@ -0,0 +1,49 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import { axios } from "@pipedream/platform"; +import fs from "fs"; + +export default { + key: "pipedream_utils-download-file-to-tmp", + name: "Helper Functions - Download File To /tmp", + description: "Downloads a file to [your workflow's /tmp directory](https://pipedream.com/docs/code/nodejs/working-with-files/#the-tmp-directory)", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + url: { + type: "string", + label: "Download File URL", + description: "Enter the URL of the file to download", + }, + filename: { + type: "string", + label: "Target Filename", + description: "The filename that will be used to save in /tmp", + }, + }, + async run({ $ }) { + const { + url, filename, + } = this; + + const resp = await axios($, { + url, + responseType: "arraybuffer", + }); + + /** + * Saves file to /tmp folder and exports file's file name and file path, + */ + const rawcontent = resp.toString("base64"); + const buffer = Buffer.from(rawcontent, "base64"); + const downloadedFilepath = `/tmp/${filename}`; + fs.writeFileSync(downloadedFilepath, buffer); + + const filedata = [ + filename, + downloadedFilepath, + ]; + + return filedata; + }, +}; diff --git a/components/pipedream_utils/actions/export-variables/export-variables.mjs b/components/pipedream_utils/actions/export-variables/export-variables.mjs new file mode 100644 index 0000000000000..5972e5fd20046 --- /dev/null +++ b/components/pipedream_utils/actions/export-variables/export-variables.mjs @@ -0,0 +1,40 @@ +import { ConfigurationError } from "@pipedream/platform"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-export-variables", + name: "Helper Functions - Export Variables", + description: "Export variables for use in your workflow", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + config: { + type: "object", + label: "Configuration", + description: "Enter key-value pairs that you'd like to reference throughout your workflow.", + }, + }, + methods: { + emptyStrToUndefined(value) { + const trimmed = typeof(value) === "string" && value.trim(); + return trimmed === "" + ? undefined + : value; + }, + parse(value) { + const valueToParse = this.emptyStrToUndefined(value); + if (typeof(valueToParse) === "object" || valueToParse === undefined) { + return valueToParse; + } + try { + return JSON.parse(valueToParse); + } catch (e) { + throw new ConfigurationError("Make sure the custom expression contains a valid object"); + } + }, + }, + run({ $ }) { + $.export("config", this.parse(this.config)); + }, +}; diff --git a/components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs b/components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs new file mode 100644 index 0000000000000..53be0bf379363 --- /dev/null +++ b/components/pipedream_utils/actions/extract-by-regular-expression/extract-by-regular-expression.mjs @@ -0,0 +1,49 @@ +import buildRegExp from "../../common/text/buildRegExp.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Extract by Regular Expression", + description: "Find a match for a regular expression pattern. Returns all matched groups with start and end position.", + key: "pipedream_utils-extract-by-regular-expression", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Text you would like to find a pattern from", + type: "string", + }, + regExpString: { + label: "Regular Expression", + description: "Enter a string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp)", + type: "string", + }, + }, + methods: { + getRegExp() { + const { regExpString } = this; + return regExpString.startsWith("/") + ? buildRegExp(regExpString, [ + "g", + ]) + : regExpString; + }, + getResult(input) { + return [ + ...input.matchAll(this.getRegExp()), + ].map((match) => ({ + match: match[0], + startPosition: match.index, + endPosition: match.index + match[0].length, + })); + }, + }, + async run({ $ }) { + const input = this.input; + const result = this.getResult(input); + $.export("$summary", result.length + ? `Successfully found ${result.length} matches` + : "No matches found"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs b/components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs new file mode 100644 index 0000000000000..3a60b74680088 --- /dev/null +++ b/components/pipedream_utils/actions/extract-email-address/extract-email-address.mjs @@ -0,0 +1,26 @@ +import commonExtractText from "../../common/text/commonExtractText.mjs"; +export default { + ...commonExtractText, + name: "Formatting - [Text] Extract Email Address", + description: "Find an email address out of a text field. Finds the first email address only.", + key: "pipedream_utils-extract-email-address", + version: "0.0.6", + type: "action", + props: { + ...commonExtractText.props, + input: { + label: "Input", + description: "String from which you'd like to extract an email address", + type: "string", + }, + }, + methods: { + ...commonExtractText.methods, + getRegExp() { + return /[\w.!#$%&'*+-/=?^_`{|}~]+@([\w-]+\.)+[\w-]{2,}/; + }, + getType() { + return "email address"; + }, + }, +}; diff --git a/components/pipedream_utils/actions/extract-number/extract-number.mjs b/components/pipedream_utils/actions/extract-number/extract-number.mjs new file mode 100644 index 0000000000000..e9ca9aa6b5181 --- /dev/null +++ b/components/pipedream_utils/actions/extract-number/extract-number.mjs @@ -0,0 +1,26 @@ +import commonExtractText from "../../common/text/commonExtractText.mjs"; +export default { + ...commonExtractText, + name: "Formatting - [Text] Extract Number", + description: "Find a number out of a text field. Finds the first number only.", + key: "pipedream_utils-extract-number", + version: "0.0.5", + type: "action", + props: { + ...commonExtractText.props, + input: { + label: "Input", + description: "String from which you'd like to extract a number", + type: "string", + }, + }, + methods: { + ...commonExtractText.methods, + getRegExp() { + return /[0-9][0-9.,]*/; + }, + getType() { + return "number"; + }, + }, +}; diff --git a/components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs b/components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs new file mode 100644 index 0000000000000..357edaf920f97 --- /dev/null +++ b/components/pipedream_utils/actions/extract-phone-number/extract-phone-number.mjs @@ -0,0 +1,41 @@ +import commonExtractText from "../../common/text/commonExtractText.mjs"; +export default { + ...commonExtractText, + name: "Formatting - [Text] Extract Phone Number", + description: "Find a complete phone number out of a text field. Finds the first number only.", + key: "pipedream_utils-extract-phone-number", + version: "0.0.5", + type: "action", + props: { + ...commonExtractText.props, + input: { + label: "Input", + description: "String from which you'd like to extract a phone number", + type: "string", + }, + format: { + label: "Phone Number Format", + description: "Choose a phone number format, or use a custom string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp) (without the forward slashes)", + type: "string", + options: [ + { + label: "North American Number Plan (NANP) e.g. `(123) 456-7890` or `123-456-7890`", + value: "((\\([0-9]{3}\\) ?)|[0-9]{3}-)[0-9]{3}-[0-9]{4}", + }, + { + label: "International e.g. `(12) 34-56-78-90`", + value: "\\([0-9]{2}\\) ?([0-9]{2}-){3}[0-9]{2}", + }, + ], + }, + }, + methods: { + ...commonExtractText.methods, + getRegExp() { + return new RegExp(this.format); + }, + getType() { + return "phone number"; + }, + }, +}; diff --git a/components/pipedream_utils/actions/extract-url/extract-url.mjs b/components/pipedream_utils/actions/extract-url/extract-url.mjs new file mode 100644 index 0000000000000..e458bad09e142 --- /dev/null +++ b/components/pipedream_utils/actions/extract-url/extract-url.mjs @@ -0,0 +1,26 @@ +import commonExtractText from "../../common/text/commonExtractText.mjs"; +export default { + ...commonExtractText, + name: "Formatting - [Text] Extract URL", + description: "Find a web URL out of a text field. Finds the first URL only.", + key: "pipedream_utils-extract-url", + version: "0.0.5", + type: "action", + props: { + ...commonExtractText.props, + input: { + label: "Input", + description: "String from which you'd like to extract a URL", + type: "string", + }, + }, + methods: { + ...commonExtractText.methods, + getRegExp() { + return /https?:\/\/[^\s]+\.[^\s]+/; + }, + getType() { + return "URL"; + }, + }, +}; diff --git a/components/pipedream_utils/actions/format-currency/format-currency.mjs b/components/pipedream_utils/actions/format-currency/format-currency.mjs new file mode 100644 index 0000000000000..64af436ebd74d --- /dev/null +++ b/components/pipedream_utils/actions/format-currency/format-currency.mjs @@ -0,0 +1,73 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { CURRENCY_OPTIONS } from "../../common/numbers/currencies.mjs"; +import { CURRENCY_FORMAT_OPTIONS } from "../../common/numbers/currencyFormats.mjs"; +import formatNumber from "../../common/numbers/formatNumber.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Numbers] Format Currency", + description: "Format a number as a currency", + key: "pipedream_utils-format-currency", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Number you would like to format as a currency.", + type: "string", + }, + currency: { + label: "Currency", + description: "Specify the currency to be used for formatting", + type: "string", + options: CURRENCY_OPTIONS, + }, + currencyFormat: { + label: "Currency Format", + description: "Specify the format to be used for the currency formatting. Use the unicode currency symbol `¤` for special formatting options. [Formatting rules can be found here](http://www.unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns)", + type: "string", + options: CURRENCY_FORMAT_OPTIONS, + }, + }, + async run({ $ }) { + const { + currency, currencyFormat, + } = this; + const input = this.input.toString(); + const [ + isoCode, + currencySymbol, + currencyName, + ] = currency.split(" - "); + let result = (currencyFormat.startsWith("¤") && currencySymbol) || ""; + const [ + integer, + decimal, + ] = input.split(input.includes(".") + ? "." + : ","); + if (isNaN(Number(integer))) { + throw new ConfigurationError("**Invalid number** - please check your input."); + } + const numberString = formatNumber(integer, (decimal?.length > 1 + ? decimal + : (decimal ?? "0") + "0"), currencyFormat.includes(",") + ? "," + : ""); + result += numberString; + switch (currencyFormat.match(/¤+$/g)?.[0].length) { + default: + break; + // ¤¤ - ISO currency symbol: USD, BRL, etc. + case 2: + result += ` ${isoCode}`; + break; + // ¤¤¤ - Currency display name: United States dollar, Brazilian real, etc. + case 3: + result += ` ${currencyName}`; + break; + } + $.export("$summary", "Successfully formatted as currency"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs b/components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs new file mode 100644 index 0000000000000..6241653fefdae --- /dev/null +++ b/components/pipedream_utils/actions/format-iso8601-datetime/format-iso8601-datetime.mjs @@ -0,0 +1,25 @@ +// legacy_hash_id: a_nji3no +import moment from "moment"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-format-iso8601-datetime", + name: "Helper Functions - Format ISO8601 Date/Time for Google Sheets", + description: "Use the moment.js npm package to format an ISO8601 date/time as Google Sheets friendly formats. This action exports an object with compound date/time, date-only, and time-only values.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + iso8601: { + type: "string", + label: "ISO 8601 Date/Time", + }, + }, + async run() { + return { + date_time: moment(this.iso8601).format("MM/DD/YYYY h:mm:ss a"), + date: moment(this.iso8601).format("MM/DD/YYYY"), + time: moment(this.iso8601).format("h:mm:ss a"), + }; + }, +}; diff --git a/components/pipedream_utils/actions/format-number/format-number.mjs b/components/pipedream_utils/actions/format-number/format-number.mjs new file mode 100644 index 0000000000000..76d7e823560f2 --- /dev/null +++ b/components/pipedream_utils/actions/format-number/format-number.mjs @@ -0,0 +1,56 @@ +import { ConfigurationError } from "@pipedream/platform"; +import formatNumber from "../../common/numbers/formatNumber.mjs"; +import { + DECIMAL_MARK_OPTIONS, FINAL_FORMAT_OPTIONS, +} from "../../common/numbers/numberFormattingOptions.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Numbers] Format Number", + description: "Format a number to a new style. Does not perform any rounding or padding of the number.", + key: "pipedream_utils-format-number", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Number string you would like to format.", + type: "string", + }, + inputDecimalMark: { + label: "Input Decimal Mark", + description: "The character the input uses to denote the decimal/fractional portion of the number. Defaults to period `.`", + type: "string", + options: DECIMAL_MARK_OPTIONS, + optional: true, + }, + toFormat: { + label: "Output Format", + description: "The format the number will be converted to.", + type: "string", + options: FINAL_FORMAT_OPTIONS, + }, + }, + async run({ $ }) { + const { + inputDecimalMark, toFormat, + } = this; + const input = this.input.toString(); + const decimalMark = inputDecimalMark ?? "."; + const splitInput = input.split(decimalMark); + if (splitInput.length > 2) { + throw new ConfigurationError(`Input has more than one decimal mark (\`${decimalMark}\`). Check if the \`Input Decimal Mark\` prop is set correctly.`); + } + const [ + integer, + decimal, + ] = splitInput; + const [ + groupChar, + decimalChar, + ] = toFormat.split(""); + const result = formatNumber(integer, decimal, groupChar, decimalChar); + $.export("$summary", "Successfully formatted number"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs b/components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs new file mode 100644 index 0000000000000..34844c49b4fe0 --- /dev/null +++ b/components/pipedream_utils/actions/get-coutry-name-by-code-iso/get-coutry-name-by-code-iso.mjs @@ -0,0 +1,273 @@ +// legacy_hash_id: a_njiVEV +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-get-coutry-name-by-code-iso", + name: "Helper Functions - Country name, given code (2-letter)", + description: "Return the country name (in English) when given the 2-letter country code", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + countryCode: { + type: "string", + description: "The 2 letter capitalized country code", + }, + }, + async run() { + const { countryCode } = this; + const isoCountries = { + "AF": "Afghanistan", + "AX": "Aland Islands", + "AL": "Albania", + "DZ": "Algeria", + "AS": "American Samoa", + "AD": "Andorra", + "AO": "Angola", + "AI": "Anguilla", + "AQ": "Antarctica", + "AG": "Antigua And Barbuda", + "AR": "Argentina", + "AM": "Armenia", + "AW": "Aruba", + "AU": "Australia", + "AT": "Austria", + "AZ": "Azerbaijan", + "BS": "Bahamas", + "BH": "Bahrain", + "BD": "Bangladesh", + "BB": "Barbados", + "BY": "Belarus", + "BE": "Belgium", + "BZ": "Belize", + "BJ": "Benin", + "BM": "Bermuda", + "BT": "Bhutan", + "BO": "Bolivia", + "BA": "Bosnia And Herzegovina", + "BW": "Botswana", + "BV": "Bouvet Island", + "BR": "Brazil", + "IO": "British Indian Ocean Territory", + "BN": "Brunei Darussalam", + "BG": "Bulgaria", + "BF": "Burkina Faso", + "BI": "Burundi", + "KH": "Cambodia", + "CM": "Cameroon", + "CA": "Canada", + "CV": "Cape Verde", + "KY": "Cayman Islands", + "CF": "Central African Republic", + "TD": "Chad", + "CL": "Chile", + "CN": "China", + "CX": "Christmas Island", + "CC": "Cocos (Keeling) Islands", + "CO": "Colombia", + "KM": "Comoros", + "CG": "Congo", + "CD": "Congo, Democratic Republic", + "CK": "Cook Islands", + "CR": "Costa Rica", + "CI": "Cote D'Ivoire", + "HR": "Croatia", + "CU": "Cuba", + "CY": "Cyprus", + "CZ": "Czech Republic", + "DK": "Denmark", + "DJ": "Djibouti", + "DM": "Dominica", + "DO": "Dominican Republic", + "EC": "Ecuador", + "EG": "Egypt", + "SV": "El Salvador", + "GQ": "Equatorial Guinea", + "ER": "Eritrea", + "EE": "Estonia", + "ET": "Ethiopia", + "FK": "Falkland Islands (Malvinas)", + "FO": "Faroe Islands", + "FJ": "Fiji", + "FI": "Finland", + "FR": "France", + "GF": "French Guiana", + "PF": "French Polynesia", + "TF": "French Southern Territories", + "GA": "Gabon", + "GM": "Gambia", + "GE": "Georgia", + "DE": "Germany", + "GH": "Ghana", + "GI": "Gibraltar", + "GR": "Greece", + "GL": "Greenland", + "GD": "Grenada", + "GP": "Guadeloupe", + "GU": "Guam", + "GT": "Guatemala", + "GG": "Guernsey", + "GN": "Guinea", + "GW": "Guinea-Bissau", + "GY": "Guyana", + "HT": "Haiti", + "HM": "Heard Island & Mcdonald Islands", + "VA": "Holy See (Vatican City State)", + "HN": "Honduras", + "HK": "Hong Kong", + "HU": "Hungary", + "IS": "Iceland", + "IN": "India", + "ID": "Indonesia", + "IR": "Iran, Islamic Republic Of", + "IQ": "Iraq", + "IE": "Ireland", + "IM": "Isle Of Man", + "IL": "Israel", + "IT": "Italy", + "JM": "Jamaica", + "JP": "Japan", + "JE": "Jersey", + "JO": "Jordan", + "KZ": "Kazakhstan", + "KE": "Kenya", + "KI": "Kiribati", + "KR": "Korea", + "KW": "Kuwait", + "KG": "Kyrgyzstan", + "LA": "Lao People's Democratic Republic", + "LV": "Latvia", + "LB": "Lebanon", + "LS": "Lesotho", + "LR": "Liberia", + "LY": "Libyan Arab Jamahiriya", + "LI": "Liechtenstein", + "LT": "Lithuania", + "LU": "Luxembourg", + "MO": "Macao", + "MK": "Macedonia", + "MG": "Madagascar", + "MW": "Malawi", + "MY": "Malaysia", + "MV": "Maldives", + "ML": "Mali", + "MT": "Malta", + "MH": "Marshall Islands", + "MQ": "Martinique", + "MR": "Mauritania", + "MU": "Mauritius", + "YT": "Mayotte", + "MX": "Mexico", + "FM": "Micronesia, Federated States Of", + "MD": "Moldova", + "MC": "Monaco", + "MN": "Mongolia", + "ME": "Montenegro", + "MS": "Montserrat", + "MA": "Morocco", + "MZ": "Mozambique", + "MM": "Myanmar", + "NA": "Namibia", + "NR": "Nauru", + "NP": "Nepal", + "NL": "Netherlands", + "AN": "Netherlands Antilles", + "NC": "New Caledonia", + "NZ": "New Zealand", + "NI": "Nicaragua", + "NE": "Niger", + "NG": "Nigeria", + "NU": "Niue", + "NF": "Norfolk Island", + "MP": "Northern Mariana Islands", + "NO": "Norway", + "OM": "Oman", + "PK": "Pakistan", + "PW": "Palau", + "PS": "Palestinian Territory, Occupied", + "PA": "Panama", + "PG": "Papua New Guinea", + "PY": "Paraguay", + "PE": "Peru", + "PH": "Philippines", + "PN": "Pitcairn", + "PL": "Poland", + "PT": "Portugal", + "PR": "Puerto Rico", + "QA": "Qatar", + "RE": "Reunion", + "RO": "Romania", + "RU": "Russian Federation", + "RW": "Rwanda", + "BL": "Saint Barthelemy", + "SH": "Saint Helena", + "KN": "Saint Kitts And Nevis", + "LC": "Saint Lucia", + "MF": "Saint Martin", + "PM": "Saint Pierre And Miquelon", + "VC": "Saint Vincent And Grenadines", + "WS": "Samoa", + "SM": "San Marino", + "ST": "Sao Tome And Principe", + "SA": "Saudi Arabia", + "SN": "Senegal", + "RS": "Serbia", + "SC": "Seychelles", + "SL": "Sierra Leone", + "SG": "Singapore", + "SK": "Slovakia", + "SI": "Slovenia", + "SB": "Solomon Islands", + "SO": "Somalia", + "ZA": "South Africa", + "GS": "South Georgia And Sandwich Isl.", + "ES": "Spain", + "LK": "Sri Lanka", + "SD": "Sudan", + "SR": "Suriname", + "SJ": "Svalbard And Jan Mayen", + "SZ": "Swaziland", + "SE": "Sweden", + "CH": "Switzerland", + "SY": "Syrian Arab Republic", + "TW": "Taiwan", + "TJ": "Tajikistan", + "TZ": "Tanzania", + "TH": "Thailand", + "TL": "Timor-Leste", + "TG": "Togo", + "TK": "Tokelau", + "TO": "Tonga", + "TT": "Trinidad And Tobago", + "TN": "Tunisia", + "TR": "Turkey", + "TM": "Turkmenistan", + "TC": "Turks And Caicos Islands", + "TV": "Tuvalu", + "UG": "Uganda", + "UA": "Ukraine", + "AE": "United Arab Emirates", + "GB": "United Kingdom", + "US": "United States", + "UM": "United States Outlying Islands", + "UY": "Uruguay", + "UZ": "Uzbekistan", + "VU": "Vanuatu", + "VE": "Venezuela", + "VN": "Viet Nam", + "VG": "Virgin Islands, British", + "VI": "Virgin Islands, U.S.", + "WF": "Wallis And Futuna", + "EH": "Western Sahara", + "YE": "Yemen", + "ZM": "Zambia", + "ZW": "Zimbabwe", + }; + + if (isoCountries.get(countryCode)) { + return isoCountries[countryCode]; + } else { + return countryCode; + } + }, +}; diff --git a/components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs b/components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs new file mode 100644 index 0000000000000..1c7f13e179301 --- /dev/null +++ b/components/pipedream_utils/actions/get-current-time-in-specific-timezone/get-current-time-in-specific-timezone.mjs @@ -0,0 +1,21 @@ +// legacy_hash_id: a_PNiBGY +import moment from "moment-timezone"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-get-current-time-in-specific-timezone", + name: "Helper Functions - Get Current Time in Timezone", + description: "Returns the current time, tied to this workflow invocation, in the target timezone", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + timezone: { + type: "string", + description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", + }, + }, + async run() { + return moment.tz(new Date(), this.timezone).format(); + }, +}; diff --git a/components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs b/components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs new file mode 100644 index 0000000000000..26bd3bb0dc19d --- /dev/null +++ b/components/pipedream_utils/actions/get-iso-string-n-days-ago/get-iso-string-n-days-ago.mjs @@ -0,0 +1,22 @@ +// legacy_hash_id: a_67il6m +import moment from "moment"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-get-iso-string-n-days-ago", + name: "Helper Functions - Get ISO String N Days Ago", + description: "Returns an ISO string (UTC TZ) N days ago", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + days: { + type: "string", + label: "N Days Ago", + }, + }, + async run({ $ }) { + $.export("date", moment().subtract(this.days, "days") + .toISOString()); + }, +}; diff --git a/components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs b/components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs new file mode 100644 index 0000000000000..980299487fcf1 --- /dev/null +++ b/components/pipedream_utils/actions/get-time-in-specific-timezone/get-time-in-specific-timezone.mjs @@ -0,0 +1,26 @@ +// legacy_hash_id: a_m8ijqa +import moment from "moment-timezone"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-get-time-in-specific-timezone", + name: "Helper Functions - Get Time in Timezone", + description: "Given an ISO 8601 timestamp, and a timezone, convert the time to the target timezone.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + time: { + type: "string", + label: "ISO 8601 Time", + description: "An [ISO 8601 string](https://en.wikipedia.org/wiki/ISO_8601) representing the time you'd like to convert to your target timezone. If this timestamp doesn't have a timezone component, it's assumed to be in UTC.", + }, + timezone: { + type: "string", + description: "The IANA timezone name, e.g. `America/Los_Angeles`. [See the full list here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).", + }, + }, + async run() { + return moment.tz(this.time, this.timezone).format(); + }, +}; diff --git a/components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs b/components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs new file mode 100644 index 0000000000000..fa83158a8fa60 --- /dev/null +++ b/components/pipedream_utils/actions/html-to-markdown/html-to-markdown.mjs @@ -0,0 +1,118 @@ +// legacy_hash_id: a_oViLn2 +import TurndownService from "turndown"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-html-to-markdown", + name: "Helper Functions - HTML to Markdown", + description: "Convert via turndown", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + input: { + type: "string", + label: "HTML", + }, + headingStyle: { + type: "string", + label: "Heading Style", + optional: true, + options: [ + "setext", + "atx", + ], + }, + hr: { + type: "string", + label: "HR", + optional: true, + options: [ + "***", + "===", + "---", + ], + }, + bulletListMarker: { + type: "string", + label: "Bullet List Marker", + optional: true, + options: [ + "-", + "+", + "*", + ], + }, + codeBlockStyle: { + type: "string", + label: "Code Block Style", + optional: true, + options: [ + "indented", + "fenced", + ], + }, + fence: { + type: "string", + label: "Fence", + optional: true, + options: [ + "```", + "~~~", + ], + }, + emDelimiter: { + type: "string", + label: "EM Delimiter", + optional: true, + options: [ + "_", + "*", + ], + }, + strongDelimiter: { + type: "string", + label: "Strong Delimiter", + optional: true, + options: [ + "**", + "__", + ], + }, + linkStyle: { + type: "string", + label: "Link Style", + optional: true, + options: [ + "inlined", + "referenced", + ], + }, + linkReferenceStyle: { + type: "string", + label: "Link Reference Style", + optional: true, + options: [ + "full", + "collapsed", + "shortcut", + ], + }, + }, + async run() { + const converter = new TurndownService({ + headingStyle: this.headingStyle, + hr: this.hr, + bulletListMarker: this.bulletListMarker, + codeBlockStyle: this.codeBlockStyle, + fence: this.fence, + emDelimiter: this.emDelimiter, + strongDelimiter: this.strongDelimiter, + linkStyle: this.linkStyle, + linkReferenceStyle: this.linkReferenceStyle, + }); + const markdown = await converter.turndown(this.input); + + return markdown; + }, +}; diff --git a/components/pipedream_utils/actions/parse-json/parse-json.mjs b/components/pipedream_utils/actions/parse-json/parse-json.mjs new file mode 100644 index 0000000000000..5ff976e2b41a0 --- /dev/null +++ b/components/pipedream_utils/actions/parse-json/parse-json.mjs @@ -0,0 +1,26 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Data] Parse JSON", + description: "Parse a JSON string", + key: "pipedream_utils-parse-json", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "A valid JSON format string to be parsed.", + type: "string", + }, + }, + async run({ $ }) { + try { + const result = JSON.parse(this.input); + $.export("$summary", "Successfully parsed JSON string"); + return result; + } + catch (err) { + throw new Error("Error parsing input as JSON: " + err.toString()); + } + }, +}; diff --git a/components/pipedream_utils/actions/prettify-json/prettify-json.mjs b/components/pipedream_utils/actions/prettify-json/prettify-json.mjs new file mode 100644 index 0000000000000..153bafa7c43b4 --- /dev/null +++ b/components/pipedream_utils/actions/prettify-json/prettify-json.mjs @@ -0,0 +1,22 @@ +// legacy_hash_id: a_A6i7q8 +import set from "lodash.set"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-prettify-json", + name: "Helper Functions - Pretty Print JSON", + description: "Pretty print a JavaScript object or value", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + val: { + type: "string", + label: "Value", + description: "The value to prettify", + }, + }, + async run() { + set(this, "prettifiedValue.val", JSON.stringify(this.val, null, 2)); + }, +}; diff --git a/components/pipedream_utils/actions/random-integer/random-integer.mjs b/components/pipedream_utils/actions/random-integer/random-integer.mjs new file mode 100644 index 0000000000000..693609174c38f --- /dev/null +++ b/components/pipedream_utils/actions/random-integer/random-integer.mjs @@ -0,0 +1,27 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-random-integer", + name: "Helper Functions - Random Integer", + description: "Generate a random integer (whole number). Useful for random delays.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + min: { + type: "integer", + default: 0, + label: "Min", + description: "Lowest possible integer", + }, + max: { + type: "integer", + default: 10000, + label: "Max", + description: "Highest possible integer", + }, + }, + run() { + return Math.floor(Math.random() * (this.max - this.min + 1) + this.min); + }, +}; diff --git a/components/pipedream_utils/actions/random-item-from-list/random-item-from-list.mjs b/components/pipedream_utils/actions/random-item-from-list/random-item-from-list.mjs new file mode 100644 index 0000000000000..69b028b0d79ad --- /dev/null +++ b/components/pipedream_utils/actions/random-item-from-list/random-item-from-list.mjs @@ -0,0 +1,35 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "pipedream_utils-random-item-from-list", + name: "Helper Functions - Random Item(s) from List", + description: + "Returns a randomly selected value(s) from a user-defined list of options.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + list: { + type: "string[]", + label: "List", + description: "List of items. Can pass an array from a previous step.", + }, + quantity: { + type: "integer", + label: "Quantity", + description: "How many items to return.", + default: 1, + min: 1, + }, + }, + run() { + if (this.quantity > this.list.length) { + throw new ConfigurationError("Quantity must be smaller than the list size"); + } + + return [ + ...Array(this.quantity), + ].map(() => this.list.splice(Math.floor(Math.random() * this.list.length), 1)[0]); + }, +}; diff --git a/components/pipedream_utils/actions/replace-text/replace-text.mjs b/components/pipedream_utils/actions/replace-text/replace-text.mjs new file mode 100644 index 0000000000000..7dd9ce59e60ec --- /dev/null +++ b/components/pipedream_utils/actions/replace-text/replace-text.mjs @@ -0,0 +1,61 @@ +import buildRegExp from "../../common/text/buildRegExp.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +const MAX_REPLACES = 10000; +export default { + name: "Formatting - [Text] Replace Text", + description: "Replace all instances of any character, word or phrase in the text with another character, word or phrase.", + key: "pipedream_utils-replace-text", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Text you would like to find and replace values within.", + type: "string", + }, + findText: { + label: "Find Text/Pattern", + description: "The text you would like to search for and replace. This can also be a string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp)", + type: "string", + }, + replaceText: { + label: "Replace Text", + description: "Leave blank to delete the found text", + type: "string", + optional: true, + }, + }, + async run({ $ }) { + const { + input, findText, replaceText, + } = this; + const isRegExp = findText.startsWith("/"); + const expression = isRegExp + ? buildRegExp(findText, [ + "g", + ]) + : findText; + const replaceValue = replaceText ?? ""; + // replaceAll would be optimal, but it is not supported in Node 14.x + let result = input.replace(expression, replaceValue); + if (!isRegExp) { + let counter = 0; + while (result.match(expression)) { + result = result.replace(expression, replaceValue); + if (++counter > MAX_REPLACES) { + throw new Error(`Over ${MAX_REPLACES} replace operations reached - please check your inputs and, if this is expected, search with a regular expression instead of text`); + } + } + } + const matchText = isRegExp + ? [ + ...input.matchAll(expression), + ].length + : input.includes(findText) && "text"; + $.export("$summary", matchText + ? `Successfully replaced ${matchText} matches` + : "No matches found. Input was not modified"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/retrieve-all-rss-stories/retrieve-all-rss-stories.mjs b/components/pipedream_utils/actions/retrieve-all-rss-stories/retrieve-all-rss-stories.mjs new file mode 100644 index 0000000000000..98bc37adacff6 --- /dev/null +++ b/components/pipedream_utils/actions/retrieve-all-rss-stories/retrieve-all-rss-stories.mjs @@ -0,0 +1,39 @@ +// legacy_hash_id: a_Vpi7QO +import Parser from "rss-parser"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-retrieve-all-rss-stories", + name: "Helper Functions - Retrieve all RSS Stories", + description: "Retrieve all stories from one or more RSS feeds.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + rss_feeds: { + type: "any", + description: "The URL(s) of the RSS Feeds", + }, + }, + async run({ $ }) { + let parser = new Parser(); + + let stories = []; + + for (const url of this.rss_feeds) { + let feed = await parser.parseURL(url); + console.log(feed.title); + + feed.items.forEach((item) => { + stories.push(item); + console.log(item.title + ":" + item.link); + }); + } + + if (!stories.length) { + $.flow.exit("No new stories"); + } + + return stories; + }, +}; diff --git a/components/pipedream_utils/actions/retrieve-new-rss-stories/retrieve-new-rss-stories.mjs b/components/pipedream_utils/actions/retrieve-new-rss-stories/retrieve-new-rss-stories.mjs new file mode 100644 index 0000000000000..389deb6b0729d --- /dev/null +++ b/components/pipedream_utils/actions/retrieve-new-rss-stories/retrieve-new-rss-stories.mjs @@ -0,0 +1,45 @@ +// legacy_hash_id: a_k6iY38 +import Parser from "rss-parser"; +import get from "lodash.get"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-retrieve-new-rss-stories", + name: "Helper Functions - Retrieve New RSS Stories", + description: "Gets new stories from a specified RSS feed that have not already been processed.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + db: "$.service.db", + rss_feeds: { + type: "any", + description: "The URL(s) of the RSS Feeds", + }, + }, + async run({ $ }) { + let parser = new Parser(); + + const previouslyPostedStories = get(this, "$checkpoint", []); + let newStories = []; + + for (const url of this.rss_feeds) { + let feed = await parser.parseURL(url); + console.log(feed.title); + + feed.items.forEach((item) => { + if (!previouslyPostedStories.includes(item.link)) { + newStories.push(item); + console.log(item.title + ":" + item.link); + } + }); + } + + if (!newStories.length) { + $.flow.exit("No new stories"); + } + + this.db.set("$checkpoint", previouslyPostedStories.concat(newStories.map((s) => s.link))); + return newStories; + }, +}; diff --git a/components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs b/components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs new file mode 100644 index 0000000000000..c1e3e5d1c272b --- /dev/null +++ b/components/pipedream_utils/actions/schedule-task-in-future/schedule-task-in-future.mjs @@ -0,0 +1,53 @@ +// legacy_hash_id: a_3LiebX +import { axios } from "@pipedream/platform"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-schedule-task-in-future", + name: "Helper Functions - Pipedream Task Scheduler - Schedule Task", + description: "Schedule a task with an existing task scheduler source. See [here](https://github.com/PipedreamHQ/pipedream/blob/master/components/pipedream/sources/new-scheduled-tasks/README.md) for more details.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + numSeconds: { + type: "string", + label: "Num Seconds", + description: "How many seconds in the future would you like to schedule the task?", + }, + secret: { + type: "string", + optional: true, + }, + taskSchedulerURL: { + type: "string", + label: "Task Scheduler URL", + description: "Enter the URL as it appears in the **Endpoint** field of your Task Scheduler source", + }, + message: { + type: "string", + description: "The message / payload to send to your task scheduler. Can be any string or JavaScript object. This message will be emitted by the task scheduler at the specified number of seconds in the future.", + }, + }, + async run({ $ }) { + // N seconds from now + const ts = (new Date(+new Date() + (this.numSeconds * 1000))).toISOString(); + $.export("ts", ts); + + const headers = { + "Content-Type": "application/json", + }; + if (this.secret) { + headers["x-pd-secret"] = this.secret; + } + + return await axios($, { + url: `${this.taskSchedulerURL}/schedule`, + headers, + data: { + timestamp: ts, + message: this.message, + }, + }); + }, +}; diff --git a/components/pipedream_utils/actions/send-email-with-nodemailer/send-email-with-nodemailer.mjs b/components/pipedream_utils/actions/send-email-with-nodemailer/send-email-with-nodemailer.mjs new file mode 100644 index 0000000000000..2ae818933e646 --- /dev/null +++ b/components/pipedream_utils/actions/send-email-with-nodemailer/send-email-with-nodemailer.mjs @@ -0,0 +1,102 @@ +// legacy_hash_id: a_EViLg3 +import nodemailer from "nodemailer"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-send-email-with-nodemailer", + name: "Helper Functions - Send email with Nodemailer", + description: "Sends an email using the nodemailer package", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + host: { + type: "string", + description: "The hostname or IP address of your SMTP server", + }, + port: { + type: "string", + description: "The port your SMTP server listens on (defaults to 587 if is secure is false or 465 if true)", + }, + secure: { + type: "boolean", + description: "If `true` the connection will use TLS when connecting to server. If `false` (the default) then TLS is used if server supports the **STARTTLS** extension. In most cases set this value to `true` if you are connecting to port 465. For port 587 or 25 keep it `false`", + optional: true, + }, + ignoreTLS: { + type: "boolean", + label: "Ignore TLS", + description: "If this is `true` and **secure** is `false` then TLS is not used even if the server supports **STARTTLS** extension", + optional: true, + }, + requireTLS: { + type: "boolean", + label: "Require TLS", + description: "If this is `true` and **secure** is `false` then Nodemailer tries to use **STARTTLS** even if the server does not advertise support for it. If the connection can not be encrypted then message is not sent", + optional: true, + }, + user: { + type: "string", + description: "The username you use to connect to your SMTP server", + }, + pass: { + type: "string", + description: "Your password", + }, + from: { + type: "string", + description: "The email address of the sender. All email addresses can be plain 'sender@server.com' or formatted '\"Sender Name\" '", + }, + to: { + type: "string", + description: "Comma separated list or an array of recipients email addresses that will appear on the To: field", + }, + cc: { + type: "string", + description: "Comma separated list or an array of recipients email addresses that will appear on the Cc: field", + optional: true, + }, + subject: { + type: "string", + description: "The subject of the email", + }, + text: { + type: "string", + description: "The plain text version of the message", + optional: true, + }, + html: { + type: "string", + description: "The HTML version of the message", + optional: true, + }, + }, + async run() { + // See the Nodemailer docs for all options: + // https://nodemailer.com/usage/ + var transporter = nodemailer.createTransport({ + host: this.host, + port: this.port, + secure: this.secure, + ignoreTLS: this.ignoreTLS, + requireTLS: this.requireTLS, + auth: { + user: this.user, + pass: this.pass, + }, + }); + + var mailOptions = { + from: this.from, + to: this.to, + cc: this.cc, + subject: this.subject, + text: (this.text || ""), + html: (this.html || ""), + }; + + var mail = await transporter.sendMail(mailOptions); + console.log("Email sent: " + mail.messageId); + return mail; + }, +}; diff --git a/components/pipedream_utils/actions/send-to-s3/send-to-s3.mjs b/components/pipedream_utils/actions/send-to-s3/send-to-s3.mjs new file mode 100644 index 0000000000000..42799629779da --- /dev/null +++ b/components/pipedream_utils/actions/send-to-s3/send-to-s3.mjs @@ -0,0 +1,34 @@ +// legacy_hash_id: a_Vpi8Rv +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-send-to-s3", + name: "Helper Functions - Send to Amazon S3", + description: "Send data to Amazon S3 using Pipedream's destination integration. See https://docs.pipedream.com/destinations/s3/", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + bucket: { + type: "string", + label: "S3 Bucket", + description: "The name of the S3 bucket you'd like to send data to", + }, + prefix: { + type: "string", + description: "The bucket prefix where you'd like to save data. You must include a trailing slash (for example, \"test/\") if you'd like this prefix to operate like a folder in S3", + optional: true, + }, + payload: { + type: "object", + description: "An object, either a reference to a variable from a previous step (for example, event.body), or a set of hardcoded key-value pairs.", + }, + }, + async run({ $ }) { + $.send.s3({ + bucket: this.bucket, + prefix: this.prefix, + payload: this.payload, + }); + }, +}; diff --git a/components/pipedream_utils/actions/set-default-value/set-default-value.mjs b/components/pipedream_utils/actions/set-default-value/set-default-value.mjs new file mode 100644 index 0000000000000..7008a12718345 --- /dev/null +++ b/components/pipedream_utils/actions/set-default-value/set-default-value.mjs @@ -0,0 +1,31 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Set Default Value", + description: "Return a default value if the text is empty", + key: "pipedream_utils-set-default-value", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Reference a previous step where you'd like to apply a default value in the case the field is empty or undefined. For example, `{{steps.code.$return_value.test}}`", + type: "string", + }, + defaultValue: { + label: "Default Value", + description: "Value to return if the text is empty", + type: "string", + }, + }, + async run({ $ }) { + const { + input, defaultValue, + } = this; + const result = input || defaultValue; + $.export("$summary", input + ? "Checked text - not empty" + : "Replaced empty text with default value"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/split-text/split-text.mjs b/components/pipedream_utils/actions/split-text/split-text.mjs new file mode 100644 index 0000000000000..6ea0b68a2a80e --- /dev/null +++ b/components/pipedream_utils/actions/split-text/split-text.mjs @@ -0,0 +1,66 @@ +import buildRegExp from "../../common/text/buildRegExp.mjs"; +import { + INDEX_ALL_SEGMENTS, SPLIT_TEXT_OPTIONS, +} from "../../common/text/splitTextOptions.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Split Text", + description: "Split the text on a character or word and return one or all segments", + key: "pipedream_utils-split-text", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Text you would like to split", + type: "string", + }, + separator: { + label: "Separator", + description: "Character or word separator to split the text on. This can also be a string representing a [Regular Expression](https://www.w3schools.com/js/js_regexp.asp)", + type: "string", + }, + segmentIndex: { + label: "Segment Index", + type: "integer", + description: "Segment of text to return after splitting. Choose one of the options, or use a custom positive (*nth*-match) or negative (*nth-to-last* match) integer.", + options: SPLIT_TEXT_OPTIONS, + }, + }, + async run({ $ }) { + const { + input, segmentIndex, + } = this; + let { separator } = this; + if (separator.startsWith("/")) { + const regExp = buildRegExp(separator); + separator = input.match(regExp)?.[0]; + } + let summary = "Separator not found - returned unmodified input"; + let result = input; + const arrResults = input.split(separator); + const { length } = arrResults; + if (length > 1) { + summary = `Successfully split text into ${length} segments`; + switch (segmentIndex) { + case INDEX_ALL_SEGMENTS: + result = arrResults; + break; + // this case would not be needed if 0 was accepted as an option + // see issue #5429 + case INDEX_ALL_SEGMENTS * -1: + result = arrResults[0]; + break; + default: + result = + arrResults[segmentIndex < 0 + ? length + segmentIndex + : segmentIndex]; + break; + } + } + $.export("$summary", summary); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/transform-case/transform-case.mjs b/components/pipedream_utils/actions/transform-case/transform-case.mjs new file mode 100644 index 0000000000000..0995093e07338 --- /dev/null +++ b/components/pipedream_utils/actions/transform-case/transform-case.mjs @@ -0,0 +1,40 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { + CASE_OPTIONS, CASE_OPTIONS_PROP, +} from "../../common/text/caseOptions.mjs"; +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Transform Case", + description: "Transform case for a text input", + key: "pipedream_utils-transform-case", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "The text you would like to transform", + type: "string", + }, + operation: { + label: "Operation", + description: "The case operation", + type: "string", + options: CASE_OPTIONS_PROP, + }, + }, + async run({ $ }) { + const { + input, operation, + } = this; + try { + const { outputFn } = CASE_OPTIONS.find(({ value }) => value === operation); + const result = outputFn(input); + $.export("$summary", "Successfully transformed text case"); + return result; + } + catch (err) { + throw new ConfigurationError("**Parse error** - check your input and if the selected operation is correct."); + } + }, +}; diff --git a/components/pipedream_utils/actions/trigger-workflow/trigger-workflow.mjs b/components/pipedream_utils/actions/trigger-workflow/trigger-workflow.mjs new file mode 100644 index 0000000000000..773b864a302e7 --- /dev/null +++ b/components/pipedream_utils/actions/trigger-workflow/trigger-workflow.mjs @@ -0,0 +1,35 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; + +export default { + key: "pipedream_utils-trigger-workflow", + name: "Helper Functions - Trigger Workflow", + description: "Trigger another Pipedream workflow in your workspace.", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + workflowId: { + type: "string", + label: "Workflow ID", + description: "The ID of the workflow to trigger. Workflow IDs are formatted as `p_******` and you can find a workflow’s ID within the workflow builder URL.", + }, + event: { + type: "object", + label: "Event", + description: "The event to be sent to the triggered workflow as the triggering event. In the triggered workflow, you can reference this event object with a custom expression (e.g., `{{steps.trigger.event}}`).", + optional: true, + }, + }, + async run({ $ }) { + const { + workflowId, + event = {}, + } = this; + + const result = await $.flow.trigger(workflowId, event); + + $.export("$summary", `Successfully triggered workflow ID **${workflowId}**`); + + return result; + }, +}; diff --git a/components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs b/components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs new file mode 100644 index 0000000000000..fc326918f87ce --- /dev/null +++ b/components/pipedream_utils/actions/trim-whitespace/trim-whitespace.mjs @@ -0,0 +1,22 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Trim Whitespace", + description: "Removes leading and trailing whitespace", + key: "pipedream_utils-trim-whitespace", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "Text you would like remove leading and trailing whitespace from.", + type: "string", + }, + }, + async run({ $ }) { + const { input } = this; + const result = input.trim(); + $.export("$summary", "Successfully trimmed text"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/url-decode/url-decode.mjs b/components/pipedream_utils/actions/url-decode/url-decode.mjs new file mode 100644 index 0000000000000..d53f04f3dd64f --- /dev/null +++ b/components/pipedream_utils/actions/url-decode/url-decode.mjs @@ -0,0 +1,21 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Decode URL", + description: "Decode a URL string", + key: "pipedream_utils-url-decode", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "A valid URL as a string to be decoded.", + type: "string", + }, + }, + async run({ $ }) { + const result = decodeURIComponent(this.input); + $.export("$summary", "Successfully decoded URL"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/url-encode/url-encode.mjs b/components/pipedream_utils/actions/url-encode/url-encode.mjs new file mode 100644 index 0000000000000..fd65def6c01b7 --- /dev/null +++ b/components/pipedream_utils/actions/url-encode/url-encode.mjs @@ -0,0 +1,21 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + name: "Formatting - [Text] Encode URL", + description: "Encode a string as a URL", + key: "pipedream_utils-url-encode", + version: "0.0.5", + type: "action", + props: { + pipedream_utils, + input: { + label: "Input", + description: "A valid URL as a string to be encoded.", + type: "string", + }, + }, + async run({ $ }) { + const result = encodeURIComponent(this.input); + $.export("$summary", "Successfully encoded URL"); + return result; + }, +}; diff --git a/components/pipedream_utils/actions/xml-to-json/xml-to-json.mjs b/components/pipedream_utils/actions/xml-to-json/xml-to-json.mjs new file mode 100644 index 0000000000000..b6bb752a40a42 --- /dev/null +++ b/components/pipedream_utils/actions/xml-to-json/xml-to-json.mjs @@ -0,0 +1,40 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import { xml2js } from "xml-js"; + +export default { + key: "pipedream_utils-xml-to-json", + name: "Helper Functions - XML to JSON", + description: "See [xml-js](https://github.com/nashwaan/xml-js)", + version: "0.0.1", + type: "action", + props: { + pipedream_utils, + input: { + type: "string", + label: "XML", + description: "XML Input", + }, + compact: { + type: "boolean", + label: "Compact", + description: "More information [here](https://github.com/nashwaan/xml-js#compact-vs-non-compact). Defaults to `true`.", + optional: true, + default: true, + }, + spaces: { + type: "integer", + label: "Spaces", + description: "Space indentation. Defaults to `2`.", + optional: true, + default: 2, + }, + }, + async run({ $ }) { + const json = xml2js(this.input, { + compact: this.compact, + spaces: this.spaces, + }); + $.export("$summary", "Successfully converted XML to JSON"); + return json; + }, +}; diff --git a/components/pipedream_utils/common/date-time/commonDateTime.mjs b/components/pipedream_utils/common/date-time/commonDateTime.mjs new file mode 100644 index 0000000000000..49cc4b23f8e88 --- /dev/null +++ b/components/pipedream_utils/common/date-time/commonDateTime.mjs @@ -0,0 +1,41 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; +import { + DATE_FORMAT_PARSE_MAP, DEFAULT_INPUT_FUNCTION, +} from "./dateFormats.mjs"; +export default { + props: { + pipedream_utils, + inputDate: { + description: "A valid date string, in the format selected in **Input Format**. If the format is not set, Pipedream will attempt to infer it using the parser from [Sugar Date library](https://sugarjs.com/dates/#/Parsing).", + propDefinition: [ + pipedream_utils, + "inputDate", + ], + }, + inputFormat: { + propDefinition: [ + pipedream_utils, + "inputFormat", + ], + }, + }, + methods: { + getDateFromInput(date = this.inputDate, format = this.inputFormat) { + let dateObj; + try { + const inputFn = DATE_FORMAT_PARSE_MAP.get(format)?.inputFn ?? + DEFAULT_INPUT_FUNCTION; + dateObj = inputFn(date); + if (isNaN(dateObj.getFullYear())) + throw new Error("Invalid date"); + } + catch (err) { + throw new ConfigurationError(`**Error** parsing input \`${date}\` ${format + ? `expecting specified format \`${format}\`` + : "- try selecting a format in the **Input Format** prop."}`); + } + return dateObj; + }, + }, +}; diff --git a/components/pipedream_utils/common/date-time/dateFormats.mjs b/components/pipedream_utils/common/date-time/dateFormats.mjs new file mode 100644 index 0000000000000..a79ef6fc4e174 --- /dev/null +++ b/components/pipedream_utils/common/date-time/dateFormats.mjs @@ -0,0 +1,168 @@ +import sugar from "sugar"; +export const DEFAULT_FORMAT_VALUE = "2006-01-22T23:04:05+0000"; +export const DEFAULT_INPUT_FUNCTION = (str) => { + const num = Number(str); + return sugar.Date.create(num * 1000 || str); +}; +// https://tc39.es/ecma402/#table-datetimeformat-components +const DATE_FORMATS = [ + { + value: "Sun Jan 22 23:04:05 +0000 2006", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%a %b %d %H:%M:%S {ZZ} %Y"); + }, + }, + { + value: "January 22 2006 23:04:05", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%B %d %Y %H:%M:%S"); + }, + }, + { + value: "January 22 2006", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%B %d %Y"); + }, + }, + { + value: "Jan 22 2006", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%b %d %Y"); + }, + }, + { + value: "2006-01-22T23:04:05", + outputFn(dateObj) { + return [ + sugar.Date.format(dateObj, "%Y-%m-%d"), + "T", + sugar.Date.format(dateObj, "%H:%M:%S"), + ].join(""); + }, + }, + { + value: "2006-01-22T23:04:05+0000", + outputFn(dateObj) { + return [ + sugar.Date.format(dateObj, "%Y-%m-%d"), + "T", + sugar.Date.format(dateObj, "%H:%M:%S{ZZ}"), + ].join(""); + }, + }, + { + value: "2006-01-22 23:04:05 +0000", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%Y-%m-%d %H:%M:%S {ZZ}"); + }, + }, + { + value: "2006-01-22 23:04", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%Y-%m-%d %H:%M"); + }, + }, + { + value: "2006-01-22", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%Y-%m-%d"); + }, + }, + { + value: "01-22-2006", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%m-%d-%Y"); + }, + }, + { + value: "01/22/2006", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%m/%d/%Y"); + }, + }, + { + value: "01/22/06", + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%m/%d/{yy}"); + }, + }, + { + value: "22-01-2006", + inputFn(str) { + const [ + day, + month, + year, + ] = str.split("-"); + return sugar.Date.create(`${year}-${month}-${day}`, { + fromUTC: true, + }); + }, + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%d-%m-%Y"); + }, + }, + { + value: "22/01/2006", + inputFn(str) { + const [ + day, + month, + year, + ] = str.split("/"); + return sugar.Date.create(`${year}-${month}-${day}`, { + fromUTC: true, + }); + }, + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%d/%m/%Y"); + }, + }, + { + value: "22/01/06", + inputFn(str) { + const [ + day, + month, + year, + ] = str.split("/"); + return sugar.Date.create(`${Number(year) + 2000}-${month}-${day}`, { + fromUTC: true, + }); + }, + outputFn(dateObj) { + return sugar.Date.format(dateObj, "%d/%m/{yy}"); + }, + }, + { + value: "Unix time (seconds) Eg. 1137971045", + inputFn(str) { + return sugar.Date.create(Number(str) * 1000); + }, + outputFn(dateObj) { + return dateObj.valueOf() / 1000; + }, + }, + { + value: "Unix time (milliseconds) Eg. 1137971045000", + inputFn(str) { + return sugar.Date.create(Number(str)); + }, + outputFn(dateObj) { + return dateObj.valueOf(); + }, + }, +]; +const mapData = DATE_FORMATS.map(({ + value, inputFn, outputFn, +}) => { + return [ + value, + { + inputFn, + outputFn, + }, + ]; +}); +export const DATE_FORMAT_PARSE_MAP = new Map(mapData); +export const DATE_FORMAT_OPTIONS = DATE_FORMATS.map(({ value }) => value); diff --git a/components/pipedream_utils/common/date-time/dateTimeUnits.mjs b/components/pipedream_utils/common/date-time/dateTimeUnits.mjs new file mode 100644 index 0000000000000..c77ff41d60a68 --- /dev/null +++ b/components/pipedream_utils/common/date-time/dateTimeUnits.mjs @@ -0,0 +1,16 @@ +const millisecond = 1; +const second = 1000 * millisecond; +const minute = 60 * second; +const hour = 60 * minute; +const day = 24 * hour; +const week = 7 * day; +const year = 365 * day; +export const DATE_TIME_UNITS = { + millisecond, + second, + minute, + hour, + day, + week, + year, +}; diff --git a/components/pipedream_utils/common/numbers/currencies.mjs b/components/pipedream_utils/common/numbers/currencies.mjs new file mode 100644 index 0000000000000..2841ad3c76573 --- /dev/null +++ b/components/pipedream_utils/common/numbers/currencies.mjs @@ -0,0 +1,773 @@ +const CURRENCIES = [ + { + name: "United States dollar", + symbol: "$", + iso: "USD", + }, + { + name: "Euro", + symbol: "€", + iso: "EUR", + }, + { + name: "Japanese yen", + symbol: "¥", + iso: "JPY", + }, + { + name: "Brazilian real", + symbol: "R$", + iso: "BRL", + }, + { + name: "Russian ruble", + symbol: "₽", + iso: "RUB", + }, + { + name: "Sterling", + symbol: "£", + iso: "GBP", + }, + { + name: "Canadian dollar", + symbol: "$", + iso: "CAD", + }, + { + name: "Afghan afghani", + symbol: "Af", + iso: "AFN", + }, + { + name: "Albanian lek", + symbol: "Lek", + iso: "ALL", + }, + { + name: "Algerian dinar", + symbol: "DA", + iso: "DZD", + }, + { + name: "Angolan kwanza", + symbol: "Kz", + iso: "AOA", + }, + { + name: "Eastern Caribbean dollar", + symbol: "$", + iso: "XCD", + }, + { + name: "Argentine peso", + symbol: "$", + iso: "ARS", + }, + { + name: "Armenian dram", + symbol: "֏", + iso: "AMD", + }, + { + name: "Aruban florin", + symbol: "ƒ", + iso: "AWG", + }, + { + name: "Saint Helena pound", + symbol: "£", + iso: "SHP", + }, + { + name: "Australian dollar", + symbol: "$", + iso: "AUD", + }, + { + name: "Azerbaijani manat", + symbol: "₼", + iso: "AZN", + }, + { + name: "Bahamian dollar", + symbol: "$", + iso: "BSD", + }, + { + name: "Bahraini dinar", + symbol: "BD", + iso: "BHD", + }, + { + name: "Bangladeshi taka", + symbol: "৳", + iso: "BDT", + }, + { + name: "Barbadian dollar", + symbol: "$", + iso: "BBD", + }, + { + name: "Belarusian ruble", + symbol: "Rbl", + iso: "BYN", + }, + { + name: "Belize dollar", + symbol: "$", + iso: "BZD", + }, + { + name: "West African CFA franc", + symbol: "Fr", + iso: "XOF", + }, + { + name: "Bermudian dollar", + symbol: "$", + iso: "BMD", + }, + { + name: "Bhutanese ngultrum", + symbol: "Nu", + iso: "BTN", + }, + { + name: "Indian rupee", + symbol: "₹", + iso: "INR", + }, + { + name: "Bolivian boliviano", + symbol: "Bs", + iso: "BOB", + }, + { + name: "Bosnia and Herzegovina convertible mark", + symbol: "KM", + iso: "BAM", + }, + { + name: "Botswana pula", + symbol: "P", + iso: "BWP", + }, + { + name: "Brunei dollar", + symbol: "$", + iso: "BND", + }, + { + name: "Singapore dollar", + symbol: "$", + iso: "SGD", + }, + { + name: "Bulgarian lev", + symbol: "Lev", + iso: "BGN", + }, + { + name: "Burundian franc", + symbol: "Fr", + iso: "BIF", + }, + { + name: "Cambodian riel", + symbol: "CR", + iso: "KHR", + }, + { + name: "Central African CFA franc", + symbol: "Fr", + iso: "XAF", + }, + { + name: "Cape Verdean escudo", + symbol: "$", + iso: "CVE", + }, + { + name: "Cayman Islands dollar", + symbol: "$", + iso: "KYD", + }, + { + name: "Chilean peso", + symbol: "$", + iso: "CLP", + }, + { + name: "Renminbi", + symbol: "¥", + iso: "CNY", + }, + { + name: "Colombian peso", + symbol: "$", + iso: "COP", + }, + { + name: "Comorian franc", + symbol: "Fr", + iso: "KMF", + }, + { + name: "Congolese franc", + symbol: "Fr", + iso: "CDF", + }, + { + name: "New Zealand dollar", + symbol: "$", + iso: "NZD", + }, + { + name: "Costa Rican colón", + symbol: "₡", + iso: "CRC", + }, + { + name: "Cuban peso", + symbol: "$", + iso: "CUP", + }, + { + name: "Netherlands Antillean guilder", + symbol: "ƒ", + iso: "ANG", + }, + { + name: "Czech koruna", + symbol: "Kč", + iso: "CZK", + }, + { + name: "Danish krone", + symbol: "kr", + iso: "DKK", + }, + { + name: "Djiboutian franc", + symbol: "Fr", + iso: "DJF", + }, + { + name: "Dominican peso", + symbol: "$", + iso: "DOP", + }, + { + name: "Egyptian pound", + symbol: "LE", + iso: "EGP", + }, + { + name: "Eritrean nakfa", + symbol: "Nkf", + iso: "ERN", + }, + { + name: "Swazi lilangeni", + symbol: "L", + iso: "SZL", + }, + { + name: "South African rand", + symbol: "R", + iso: "ZAR", + }, + { + name: "Ethiopian birr", + symbol: "Br", + iso: "ETB", + }, + { + name: "Falkland Islands pound", + symbol: "£", + iso: "FKP", + }, + { + name: "Fijian dollar", + symbol: "$", + iso: "FJD", + }, + { + name: "CFP franc", + symbol: "Fr", + iso: "XPF", + }, + { + name: "Gambian dalasi", + symbol: "D", + iso: "GMD", + }, + { + name: "Georgian lari", + symbol: "₾", + iso: "GEL", + }, + { + name: "Ghanaian cedi", + symbol: "₵", + iso: "GHS", + }, + { + name: "Gibraltar pound", + symbol: "£", + iso: "GIP", + }, + { + name: "Guatemalan quetzal", + symbol: "Q", + iso: "GTQ", + }, + { + name: "Guinean franc", + symbol: "Fr", + iso: "GNF", + }, + { + name: "Guyanese dollar", + symbol: "$", + iso: "GYD", + }, + { + name: "Haitian gourde", + symbol: "G", + iso: "HTG", + }, + { + name: "Honduran lempira", + symbol: "L", + iso: "HNL", + }, + { + name: "Hong Kong dollar", + symbol: "$", + iso: "HKD", + }, + { + name: "Hungarian forint", + symbol: "Ft", + iso: "HUF", + }, + { + name: "Icelandic króna", + symbol: "kr", + iso: "ISK", + }, + { + name: "Indonesian rupiah", + symbol: "Rp", + iso: "IDR", + }, + { + name: "Iranian rial", + symbol: "Rl", + iso: "IRR", + }, + { + name: "Iraqi dinar", + symbol: "ID", + iso: "IQD", + }, + { + name: "Israeli new shekel", + symbol: "₪", + iso: "ILS", + }, + { + name: "Jamaican dollar", + symbol: "$", + iso: "JMD", + }, + { + name: "Jordanian dinar", + symbol: "JD", + iso: "JOD", + }, + { + name: "Kazakhstani tenge", + symbol: "₸", + iso: "KZT", + }, + { + name: "Kenyan shilling", + symbol: "Sh", + iso: "KES", + }, + { + name: "North Korean won", + symbol: "₩", + iso: "KPW", + }, + { + name: "South Korean won", + symbol: "₩", + iso: "KRW", + }, + { + name: "Kuwaiti dinar", + symbol: "KD", + iso: "KWD", + }, + { + name: "Kyrgyz som", + symbol: "som", + iso: "KGS", + }, + { + name: "Lao kip", + symbol: "₭", + iso: "LAK", + }, + { + name: "Lebanese pound", + symbol: "LL", + iso: "LBP", + }, + { + name: "Lesotho loti", + symbol: "L", + iso: "LSL", + }, + { + name: "Liberian dollar", + symbol: "$", + iso: "LRD", + }, + { + name: "Libyan dinar", + symbol: "LD", + iso: "LYD", + }, + { + name: "Swiss franc", + symbol: "Fr", + iso: "CHF", + }, + { + name: "Macanese pataca", + symbol: "MOP$", + iso: "MOP", + }, + { + name: "Malagasy ariary", + symbol: "Ar", + iso: "MGA", + }, + { + name: "Malawian kwacha", + symbol: "K", + iso: "MWK", + }, + { + name: "Malaysian ringgit", + symbol: "RM", + iso: "MYR", + }, + { + name: "Maldivian rufiyaa", + symbol: "Rf", + iso: "MVR", + }, + { + name: "Mauritanian ouguiya", + symbol: "UM", + iso: "MRU", + }, + { + name: "Mauritian rupee", + symbol: "Re", + iso: "MUR", + }, + { + name: "Mexican peso", + symbol: "$", + iso: "MXN", + }, + { + name: "Moldovan leu", + symbol: "Leu", + iso: "MDL", + }, + { + name: "Mongolian tögrög", + symbol: "₮", + iso: "MNT", + }, + { + name: "Moroccan dirham", + symbol: "DH", + iso: "MAD", + }, + { + name: "Mozambican metical", + symbol: "Mt", + iso: "MZN", + }, + { + name: "Burmese kyat", + symbol: "K", + iso: "MMK", + }, + { + name: "Namibian dollar", + symbol: "$", + iso: "NAD", + }, + { + name: "Nepalese rupee", + symbol: "Re", + iso: "NPR", + }, + { + name: "Nicaraguan córdoba", + symbol: "C$", + iso: "NIO", + }, + { + name: "Nigerian naira", + symbol: "₦", + iso: "NGN", + }, + { + name: "Macedonian denar", + symbol: "DEN", + iso: "MKD", + }, + { + name: "Turkish lira", + symbol: "₺", + iso: "TRY", + }, + { + name: "Norwegian krone", + symbol: "kr", + iso: "NOK", + }, + { + name: "Omani rial", + symbol: "RO", + iso: "OMR", + }, + { + name: "Pakistani rupee", + symbol: "Re", + iso: "PKR", + }, + { + name: "Panamanian balboa", + symbol: "B/", + iso: "PAB", + }, + { + name: "Papua New Guinean kina", + symbol: "K", + iso: "PGK", + }, + { + name: "Paraguayan guaraní", + symbol: "₲", + iso: "PYG", + }, + { + name: "Peruvian sol", + symbol: "S/", + iso: "PEN", + }, + { + name: "Philippine peso", + symbol: "₱", + iso: "PHP", + }, + { + name: "Polish złoty", + symbol: "zł", + iso: "PLN", + }, + { + name: "Qatari riyal", + symbol: "QR", + iso: "QAR", + }, + { + name: "Romanian leu", + symbol: "Leu", + iso: "RON", + }, + { + name: "Rwandan franc", + symbol: "Fr", + iso: "RWF", + }, + { + name: "Samoan tālā", + symbol: "$", + iso: "WST", + }, + { + name: "São Tomé and Príncipe dobra", + symbol: "Db", + iso: "STN", + }, + { + name: "Saudi riyal", + symbol: "Rl", + iso: "SAR", + }, + { + name: "Serbian dinar", + symbol: "DIN", + iso: "RSD", + }, + { + name: "Seychellois rupee", + symbol: "Re", + iso: "SCR", + }, + { + name: "Sierra Leonean leone", + symbol: "Le", + iso: "SLE", + }, + { + name: "Solomon Islands dollar", + symbol: "$", + iso: "SBD", + }, + { + name: "Somali shilling", + symbol: "Sh", + iso: "SOS", + }, + { + name: "Sri Lankan rupee", + symbol: "Re", + iso: "LKR", + }, + { + name: "Sudanese pound", + symbol: "LS", + iso: "SDG", + }, + { + name: "Surinamese dollar", + symbol: "$", + iso: "SRD", + }, + { + name: "Swedish krona", + symbol: "kr", + iso: "SEK", + }, + { + name: "Syrian pound", + symbol: "LS", + iso: "SYP", + }, + { + name: "New Taiwan dollar", + symbol: "$", + iso: "TWD", + }, + { + name: "Tajikistani somoni", + symbol: "SM", + iso: "TJS", + }, + { + name: "Tanzanian shilling", + symbol: "Sh", + iso: "TZS", + }, + { + name: "Thai baht", + symbol: "฿", + iso: "THB", + }, + { + name: "Tongan pa'anga", + symbol: "T$", + iso: "TOP", + }, + { + name: "Trinidad and Tobago dollar", + symbol: "$", + iso: "TTD", + }, + { + name: "Tunisian dinar", + symbol: "DT", + iso: "TND", + }, + { + name: "Turkmenistani manat", + symbol: "m", + iso: "TMT", + }, + { + name: "Ugandan shilling", + symbol: "Sh", + iso: "UGX", + }, + { + name: "Ukrainian hryvnia", + symbol: "₴", + iso: "UAH", + }, + { + name: "United Arab Emirates dirham", + symbol: "Dh", + iso: "AED", + }, + { + name: "Uruguayan peso", + symbol: "$", + iso: "UYU", + }, + { + name: "Uzbekistani sum", + symbol: "soum", + iso: "UZS", + }, + { + name: "Vanuatu vatu", + symbol: "VT", + iso: "VUV", + }, + { + name: "Venezuelan sovereign bolívar", + symbol: "Bs.S", + iso: "VES", + }, + { + name: "Venezuelan digital bolívar", + symbol: "Bs.D", + iso: "VED", + }, + { + name: "Vietnamese đồng", + symbol: "₫", + iso: "VND", + }, + { + name: "Yemeni rial", + symbol: "Rl", + iso: "YER", + }, + { + name: "Zambian kwacha", + symbol: "K", + iso: "ZMW", + }, +]; +export const CURRENCY_OPTIONS = CURRENCIES.map(({ + name, symbol, iso, +}) => ({ + label: name, + value: `${iso} - ${symbol} - ${name}`, +})); diff --git a/components/pipedream_utils/common/numbers/currencyFormats.mjs b/components/pipedream_utils/common/numbers/currencyFormats.mjs new file mode 100644 index 0000000000000..9c69d8631184f --- /dev/null +++ b/components/pipedream_utils/common/numbers/currencyFormats.mjs @@ -0,0 +1,26 @@ +export const CURRENCY_FORMAT_OPTIONS = [ + { + label: "$1,000.00", + value: "¤#,##0.00", + }, + { + label: "$1,000.00 USD", + value: "¤#,##0.00 ¤¤", + }, + { + label: "$1,000.00 United States dollar", + value: "¤#,##0.00 ¤¤¤", + }, + { + label: "$1000.00", + value: "¤###0.00", + }, + { + label: "1,000.00", + value: "#,##0.00", + }, + { + label: "1000.00", + value: "###0.00", + }, +]; diff --git a/components/pipedream_utils/common/numbers/formatNumber.mjs b/components/pipedream_utils/common/numbers/formatNumber.mjs new file mode 100644 index 0000000000000..d927f6c868b4a --- /dev/null +++ b/components/pipedream_utils/common/numbers/formatNumber.mjs @@ -0,0 +1,15 @@ +export default function (integer, decimal, groupChar = ",", decimalChar = ".") { + const result = []; + if (groupChar) { + for (let i = integer.length; i > 0; i -= 3) { + result.push(groupChar, integer.slice(Math.max(0, i - 3), i)); + } + result.reverse().pop(); + } + else + result.push(integer); + if (decimal) { + result.push(decimalChar + decimal); + } + return result.join(""); +} diff --git a/components/pipedream_utils/common/numbers/numberFormattingOptions.mjs b/components/pipedream_utils/common/numbers/numberFormattingOptions.mjs new file mode 100644 index 0000000000000..00d0482a26dba --- /dev/null +++ b/components/pipedream_utils/common/numbers/numberFormattingOptions.mjs @@ -0,0 +1,28 @@ +export const DECIMAL_MARK_OPTIONS = [ + { + label: "comma", + value: ",", + }, + { + label: "period", + value: ".", + }, +]; +export const FINAL_FORMAT_OPTIONS = [ + { + label: "Comma for grouping & period for decimal", + value: ",.", + }, + { + label: "Period for grouping & comma for decimal", + value: ".,", + }, + { + label: "Space for grouping & period for decimal", + value: " .", + }, + { + label: "Space for grouping & comma for decimal", + value: " ,", + }, +]; diff --git a/components/pipedream_utils/common/text/buildRegExp.mjs b/components/pipedream_utils/common/text/buildRegExp.mjs new file mode 100644 index 0000000000000..001469edb75cf --- /dev/null +++ b/components/pipedream_utils/common/text/buildRegExp.mjs @@ -0,0 +1,14 @@ +import { ConfigurationError } from "@pipedream/platform"; +export default function (str, defaultFlags) { + const end = str.match(/\/[a-z]*$/); + if (!end) { + throw new ConfigurationError("Parse error - invalid regular expression."); + } + str = str.slice(1, end[0].length * -1); + let flags = end[0].split("/")[1] ?? ""; + defaultFlags?.forEach((flag) => { + if (!flags.includes(flag)) + flags += flag; + }); + return new RegExp(str, flags); +} diff --git a/components/pipedream_utils/common/text/caseOptions.mjs b/components/pipedream_utils/common/text/caseOptions.mjs new file mode 100644 index 0000000000000..3b1dd9ff52d89 --- /dev/null +++ b/components/pipedream_utils/common/text/caseOptions.mjs @@ -0,0 +1,48 @@ +import pluralize from "pluralize"; +import { titleCase } from "title-case"; +export const CASE_OPTIONS = [ + { + label: "Capitalize the first character of every word in the string", + value: "Capitalize", + outputFn(str) { + return str + .split(" ") + .map((word) => word[0].toUpperCase() + word.slice(1)) + .join(" "); + }, + }, + { + label: "Convert all characters in the string to titlecase", + value: "Titlecase", + outputFn(str) { + return titleCase(str); + }, + }, + { + label: "Pluralize any English word (frog turns into frogs; child turns into children)", + value: "Pluralize", + outputFn(str) { + return pluralize.plural(str); + }, + }, + { + label: "Capitalize every character in the string", + value: "Uppercase", + outputFn(str) { + return str.toUpperCase(); + }, + }, + { + label: "Convert all characters in the string to lowercase", + value: "Lowercase", + outputFn(str) { + return str.toLowerCase(); + }, + }, +]; +export const CASE_OPTIONS_PROP = CASE_OPTIONS.map(({ + label, value, +}) => ({ + label, + value, +})); diff --git a/components/pipedream_utils/common/text/commonExtractText.mjs b/components/pipedream_utils/common/text/commonExtractText.mjs new file mode 100644 index 0000000000000..535a8188f4ef2 --- /dev/null +++ b/components/pipedream_utils/common/text/commonExtractText.mjs @@ -0,0 +1,25 @@ +import pipedream_utils from "../../pipedream_utils.app.mjs"; +export default { + props: { + pipedream_utils, + }, + methods: { + getRegExp() { + throw new Error("RegExp not implemented for this action!"); + }, + getResult(input) { + return input.match(this.getRegExp())?.[0]; + }, + getType() { + throw new Error("Type not implemented for this action!"); + }, + }, + async run({ $ }) { + const input = this.input; + const result = this.getResult(input); + $.export("$summary", result + ? `Successfully found ${this.getType()} "${result}"` + : `No ${this.getType()} found`); + return result; + }, +}; diff --git a/components/pipedream_utils/common/text/splitTextOptions.mjs b/components/pipedream_utils/common/text/splitTextOptions.mjs new file mode 100644 index 0000000000000..7042db5753449 --- /dev/null +++ b/components/pipedream_utils/common/text/splitTextOptions.mjs @@ -0,0 +1,23 @@ +export const INDEX_ALL_SEGMENTS = 999; +export const SPLIT_TEXT_OPTIONS = [ + { + label: "First", + value: INDEX_ALL_SEGMENTS * -1, // value should be 0, but that is not accepted - see issue #5429 + }, + { + label: "Second", + value: 1, + }, + { + label: "Last", + value: -1, + }, + { + label: "Second to Last", + value: -2, + }, + { + label: "All", + value: INDEX_ALL_SEGMENTS, + }, +]; diff --git a/components/pipedream_utils/package.json b/components/pipedream_utils/package.json index c686559b9c948..9c6d0495f16ec 100644 --- a/components/pipedream_utils/package.json +++ b/components/pipedream_utils/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/pipedream_utils", - "version": "0.0.1", + "version": "0.0.2", "description": "Pipedream Utils Components", "main": "pipedream_utils.app.mjs", "keywords": [ @@ -11,5 +11,25 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.0", + "csv-parse": "^5.5.6", + "html-to-text": "^8.2.1", + "linkedom": "^0.14.26", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "moment": "^2.30.1", + "moment-timezone": "^0.5.47", + "nodemailer": "^6.7.8", + "pluralize": "^8.0.0", + "rss-parser": "^3.12.0", + "showdown": "^2.1.0", + "slackify-html": "^1.0.1", + "streamifier": "^0.1.1", + "sugar": "^2.0.6", + "title-case": "^3.0.3", + "turndown": "^7.2.0", + "xml-js": "^1.6.11" } } diff --git a/components/pipedream_utils/pipedream_utils.app.mjs b/components/pipedream_utils/pipedream_utils.app.mjs index ba4778e220d8c..1c2fad69b323e 100644 --- a/components/pipedream_utils/pipedream_utils.app.mjs +++ b/components/pipedream_utils/pipedream_utils.app.mjs @@ -1,11 +1,33 @@ +import { DATE_FORMAT_OPTIONS } from "./common/date-time/dateFormats.mjs"; + export default { type: "app", app: "pipedream_utils", - propDefinitions: {}, + propDefinitions: { + inputDate: { + label: "Input Date", + description: "A valid date string, in the format selected in `Input Format`. If the format is not set, Pipedream will attempt to infer it from the input. If the input is an integer, it will be treated as a unix timestamp in seconds.", + type: "string", + }, + inputFormat: { + label: "Input Format", + type: "string", + options: DATE_FORMAT_OPTIONS, + description: "The format of the provided date.", + optional: true, + }, + outputFormat: { + label: "Output Format", + type: "string", + options: DATE_FORMAT_OPTIONS, + description: "The format of the output date. If not provided, the input format will be used (default is ISO 8601).", + optional: true, + }, + }, methods: { // this.$auth contains connected account data authKeys() { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/pipedream_utils/pipedream_utils.mjs b/components/pipedream_utils/pipedream_utils.mjs deleted file mode 100644 index b2ee0e99bf2fa..0000000000000 --- a/components/pipedream_utils/pipedream_utils.mjs +++ /dev/null @@ -1,11 +0,0 @@ -export default { - type: "app", - app: "pipedream_utils", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53d62b13085c8..e8ee35d1369ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4225,8 +4225,7 @@ importers: components/extensiv_integration_manager: {} - components/extracta_ai: - specifiers: {} + components/extracta_ai: {} components/eyepop_ai: {} @@ -6880,8 +6879,7 @@ importers: components/kucoin_futures: {} - components/kustomer: - specifiers: {} + components/kustomer: {} components/kvdb: dependencies: @@ -8573,8 +8571,7 @@ importers: components/nusii_proposals: {} - components/nutrient_document_web_services_api: - specifiers: {} + components/nutrient_document_web_services_api: {} components/nutshell: dependencies: @@ -9457,7 +9454,61 @@ importers: components/pipedream_connect: {} components/pipedream_utils: - specifiers: {} + dependencies: + '@pipedream/platform': + specifier: ^3.0.0 + version: 3.0.3 + csv-parse: + specifier: ^5.5.6 + version: 5.6.0 + html-to-text: + specifier: ^8.2.1 + version: 8.2.1 + linkedom: + specifier: ^0.14.26 + version: 0.14.26 + lodash.get: + specifier: ^4.4.2 + version: 4.4.2 + lodash.set: + specifier: ^4.3.2 + version: 4.3.2 + moment: + specifier: ^2.30.1 + version: 2.30.1 + moment-timezone: + specifier: ^0.5.47 + version: 0.5.47 + nodemailer: + specifier: ^6.7.8 + version: 6.9.16 + pluralize: + specifier: ^8.0.0 + version: 8.0.0 + rss-parser: + specifier: ^3.12.0 + version: 3.13.0 + showdown: + specifier: ^2.1.0 + version: 2.1.0 + slackify-html: + specifier: ^1.0.1 + version: 1.0.1 + streamifier: + specifier: ^0.1.1 + version: 0.1.1 + sugar: + specifier: ^2.0.6 + version: 2.0.6 + title-case: + specifier: ^3.0.3 + version: 3.0.3 + turndown: + specifier: ^7.2.0 + version: 7.2.0 + xml-js: + specifier: ^1.6.11 + version: 1.6.11 components/pipedrive: dependencies: @@ -10790,8 +10841,7 @@ importers: components/runpod: {} - components/runsignup: - specifiers: {} + components/runsignup: {} components/runware: dependencies: @@ -17246,6 +17296,9 @@ packages: '@microsoft/tsdoc@0.15.0': resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@mixmark-io/domino@2.2.0': + resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} + '@mongodb-js/saslprep@1.1.9': resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==} @@ -23262,6 +23315,9 @@ packages: html-entities-decoder@1.0.5: resolution: {integrity: sha512-Cc/RSOGlojr7NDw1oXamUQenYBB0f/SISO8QWtRdZkDOmlO/hvbGZMjgyl+6+mh2PKPRrGXUKH4JhCU18LNS2g==} + html-entities@1.4.0: + resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==} + html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} @@ -23296,6 +23352,10 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + htmlparser@1.7.7: + resolution: {integrity: sha512-zpK66ifkT0fauyFh2Mulrq4AqGTucxGtOhZ8OjkbSfcCpkqQEI8qRkY0tSQSJNAQ4HUZkgWaU4fK4EH6SVH9PQ==} + engines: {node: '>=0.1.33'} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -24542,6 +24602,9 @@ packages: lodash.pickby@4.6.0: resolution: {integrity: sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==} + lodash.set@4.3.2: + resolution: {integrity: sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==} + lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} @@ -25163,6 +25226,9 @@ packages: moment-timezone@0.5.46: resolution: {integrity: sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==} + moment-timezone@0.5.47: + resolution: {integrity: sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA==} + moment@2.29.4: resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} @@ -27105,6 +27171,9 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slackify-html@1.0.1: + resolution: {integrity: sha512-9e5Wo8Z2QSORedN6vqImnjIUwaHI8mpjeQQfXBcIcvIewoJ9SGB56MN2FVIPt6ACn+g4gLsQZHeGXwe5VQMnzA==} + slash@2.0.0: resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} engines: {node: '>=6'} @@ -27926,6 +27995,9 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + turndown@7.2.0: + resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} @@ -33264,6 +33336,8 @@ snapshots: '@microsoft/tsdoc@0.15.0': {} + '@mixmark-io/domino@2.2.0': {} + '@mongodb-js/saslprep@1.1.9': dependencies: sparse-bitfield: 3.0.3 @@ -40812,6 +40886,8 @@ snapshots: html-entities-decoder@1.0.5: {} + html-entities@1.4.0: {} + html-entities@2.5.2: optional: true @@ -40856,6 +40932,8 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 + htmlparser@1.7.7: {} + http-cache-semantics@4.1.1: {} http-errors@2.0.0: @@ -42368,6 +42446,8 @@ snapshots: lodash.pickby@4.6.0: {} + lodash.set@4.3.2: {} + lodash.snakecase@4.1.1: {} lodash.sortby@4.7.0: {} @@ -43364,6 +43444,10 @@ snapshots: dependencies: moment: 2.30.1 + moment-timezone@0.5.47: + dependencies: + moment: 2.30.1 + moment@2.29.4: {} moment@2.30.1: {} @@ -46149,6 +46233,11 @@ snapshots: sisteransi@1.0.5: {} + slackify-html@1.0.1: + dependencies: + html-entities: 1.4.0 + htmlparser: 1.7.7 + slash@2.0.0: {} slash@3.0.0: {} @@ -47205,6 +47294,10 @@ snapshots: tunnel@0.0.6: {} + turndown@7.2.0: + dependencies: + '@mixmark-io/domino': 2.2.0 + tweetnacl@0.14.5: {} twilio@3.84.1: