diff --git a/components/dealcloud/actions/common/common-create-update.mjs b/components/dealcloud/actions/common/common-create-update.mjs new file mode 100644 index 0000000000000..b37eb1c12ebde --- /dev/null +++ b/components/dealcloud/actions/common/common-create-update.mjs @@ -0,0 +1,57 @@ +import { convertFieldsToProps } from "../../common/utils.mjs"; +import dealcloud from "../../dealcloud.app.mjs"; + +export default { + methods: { + convertFieldsToProps, + isUpdate() { + return false; + }, + getEntryId() { + return -1; + }, + getRequestData(props) { + return { + storeRequests: Object.entries(props).map(([ + key, + value, + ]) => { + const fieldId = Number(key.split("_")[1]); + return { + entryId: this.getEntryId(), + fieldId, + ignoreNearDups: this.ignoreNearDups, + value, + }; + }), + }; + }, + }, + props: { + dealcloud, + entryTypeId: { + propDefinition: [ + dealcloud, + "entryTypeId", + ], + reloadProps: true, + }, + ignoreNearDups: { + propDefinition: [ + dealcloud, + "ignoreNearDups", + ], + }, + }, + async additionalProps() { + const props = {}; + if (!this.entryTypeId) { + return props; + } + const fields = await this.dealcloud.getEntryTypeFields({ + entryTypeId: this.entryTypeId, + }); + return this.convertFieldsToProps(fields); + }, +}; + diff --git a/components/dealcloud/actions/create-record/create-record.mjs b/components/dealcloud/actions/create-record/create-record.mjs new file mode 100644 index 0000000000000..2cd90020eea99 --- /dev/null +++ b/components/dealcloud/actions/create-record/create-record.mjs @@ -0,0 +1,40 @@ +import commonCreateUpdate from "../common/common-create-update.mjs"; + +export default { + ...commonCreateUpdate, + key: "dealcloud-create-record", + name: "Create Record", + description: "Creates a new record (entry) in DealCloud. [See the documentation](https://api.docs.dealcloud.com/docs/data/cells/postput)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + async run({ $ }) { + /* eslint-disable no-unused-vars */ + const { + dealcloud, + entryTypeId, + ignoreNearDups, + convertFieldsToProps, + isUpdate, + getEntryId, + getRequestData, + ...props + } = this; + /* eslint-enable no-unused-vars */ + + const response = await dealcloud.createEntry({ + $, + entryTypeId, + data: this.getRequestData(props), + }); + + $.export("$summary", "Successfully created record"); + // add id to summary when we know the response schema + return response; + }, +}; + diff --git a/components/dealcloud/actions/delete-records/delete-records.mjs b/components/dealcloud/actions/delete-records/delete-records.mjs new file mode 100644 index 0000000000000..a8c73568e07d4 --- /dev/null +++ b/components/dealcloud/actions/delete-records/delete-records.mjs @@ -0,0 +1,51 @@ +import { checkIdArray } from "../../common/utils.mjs"; +import app from "../../dealcloud.app.mjs"; + +export default { + key: "dealcloud-delete-records", + name: "Delete Record(s)", + description: "Deletes one or more records (entries) from DealCloud. [See the documentation](https://api.docs.dealcloud.com/docs/data/delete)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + entryTypeId: { + propDefinition: [ + app, + "entryTypeId", + ], + }, + entryIds: { + propDefinition: [ + app, + "entryId", + ({ entryTypeId }) => ({ + entryTypeId, + }), + ], + type: "integer[]", + label: "Record ID(s)", + description: "The ID(s) of one or more records (entries) to delete.", + }, + }, + async run({ $ }) { + const entryIds = checkIdArray(this.entryIds).map(Number); + const response = await this.app.deleteEntry({ + $, + entryTypeId: this.entryTypeId, + data: entryIds, + }); + + const count = response.length; + $.export("$summary", `Successfully deleted ${count} record${count === 1 + ? "" + : "s"}`); + return response; + }, +}; + diff --git a/components/dealcloud/actions/get-records/get-records.mjs b/components/dealcloud/actions/get-records/get-records.mjs new file mode 100644 index 0000000000000..4a9013e871f87 --- /dev/null +++ b/components/dealcloud/actions/get-records/get-records.mjs @@ -0,0 +1,55 @@ +import { checkIdArray } from "../../common/utils.mjs"; +import dealcloud from "../../dealcloud.app.mjs"; + +export default { + key: "dealcloud-get-records", + name: "Get Record(s)", + description: "Retrieves one or more records (entries) from DealCloud. [See the documentation](https://api.docs.dealcloud.com/docs/data/rows/get)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + dealcloud, + entryTypeId: { + propDefinition: [ + dealcloud, + "entryTypeId", + ], + }, + entryIds: { + propDefinition: [ + dealcloud, + "entryId", + ({ entryTypeId }) => ({ + entryTypeId, + }), + ], + type: "integer[]", + label: "Record ID(s)", + description: "The ID(s) of one or more records (entries) to retrieve.", + }, + }, + async run({ $ }) { + const entryIds = checkIdArray(this.entryIds); + const response = await this.dealcloud.queryEntries({ + $, + entryTypeId: this.entryTypeId, + params: { + query: `{entryid: {$in:[${entryIds.join()}]}}`, + resolveReferenceUrls: true, + wrapIntoArrays: true, + }, + }); + + const count = response.length; + $.export("$summary", `Successfully retrieved ${count} record${count === 1 + ? "" + : "s"}`); + return response; + }, +}; + diff --git a/components/dealcloud/actions/update-record/update-record.mjs b/components/dealcloud/actions/update-record/update-record.mjs new file mode 100644 index 0000000000000..d9cc54241149c --- /dev/null +++ b/components/dealcloud/actions/update-record/update-record.mjs @@ -0,0 +1,69 @@ +import commonCreateUpdate from "../common/common-create-update.mjs"; + +const { + props: { + dealcloud, entryTypeId, ignoreNearDups, + }, +} = commonCreateUpdate; + +export default { + ...commonCreateUpdate, + key: "dealcloud-update-record", + name: "Update Record", + description: "Updates a record (entry) in DealCloud. [See the documentation](https://api.docs.dealcloud.com/docs/data/cells/postput)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + dealcloud, + entryTypeId, + entryId: { + propDefinition: [ + dealcloud, + "entryId", + ({ entryTypeId }) => ({ + entryTypeId, + }), + ], + }, + ignoreNearDups, + }, + methods: { + ...commonCreateUpdate.methods, + isUpdate() { + return true; + }, + getEntryId() { + return this.entryId; + }, + }, + async run({ $ }) { + /* eslint-disable no-unused-vars */ + const { + dealcloud, + entryTypeId, + entryId, + ignoreNearDups, + convertFieldsToProps, + isUpdate, + getEntryId, + getRequestData, + ...props + } = this; + /* eslint-enable no-unused-vars */ + + const response = await dealcloud.updateEntry({ + $, + entryTypeId, + data: this.getRequestData(props), + }); + + $.export("$summary", "Successfully updated record"); + return response; + }, +}; + diff --git a/components/dealcloud/common/constants.mjs b/components/dealcloud/common/constants.mjs new file mode 100644 index 0000000000000..b0fc37cfab801 --- /dev/null +++ b/components/dealcloud/common/constants.mjs @@ -0,0 +1,154 @@ +export const CURRENCY_OPTIONS = [ + "AED", + "AFN", + "ALL", + "AMD", + "ANG", + "AOA", + "ARS", + "AUD", + "AWG", + "AZN", + "BAM", + "BBD", + "BDT", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BRL", + "BSD", + "BTN", + "BWP", + "BYN", + "BZD", + "CAD", + "CDF", + "CHF", + "CLP", + "CNH", + "CNY", + "COP", + "CRC", + "CUC", + "CUP", + "CVE", + "CZK", + "DJF", + "DKK", + "DOP", + "DZD", + "EEK", + "EGP", + "ERN", + "ETB", + "EUR", + "FJD", + "FKP", + "GBP", + "GEL", + "GHS", + "GIP", + "GMD", + "GNF", + "GTQ", + "GYD", + "HKD", + "HNL", + "HRK", + "HTG", + "HUF", + "IDR", + "ILS", + "INR", + "IQD", + "IRR", + "ISK", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KRW", + "KWD", + "KYD", + "KZT", + "LAK", + "LBP", + "LKR", + "LRD", + "LSL", + "LTL", + "LVL", + "LYD", + "MAD", + "MDL", + "MGA", + "MKD", + "MMK", + "MNT", + "MOP", + "MRU", + "MUR", + "MVR", + "MWK", + "MXN", + "MYR", + "MZN", + "NAD", + "NGN", + "NIO", + "NLG", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEN", + "PGK", + "PHP", + "PKR", + "PLN", + "PYG", + "QAR", + "RON", + "RSD", + "RUB", + "RWF", + "SAR", + "SBD", + "SCR", + "SDG", + "SEK", + "SGD", + "SLL", + "SRD", + "SYP", + "SZL", + "THB", + "TJS", + "TMT", + "TND", + "TOP", + "TRY", + "TTD", + "TWD", + "TZS", + "UAH", + "UGX", + "USD", + "UYU", + "UZS", + "VND", + "VUV", + "WST", + "XAF", + "XCD", + "XOF", + "ZAR", + "ZMW", +]; diff --git a/components/dealcloud/common/utils.mjs b/components/dealcloud/common/utils.mjs new file mode 100644 index 0000000000000..763e1e9bc443f --- /dev/null +++ b/components/dealcloud/common/utils.mjs @@ -0,0 +1,97 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { CURRENCY_OPTIONS } from "./constants.mjs"; + +export function checkIdArray(value) { + if (typeof value === "string") { + return value.split(",").map((id) => id.trim()) + .filter(Boolean); + } + if (Array.isArray(value)) { + return value; + } + throw new ConfigurationError("Invalid ID array input: " + JSON.stringify(value)); +} + +const FIELD_TYPES = { + TEXT: 1, + CHOICE: 2, + NUMBER: 3, + DATE: 4, + REFERENCE: 5, + BOOLEAN: 6, + USER: 7, + BINARY: 13, + ENTRY_LIST_ID: 14, + COUNTER: 15, + IMAGE: 16, + DATA_SOURCE: 17, + CURRENCY: 18, +}; + +/** + * Maps DealCloud field type ID to Pipedream prop type + * @param {number} fieldTypeId - The DealCloud field type ID + * @param {boolean} isArray - Whether the field is an array + * @returns {string} The corresponding Pipedream prop type + */ +function mapFieldTypeToPropType(fieldTypeId, isArray) { + switch (fieldTypeId) { + case FIELD_TYPES.NUMBER: + return isArray + ? "integer[]" + : "integer"; + case FIELD_TYPES.BOOLEAN: + return "boolean"; + default: + return isArray + ? "string[]" + : "string"; + } +} + +/** + * @typedef {Object} DealCloudField + * @property {string} apiName - The API name of the field + * @property {number} fieldType - The type of the field (numeric code) + * @property {boolean} isRequired - Whether the field is required + * @property {boolean} isMoney - Whether the field represents a monetary value + * @property {boolean} isMultiSelect - Whether the field allows multiple selections + * @property {Array} entryLists - List of entry options for the field + * @property {number} systemFieldType - The system field type (numeric code) + * @property {boolean} isKey - Whether the field is a key field + * @property {boolean} isCalculated - Whether the field is calculated + * @property {number} id - The unique identifier of the field + * @property {string} name - The display name of the field + * @property {number} entryListId - The ID of the associated entry list + */ + +/** + * Converts DealCloud fields to Pipedream prop format + * @param {DealCloudField[]} fields - Array of DealCloud field objects + * @returns Object to be returned in dynamic props + */ +export function convertFieldsToProps(fields) { + return fields.reduce((acc, field) => { + const { fieldType } = field; + const prop = { + label: field.name, + type: mapFieldTypeToPropType(fieldType, field.isMultiSelect), + description: `Field ID: \`${field.id}\`. Field type: \`${field.fieldType}\``, + optional: this.isUpdate() + ? true + : !field.isRequired, + }; + + if (fieldType === FIELD_TYPES.CURRENCY) { + prop.options = CURRENCY_OPTIONS; + } + + if (field.entryLists?.length) { + // TODO: add dynamic options for entry lists + prop.description = prop.description + ` Entry List ID: \`${field.entryLists[0]}\``; + } + + acc[`field_${field.id}`] = prop; + return acc; + }, {}); +} diff --git a/components/dealcloud/dealcloud.app.mjs b/components/dealcloud/dealcloud.app.mjs index b5eecaa43f3d6..3d02b0fbfa977 100644 --- a/components/dealcloud/dealcloud.app.mjs +++ b/components/dealcloud/dealcloud.app.mjs @@ -1,11 +1,120 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "dealcloud", - propDefinitions: {}, + propDefinitions: { + entryTypeId: { + type: "integer", + label: "Object ID", + description: "The ID of the object (entry type) to use.", + async options() { + const results = await this.listEntryTypes(); + return results.map(({ + id: value, name: label, + }) => ({ + value, + label, + })); + }, + }, + entryId: { + type: "integer", + label: "Record ID", + description: "The ID of the record (entry) to use.", + async options({ entryTypeId }) { + const results = await this.getAllEntryTypeEntries({ + entryTypeId, + }); + return results.map((entry) => { + const value = entry.EntryId || entry.Id || entry.id; + const label = entry.Name || value; + return { + label, + value, + }; + }); + }, + }, + ignoreNearDups: { + type: "boolean", + label: "Ignore Near Duplicates", + description: "Whether to ignore near duplicates when creating a record.", + optional: true, + default: false, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + async _makeRequest({ + $ = this, headers, ...args + }) { + const config = { + baseURL: `${this.$auth.host}/api/rest/v4`, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...args, + }; + return axios($, config); + }, + async listEntryTypes(args = {}) { + return this._makeRequest({ + url: "/schema/entrytypes", + ...args, + }); + }, + async getEntryTypeFields({ + entryTypeId, ...args + }) { + return this._makeRequest({ + url: `/schema/entrytypes/${entryTypeId}/fields`, + ...args, + }); + }, + async getAllEntryTypeEntries({ + entryTypeId, ...args + }) { + return this._makeRequest({ + url: `/data/entrydata/${entryTypeId}/entries`, + ...args, + }); + }, + async queryEntries({ + entryTypeId, ...args + }) { + const response = await this._makeRequest({ + url: `/data/entrydata/rows/${entryTypeId}`, + ...args, + }); + return response?.rows || []; + }, + async createEntry({ + entryTypeId, ...args + }) { + return this._makeRequest({ + url: `/data/entrydata/${entryTypeId}`, + method: "POST", + ...args, + }); + }, + async updateEntry({ + entryTypeId, ...args + }) { + return this._makeRequest({ + url: `/data/entrydata/${entryTypeId}`, + method: "PUT", + ...args, + }); + }, + async deleteEntry({ + entryTypeId, ...args + }) { + return this._makeRequest({ + url: `/data/entrydata/${entryTypeId}`, + method: "DELETE", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/dealcloud/package.json b/components/dealcloud/package.json index c5f4f574ea809..fbb640aca692f 100644 --- a/components/dealcloud/package.json +++ b/components/dealcloud/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/dealcloud", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream DealCloud Components", "main": "dealcloud.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.1" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1499dc63d45f2..4137752ce7af3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3814,7 +3814,10 @@ importers: version: 3.1.1 components/dealcloud: - specifiers: {} + dependencies: + '@pipedream/platform': + specifier: ^3.1.1 + version: 3.1.1 components/dealmachine: dependencies: @@ -31496,17 +31499,17 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} @@ -39130,7 +39133,6 @@ snapshots: transitivePeerDependencies: - rolldown - rollup - - supports-color '@putout/operator-parens@2.0.0(rolldown@1.0.0-beta.9)(rollup@4.53.2)': dependencies: