diff --git a/components/data_stores/actions/add-update-multiple-records/add-update-multiple-records.mjs b/components/data_stores/actions/add-update-multiple-records/add-update-multiple-records.mjs index 9962c0f4d0a0b..33095b7aebff7 100644 --- a/components/data_stores/actions/add-update-multiple-records/add-update-multiple-records.mjs +++ b/components/data_stores/actions/add-update-multiple-records/add-update-multiple-records.mjs @@ -1,11 +1,10 @@ import app from "../../data_stores.app.mjs"; -import xss from "xss"; export default { key: "data_stores-add-update-multiple-records", name: "Add or update multiple records", description: "Add or update multiple records to your [Pipedream Data Store](https://pipedream.com/data-stores/).", - version: "0.0.4", + version: "0.0.5", type: "action", props: { app, @@ -53,13 +52,7 @@ export default { } } - // Try to evaluate string as javascript, using xss as extra security - // If some problem occurs, return the original string - try { - return eval(`(${xss(value)})`); - } catch { - return value; - } + return this.app.evaluate(value); }, /** * Add all the key-value pairs in the map object to be used in the data store @@ -68,8 +61,7 @@ export default { */ populateHashMapOfData(data, map) { if (!Array.isArray(data) && typeof(data) === "object") { - Object.keys(data) - .forEach((key) => map[key] = this.convertString(data[key])); + Object.keys(data).forEach((key) => map[key] = this.convertString(data[key])); return; } @@ -87,7 +79,7 @@ export default { }, async run({ $ }) { if (typeof this.data === "string") { - this.data = eval(`(${this.data})`); + this.data = this.app.evaluate(this.data); } const map = this.getHashMapOfData(this.data); const keys = Object.keys(map); @@ -96,7 +88,9 @@ export default { if (keys.length === 0) { $.export("$summary", "No data was added to the data store."); } else { - $.export("$summary", `Successfully added or updated ${keys.length} record(s)`); + // eslint-disable-next-line multiline-ternary + $.export("$summary", `Successfully added or updated ${keys.length} record${keys.length === 1 ? "" : "s"}`); } + return map; }, }; diff --git a/components/data_stores/actions/add-update-record/add-update-record.mjs b/components/data_stores/actions/add-update-record/add-update-record.mjs index eb95e55340c71..bcd54ac4cf255 100644 --- a/components/data_stores/actions/add-update-record/add-update-record.mjs +++ b/components/data_stores/actions/add-update-record/add-update-record.mjs @@ -4,7 +4,7 @@ export default { key: "data_stores-add-update-record", name: "Add or update a single record", description: "Add or update a single record in your [Pipedream Data Store](https://pipedream.com/data-stores/).", - version: "0.0.7", + version: "0.0.8", type: "action", props: { app, @@ -25,9 +25,10 @@ export default { description: "Enter a key for the record you'd like to create or select an existing key to update.", }, value: { - label: "Value", - type: "any", - description: "Enter a string, object, or array.", + propDefinition: [ + app, + "value", + ], }, }, async run({ $ }) { @@ -35,14 +36,14 @@ export default { key, value, } = this; - const record = await this.dataStore.get(key); + const exists = await this.dataStore.has(key); const parsedValue = this.app.parseValue(value); await this.dataStore.set(key, parsedValue); // eslint-disable-next-line multiline-ternary - $.export("$summary", `Successfully ${record ? "updated the record for" : "added a new record with the"} key, \`${key}\`.`); + $.export("$summary", `Successfully ${exists ? "updated the record for" : "added a new record with the"} key, \`${key}\`.`); return { key, - value, + value: parsedValue, }; }, }; diff --git a/components/data_stores/actions/delete-single-record/delete-single-record.mjs b/components/data_stores/actions/delete-single-record/delete-single-record.mjs index 0f001894dece2..daabe2a7a3fe2 100644 --- a/components/data_stores/actions/delete-single-record/delete-single-record.mjs +++ b/components/data_stores/actions/delete-single-record/delete-single-record.mjs @@ -4,7 +4,7 @@ export default { key: "data_stores-delete-single-record", name: "Delete a single record", description: "Delete a single record in your [Pipedream Data Store](https://pipedream.com/data-stores/).", - version: "0.0.6", + version: "0.0.7", type: "action", props: { app, @@ -30,9 +30,10 @@ export default { if (record) { await this.dataStore.delete(this.key); - $.export("$summary", "Successfully deleted the record for key, `" + this.key + "`."); - } else { - $.export("$summary", "No record found for key, `" + this.key + "`. No data was deleted."); + $.export("$summary", `Successfully deleted the record for key, \`${this.key}\`.`); + return record; } + + $.export("$summary", `No record found for key, \`${this.key}\`. No data was deleted.`); }, }; diff --git a/components/data_stores/actions/get-record-or-create/get-record-or-create.mjs b/components/data_stores/actions/get-record-or-create/get-record-or-create.mjs index 941ebda62053d..643efddcdbaca 100644 --- a/components/data_stores/actions/get-record-or-create/get-record-or-create.mjs +++ b/components/data_stores/actions/get-record-or-create/get-record-or-create.mjs @@ -4,7 +4,7 @@ export default { key: "data_stores-get-record-or-create", name: "Get record (or create one if not found)", description: "Get a single record in your [Pipedream Data Store](https://pipedream.com/data-stores/) or create one if it doesn't exist.", - version: "0.0.7", + version: "0.0.8", type: "action", props: { app, @@ -32,27 +32,28 @@ export default { }, }, async additionalProps() { + const props = {}; if (this.app.shouldAddRecord(this.addRecordIfNotFound)) { - return this.app.valueProp(); + props.value = app.propDefinitions.value; } - return {}; + return props; }, async run({ $ }) { const record = await this.dataStore.get(this.key); if (record) { - $.export("$summary", "Found data for the key, `" + this.key + "`."); - } else { - if (this.app.shouldAddRecord(this.addRecordIfNotFound)) { - const parsedValue = this.app.parseValue(this.value); - await this.dataStore.set(this.key, parsedValue); - $.export("$summary", "Successfully added a new record with the key, `" + this.key + "`."); - return this.dataStore.get(this.key); - } else { - $.export("$summary", "No data found for key, `" + this.key + "`."); - } + $.export("$summary", `Found data for the key, \`${this.key}\`.`); + return record; } - return record; + if (!this.app.shouldAddRecord(this.addRecordIfNotFound)) { + $.export("$summary", `No data found for key, \`${this.key}\`.`); + return; + } + + const parsedValue = this.app.parseValue(this.value); + await this.dataStore.set(this.key, parsedValue); + $.export("$summary", `Successfully added a new record with the key, \`${this.key}\`.`); + return parsedValue; }, }; diff --git a/components/data_stores/actions/has-key-or-create/has-key-or-create.mjs b/components/data_stores/actions/has-key-or-create/has-key-or-create.mjs index 12c4e40a790a4..72237b9cbb8d0 100644 --- a/components/data_stores/actions/has-key-or-create/has-key-or-create.mjs +++ b/components/data_stores/actions/has-key-or-create/has-key-or-create.mjs @@ -4,7 +4,7 @@ export default { key: "data_stores-has-key-or-create", name: "Check for existence of key", description: "Check if a key exists in your [Pipedream Data Store](https://pipedream.com/data-stores/) or create one if it doesn't exist.", - version: "0.0.3", + version: "0.0.4", type: "action", props: { app, @@ -32,25 +32,26 @@ export default { }, }, async additionalProps() { + const props = {}; if (this.app.shouldAddRecord(this.addRecordIfNotFound)) { - return this.app.valueProp(); + props.value = app.propDefinitions.value; } - return {}; + return props; }, async run ({ $ }) { if (await this.dataStore.has(this.key)) { - $.export("$summary", `Key "${this.key}" exists.`); + $.export("$summary", `Key \`${this.key}\` exists.`); return true; } - if (this.app.shouldAddRecord(this.addRecordIfNotFound)) { - const parsedValue = this.app.parseValue(this.value); - await this.dataStore.set(this.key, parsedValue); - $.export("$summary", `Key "${this.key}" was not found. Successfully added a new record.`); - return this.dataStore.get(this.key); + if (!this.app.shouldAddRecord(this.addRecordIfNotFound)) { + $.export("$summary", `Key \`${this.key}\` does not exist.`); + return false; } - $.export("$summary", `Key "${this.key}" does not exist.`); - return false; + const parsedValue = this.app.parseValue(this.value); + await this.dataStore.set(this.key, parsedValue); + $.export("$summary", `Key \`${this.key}\` was not found. Successfully added a new record.`); + return parsedValue; }, }; diff --git a/components/data_stores/data_stores.app.mjs b/components/data_stores/data_stores.app.mjs index 7a6d54b83abf3..2bf4ed49291d4 100644 --- a/components/data_stores/data_stores.app.mjs +++ b/components/data_stores/data_stores.app.mjs @@ -1,3 +1,10 @@ +import xss from "xss"; + +/** + * Should support the following data types: + * https://pipedream.com/docs/data-stores/#supported-data-types + */ + export default { type: "app", app: "data_stores", @@ -15,6 +22,11 @@ export default { return dataStore.keys(); }, }, + value: { + label: "Value", + type: "any", + description: "Enter a string, object, or array.", + }, addRecordIfNotFound: { label: "Create a new record if the key is not found?", description: "Create a new record if no records are found for the specified key.", @@ -28,36 +40,31 @@ export default { }, }, methods: { + // using Function approach instead of eval: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval! + evaluate(value) { + try { + return Function(`"use strict"; return (${xss(value)})`)(); + } catch (err) { + return value; + } + }, + parseJSON(value) { + return JSON.parse(JSON.stringify(this.evaluate(value))); + }, shouldAddRecord(option) { return option === "Yes"; }, - valueProp() { - return { - value: { - label: "Value", - type: "any", - description: "Enter a string, object, or array.", - }, - }; - }, parseValue(value) { if (typeof value !== "string") { return value; } try { - return JSON.parse(this.sanitizeJson(value)); + return this.parseJSON(value); } catch (err) { return value; } }, - //Because user might enter a JSON as JS object; - //This method converts a JS object string to a JSON string before parsing it - //e.g. {a:"b", 'c':1} => {"a":"b", "c":1} - //We don't use eval here because of security reasons. - //Using eval may cause something undesired run in the script. - sanitizeJson(str) { - return str.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, "\"$2\": "); - }, }, };