Skip to content

Commit

Permalink
New Components - ironclad (#14861)
Browse files Browse the repository at this point in the history
* ironclad init

* wip

* pnpm-lock.yaml

* new components

* updates

* pnpm-lock.yaml

* add invalid_param error handling
  • Loading branch information
michelle0927 authored Dec 11, 2024
1 parent e313fb6 commit 49ec0cb
Show file tree
Hide file tree
Showing 15 changed files with 791 additions and 12 deletions.
103 changes: 103 additions & 0 deletions components/ironclad/actions/create-record/create-record.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import ironclad from "../../ironclad.app.mjs";

export default {
key: "ironclad-create-record",
name: "Create Record",
description: "Creates a new record in Ironclad. [See the documentation](https://developer.ironcladapp.com/reference/create-a-record)",
version: "0.0.1",
type: "action",
props: {
ironclad,
name: {
type: "string",
label: "Name",
description: "Name of the record",
},
type: {
propDefinition: [
ironclad,
"recordType",
],
},
links: {
propDefinition: [
ironclad,
"recordId",
],
type: "string[]",
label: "Links",
description: "Record ID's to link to the new record",
},
parent: {
propDefinition: [
ironclad,
"recordId",
],
label: "Parent",
description: "Record ID to be set as the parent of the current record",
},
children: {
propDefinition: [
ironclad,
"recordId",
],
type: "string[]",
label: "Children",
description: "Record ID's to be set as child records of the current record",
},
properties: {
propDefinition: [
ironclad,
"properties",
],
reloadProps: true,
},
},
async additionalProps() {
const props = {};
if (!this.properties?.length) {
return props;
}
const { properties } = await this.ironclad.getRecordsSchema();
for (const property of this.properties) {
props[property] = {
type: properties[property].type === "boolean"
? "boolean"
: "string",
label: properties[property].displayName,
description: properties[property].description ?? `Value of ${properties[property].displayName}`,
};
}
return props;
},
async run({ $ }) {
const { properties } = await this.ironclad.getRecordsSchema();
const propertiesData = {};
for (const property of this.properties) {
propertiesData[property] = {
type: properties[property].type,
value: this[property],
};
}

const response = await this.ironclad.createRecord({
$,
data: {
name: this.name,
type: this.type,
links: this.links?.length && this.links.map((link) => ({
recordId: link,
})),
parent: this.parent && {
recordId: this.parent,
},
children: this.children?.length && this.children.map((child) => ({
recordId: child,
})),
properties: propertiesData,
},
});
$.export("$summary", `Created record with ID: ${response.id}`);
return response;
},
};
105 changes: 105 additions & 0 deletions components/ironclad/actions/launch-workflow/launch-workflow.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import ironclad from "../../ironclad.app.mjs";
import { ConfigurationError } from "@pipedream/platform";
import {
getAttributeDescription, parseValue,
} from "../../common/utils.mjs";

export default {
key: "ironclad-launch-workflow",
name: "Launch Workflow",
description: "Launches a new workflow in Ironclad. [See the documentation](https://developer.ironcladapp.com/reference/launch-a-new-workflow)",
version: "0.0.1",
type: "action",
props: {
ironclad,
templateId: {
propDefinition: [
ironclad,
"templateId",
],
reloadProps: true,
},
},
async additionalProps() {
const props = {};
if (!this.templateId) {
return props;
}
const { schema } = await this.ironclad.getWorkflowSchema({
templateId: this.templateId,
});
for (const [
key,
value,
] of Object.entries(schema)) {
if (!value.readOnly) {
props[key] = {
type: value.type === "boolean"
? "boolean"
: value.type === "array"
? "string[]"
: "string",
label: value.displayName,
description: getAttributeDescription(value),
optional: (!(key === "counterpartyName") && !value.displayName.toLowerCase().includes("required")),
};
if (key === "paperSource") {
props[key].options = [
"Counterparty paper",
"Our paper",
];
}
if (key === "recordType") {
const { recordTypes } = await this.ironclad.getRecordsSchema();
props[key].options = Object.values(recordTypes)
.map((recordType) => recordType.displayName);
}
}
}
return props;
},
async run({ $ }) {
const {
ironclad,
templateId,
...attributes
} = this;

const parsedAttributes = {};
for (const [
key,
value,
] of Object.entries(attributes)) {
parsedAttributes[key] = parseValue(value);
}

try {
const response = await ironclad.launchWorkflow({
$,
params: {
useDefaultValues: true,
},
data: {
template: templateId,
attributes: parsedAttributes,
},
});
$.export("$summary", `Workflow launched successfully with ID ${response.id}`);
return response;
} catch (error) {
const msg = JSON.parse(error.message);
const { schema } = await ironclad.getWorkflowSchema({
templateId,
});
if (msg.code === "MISSING_PARAM") {
const paramNames = (JSON.parse(msg.param)).map((p) => `\`${schema[p].displayName}\``);
throw new ConfigurationError(`Please enter or update the following required parameters: ${paramNames.join(", ")}`);
}
if (msg.code === "INVALID_PARAM") {
const paramName = schema[msg.metadata.keyPath].displayName;
throw new ConfigurationError(`Invalid parameter: \`${paramName}\`. ${msg.message}`);
}
throw new ConfigurationError(msg.message);
}
},
};
112 changes: 112 additions & 0 deletions components/ironclad/actions/update-workflow/update-workflow.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import ironclad from "../../ironclad.app.mjs";
import { ConfigurationError } from "@pipedream/platform";
import {
getAttributeDescription, parseValue,
} from "../../common/utils.mjs";

export default {
key: "ironclad-update-workflow",
name: "Update Workflow Metadata",
description: "Updates the metadata of an existing workflow. [See the documentation]()",
version: "0.0.1",
type: "action",
props: {
ironclad,
workflowId: {
propDefinition: [
ironclad,
"workflowId",
],
reloadProps: true,
},
comment: {
type: "string",
label: "Comment",
description: "A comment that explains the updates you are making to the workflow",
optional: true,
},
},
async additionalProps() {
const props = {};
if (!this.workflowId) {
return props;
}
const { schema } = await this.ironclad.getWorkflow({
workflowId: this.workflowId,
});
for (const [
key,
value,
] of Object.entries(schema)) {
if (!value?.readOnly) {
props[key] = {
type: value.type === "boolean"
? "boolean"
: value.type === "array"
? "string[]"
: "string",
label: value.displayName,
description: getAttributeDescription(value),
optional: true,
};
if (key === "paperSource") {
props[key].options = [
"Counterparty paper",
"Our paper",
];
}
}
}
return props;
},
async run({ $ }) {
const {
ironclad,
workflowId,
comment,
...attributes
} = this;

const parsedAttributes = {};
for (const [
key,
value,
] of Object.entries(attributes)) {
parsedAttributes[key] = parseValue(value);
}

try {
const response = await ironclad.updateWorkflowMetadata({
$,
workflowId: workflowId,
data: {
updates: Object.entries(parsedAttributes).map(([
key,
value,
]) => ({
action: "set",
path: key,
value,
})),
comment: comment,
},
});
$.export("$summary", `Workflow ${workflowId} updated successfully`);
return response;
} catch (error) {
const msg = JSON.parse(error.message);
const { schema } = await ironclad.getWorkflow({
workflowId,
});
if (msg.code === "MISSING_PARAM") {
const paramNames = (JSON.parse(msg.param)).map((p) => `\`${schema[p].displayName}\``);
throw new ConfigurationError(`Please enter or update the following required parameters: ${paramNames.join(", ")}`);
}
if (msg.code === "INVALID_PARAM") {
const paramName = schema[msg.metadata.keyPath].displayName;
throw new ConfigurationError(`Invalid parameter: \`${paramName}\`. ${msg.message}`);
}
throw new ConfigurationError(msg.message);
}
},
};
64 changes: 64 additions & 0 deletions components/ironclad/common/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
export function getAttributeDescription({
type, displayName, elementType,
}) {
const description = `Value of ${displayName}`;
if (type === "address") {
return `${description}. Example: \`{
"lines": [
"325 5th Street",
"Suite 200"
],
"locality": "San Francisco",
"region": "California",
"postcode": "94107",
"country": "USA"
}\``;
}
if (type === "monetaryAmount") {
return `${description}. Example: \`{
"currency": "USD",
"amount": 25.37
}\``;
}
if (type === "date") {
return `${description}. Example: \`2021-05-11T17:16:53-07:00\``;
}
if (type === "duration") {
return `${description}. Example \`{
"years": 1,
"months": 2,
"weeks": 3,
"days": 4
}\``;
}
if (type === "email") {
return `${description}. Example: \`[email protected]\``;
}
if (type === "array") {
if (elementType.type === "document") {
return `${description}. Array of type \`${elementType.type}\`. Example: \`{"url": "https://your.file.server.test/test-doc-1.docx"}\``;
}
if (elementType.type === "object") {
return `${description}. Array of type \`${elementType.type}\`. See the [docs](https://developer.ironcladapp.com/docs/launch-a-workflow#32-create-request-body-attributes) for more information about field types.`;
}
return `${description}. Array of type \`${elementType.type}\`.`;
}
return description;
}

export function parseValue(value) {
if (!value) {
return undefined;
}
try {
if (typeof value === "string") {
return JSON.parse(value);
}
if (Array.isArray(value)) {
return value.map(JSON.parse);
}
return value;
} catch {
return value;
}
}
Loading

0 comments on commit 49ec0cb

Please sign in to comment.