Skip to content

Commit 49ec0cb

Browse files
authored
New Components - ironclad (#14861)
* ironclad init * wip * pnpm-lock.yaml * new components * updates * pnpm-lock.yaml * add invalid_param error handling
1 parent e313fb6 commit 49ec0cb

File tree

15 files changed

+791
-12
lines changed

15 files changed

+791
-12
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import ironclad from "../../ironclad.app.mjs";
2+
3+
export default {
4+
key: "ironclad-create-record",
5+
name: "Create Record",
6+
description: "Creates a new record in Ironclad. [See the documentation](https://developer.ironcladapp.com/reference/create-a-record)",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
ironclad,
11+
name: {
12+
type: "string",
13+
label: "Name",
14+
description: "Name of the record",
15+
},
16+
type: {
17+
propDefinition: [
18+
ironclad,
19+
"recordType",
20+
],
21+
},
22+
links: {
23+
propDefinition: [
24+
ironclad,
25+
"recordId",
26+
],
27+
type: "string[]",
28+
label: "Links",
29+
description: "Record ID's to link to the new record",
30+
},
31+
parent: {
32+
propDefinition: [
33+
ironclad,
34+
"recordId",
35+
],
36+
label: "Parent",
37+
description: "Record ID to be set as the parent of the current record",
38+
},
39+
children: {
40+
propDefinition: [
41+
ironclad,
42+
"recordId",
43+
],
44+
type: "string[]",
45+
label: "Children",
46+
description: "Record ID's to be set as child records of the current record",
47+
},
48+
properties: {
49+
propDefinition: [
50+
ironclad,
51+
"properties",
52+
],
53+
reloadProps: true,
54+
},
55+
},
56+
async additionalProps() {
57+
const props = {};
58+
if (!this.properties?.length) {
59+
return props;
60+
}
61+
const { properties } = await this.ironclad.getRecordsSchema();
62+
for (const property of this.properties) {
63+
props[property] = {
64+
type: properties[property].type === "boolean"
65+
? "boolean"
66+
: "string",
67+
label: properties[property].displayName,
68+
description: properties[property].description ?? `Value of ${properties[property].displayName}`,
69+
};
70+
}
71+
return props;
72+
},
73+
async run({ $ }) {
74+
const { properties } = await this.ironclad.getRecordsSchema();
75+
const propertiesData = {};
76+
for (const property of this.properties) {
77+
propertiesData[property] = {
78+
type: properties[property].type,
79+
value: this[property],
80+
};
81+
}
82+
83+
const response = await this.ironclad.createRecord({
84+
$,
85+
data: {
86+
name: this.name,
87+
type: this.type,
88+
links: this.links?.length && this.links.map((link) => ({
89+
recordId: link,
90+
})),
91+
parent: this.parent && {
92+
recordId: this.parent,
93+
},
94+
children: this.children?.length && this.children.map((child) => ({
95+
recordId: child,
96+
})),
97+
properties: propertiesData,
98+
},
99+
});
100+
$.export("$summary", `Created record with ID: ${response.id}`);
101+
return response;
102+
},
103+
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import ironclad from "../../ironclad.app.mjs";
2+
import { ConfigurationError } from "@pipedream/platform";
3+
import {
4+
getAttributeDescription, parseValue,
5+
} from "../../common/utils.mjs";
6+
7+
export default {
8+
key: "ironclad-launch-workflow",
9+
name: "Launch Workflow",
10+
description: "Launches a new workflow in Ironclad. [See the documentation](https://developer.ironcladapp.com/reference/launch-a-new-workflow)",
11+
version: "0.0.1",
12+
type: "action",
13+
props: {
14+
ironclad,
15+
templateId: {
16+
propDefinition: [
17+
ironclad,
18+
"templateId",
19+
],
20+
reloadProps: true,
21+
},
22+
},
23+
async additionalProps() {
24+
const props = {};
25+
if (!this.templateId) {
26+
return props;
27+
}
28+
const { schema } = await this.ironclad.getWorkflowSchema({
29+
templateId: this.templateId,
30+
});
31+
for (const [
32+
key,
33+
value,
34+
] of Object.entries(schema)) {
35+
if (!value.readOnly) {
36+
props[key] = {
37+
type: value.type === "boolean"
38+
? "boolean"
39+
: value.type === "array"
40+
? "string[]"
41+
: "string",
42+
label: value.displayName,
43+
description: getAttributeDescription(value),
44+
optional: (!(key === "counterpartyName") && !value.displayName.toLowerCase().includes("required")),
45+
};
46+
if (key === "paperSource") {
47+
props[key].options = [
48+
"Counterparty paper",
49+
"Our paper",
50+
];
51+
}
52+
if (key === "recordType") {
53+
const { recordTypes } = await this.ironclad.getRecordsSchema();
54+
props[key].options = Object.values(recordTypes)
55+
.map((recordType) => recordType.displayName);
56+
}
57+
}
58+
}
59+
return props;
60+
},
61+
async run({ $ }) {
62+
const {
63+
ironclad,
64+
templateId,
65+
...attributes
66+
} = this;
67+
68+
const parsedAttributes = {};
69+
for (const [
70+
key,
71+
value,
72+
] of Object.entries(attributes)) {
73+
parsedAttributes[key] = parseValue(value);
74+
}
75+
76+
try {
77+
const response = await ironclad.launchWorkflow({
78+
$,
79+
params: {
80+
useDefaultValues: true,
81+
},
82+
data: {
83+
template: templateId,
84+
attributes: parsedAttributes,
85+
},
86+
});
87+
$.export("$summary", `Workflow launched successfully with ID ${response.id}`);
88+
return response;
89+
} catch (error) {
90+
const msg = JSON.parse(error.message);
91+
const { schema } = await ironclad.getWorkflowSchema({
92+
templateId,
93+
});
94+
if (msg.code === "MISSING_PARAM") {
95+
const paramNames = (JSON.parse(msg.param)).map((p) => `\`${schema[p].displayName}\``);
96+
throw new ConfigurationError(`Please enter or update the following required parameters: ${paramNames.join(", ")}`);
97+
}
98+
if (msg.code === "INVALID_PARAM") {
99+
const paramName = schema[msg.metadata.keyPath].displayName;
100+
throw new ConfigurationError(`Invalid parameter: \`${paramName}\`. ${msg.message}`);
101+
}
102+
throw new ConfigurationError(msg.message);
103+
}
104+
},
105+
};
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import ironclad from "../../ironclad.app.mjs";
2+
import { ConfigurationError } from "@pipedream/platform";
3+
import {
4+
getAttributeDescription, parseValue,
5+
} from "../../common/utils.mjs";
6+
7+
export default {
8+
key: "ironclad-update-workflow",
9+
name: "Update Workflow Metadata",
10+
description: "Updates the metadata of an existing workflow. [See the documentation]()",
11+
version: "0.0.1",
12+
type: "action",
13+
props: {
14+
ironclad,
15+
workflowId: {
16+
propDefinition: [
17+
ironclad,
18+
"workflowId",
19+
],
20+
reloadProps: true,
21+
},
22+
comment: {
23+
type: "string",
24+
label: "Comment",
25+
description: "A comment that explains the updates you are making to the workflow",
26+
optional: true,
27+
},
28+
},
29+
async additionalProps() {
30+
const props = {};
31+
if (!this.workflowId) {
32+
return props;
33+
}
34+
const { schema } = await this.ironclad.getWorkflow({
35+
workflowId: this.workflowId,
36+
});
37+
for (const [
38+
key,
39+
value,
40+
] of Object.entries(schema)) {
41+
if (!value?.readOnly) {
42+
props[key] = {
43+
type: value.type === "boolean"
44+
? "boolean"
45+
: value.type === "array"
46+
? "string[]"
47+
: "string",
48+
label: value.displayName,
49+
description: getAttributeDescription(value),
50+
optional: true,
51+
};
52+
if (key === "paperSource") {
53+
props[key].options = [
54+
"Counterparty paper",
55+
"Our paper",
56+
];
57+
}
58+
}
59+
}
60+
return props;
61+
},
62+
async run({ $ }) {
63+
const {
64+
ironclad,
65+
workflowId,
66+
comment,
67+
...attributes
68+
} = this;
69+
70+
const parsedAttributes = {};
71+
for (const [
72+
key,
73+
value,
74+
] of Object.entries(attributes)) {
75+
parsedAttributes[key] = parseValue(value);
76+
}
77+
78+
try {
79+
const response = await ironclad.updateWorkflowMetadata({
80+
$,
81+
workflowId: workflowId,
82+
data: {
83+
updates: Object.entries(parsedAttributes).map(([
84+
key,
85+
value,
86+
]) => ({
87+
action: "set",
88+
path: key,
89+
value,
90+
})),
91+
comment: comment,
92+
},
93+
});
94+
$.export("$summary", `Workflow ${workflowId} updated successfully`);
95+
return response;
96+
} catch (error) {
97+
const msg = JSON.parse(error.message);
98+
const { schema } = await ironclad.getWorkflow({
99+
workflowId,
100+
});
101+
if (msg.code === "MISSING_PARAM") {
102+
const paramNames = (JSON.parse(msg.param)).map((p) => `\`${schema[p].displayName}\``);
103+
throw new ConfigurationError(`Please enter or update the following required parameters: ${paramNames.join(", ")}`);
104+
}
105+
if (msg.code === "INVALID_PARAM") {
106+
const paramName = schema[msg.metadata.keyPath].displayName;
107+
throw new ConfigurationError(`Invalid parameter: \`${paramName}\`. ${msg.message}`);
108+
}
109+
throw new ConfigurationError(msg.message);
110+
}
111+
},
112+
};

components/ironclad/common/utils.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
export function getAttributeDescription({
2+
type, displayName, elementType,
3+
}) {
4+
const description = `Value of ${displayName}`;
5+
if (type === "address") {
6+
return `${description}. Example: \`{
7+
"lines": [
8+
"325 5th Street",
9+
"Suite 200"
10+
],
11+
"locality": "San Francisco",
12+
"region": "California",
13+
"postcode": "94107",
14+
"country": "USA"
15+
}\``;
16+
}
17+
if (type === "monetaryAmount") {
18+
return `${description}. Example: \`{
19+
"currency": "USD",
20+
"amount": 25.37
21+
}\``;
22+
}
23+
if (type === "date") {
24+
return `${description}. Example: \`2021-05-11T17:16:53-07:00\``;
25+
}
26+
if (type === "duration") {
27+
return `${description}. Example \`{
28+
"years": 1,
29+
"months": 2,
30+
"weeks": 3,
31+
"days": 4
32+
}\``;
33+
}
34+
if (type === "email") {
35+
return `${description}. Example: \`test@gmail.com\``;
36+
}
37+
if (type === "array") {
38+
if (elementType.type === "document") {
39+
return `${description}. Array of type \`${elementType.type}\`. Example: \`{"url": "https://your.file.server.test/test-doc-1.docx"}\``;
40+
}
41+
if (elementType.type === "object") {
42+
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.`;
43+
}
44+
return `${description}. Array of type \`${elementType.type}\`.`;
45+
}
46+
return description;
47+
}
48+
49+
export function parseValue(value) {
50+
if (!value) {
51+
return undefined;
52+
}
53+
try {
54+
if (typeof value === "string") {
55+
return JSON.parse(value);
56+
}
57+
if (Array.isArray(value)) {
58+
return value.map(JSON.parse);
59+
}
60+
return value;
61+
} catch {
62+
return value;
63+
}
64+
}

0 commit comments

Comments
 (0)