Skip to content

Commit 0c8f724

Browse files
committed
Add create-draft-credential, create-group, issue-existing-draft-credential, send-credential actions for Certifier
1 parent 6071489 commit 0c8f724

File tree

5 files changed

+345
-0
lines changed

5 files changed

+345
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import certifier from "../../certifier.app.mjs";
2+
3+
export default {
4+
name: "Create Draft Credential",
5+
version: "0.0.1",
6+
key: "certifier-create-draft-credential",
7+
description:
8+
"Create a draft credential. [See the documentation](https://developers.certifier.io/reference/create-a-credential)",
9+
props: {
10+
certifier,
11+
groupId: {
12+
propDefinition: [
13+
certifier,
14+
"groupId",
15+
],
16+
// reloadProps is used so that customAttributes can be loaded
17+
// However, note that in the Certifier app custom attributes
18+
// are defined on a workspace level, not group
19+
reloadProps: true,
20+
},
21+
recipientName: {
22+
propDefinition: [
23+
certifier,
24+
"recipientName",
25+
],
26+
},
27+
recipientEmail: {
28+
propDefinition: [
29+
certifier,
30+
"recipientEmail",
31+
],
32+
},
33+
issueDate: {
34+
propDefinition: [
35+
certifier,
36+
"issueDate",
37+
],
38+
},
39+
expiryDate: {
40+
propDefinition: [
41+
certifier,
42+
"expiryDate",
43+
],
44+
},
45+
},
46+
async additionalProps() {
47+
const attributes = await this.certifier.searchAttributes();
48+
return attributes
49+
.filter((attribute) => !attribute.isDefault)
50+
.reduce(
51+
(acc, attribute) => ({
52+
...acc,
53+
[attribute.tag]: {
54+
type: "string",
55+
label: `Custom Attribute: ${attribute.name}`,
56+
optional: true,
57+
},
58+
}),
59+
{},
60+
);
61+
},
62+
type: "action",
63+
methods: {},
64+
async run({ $ }) {
65+
const {
66+
certifier,
67+
groupId,
68+
recipientName,
69+
recipientEmail,
70+
issueDate,
71+
expiryDate,
72+
} = this;
73+
74+
const customAttributes = Object.fromEntries(
75+
Object.entries(this).filter(([
76+
key,
77+
]) => key.startsWith("custom.")),
78+
);
79+
80+
const response = await certifier.createCredential({
81+
$,
82+
data: {
83+
groupId: groupId,
84+
recipient: {
85+
email: recipientEmail,
86+
name: recipientName,
87+
},
88+
customAttributes,
89+
...(issueDate && {
90+
issueDate,
91+
}),
92+
...(expiryDate && {
93+
expiryDate,
94+
}),
95+
},
96+
});
97+
98+
console.log(`Successfully created credential with ID \`${response.id}\`.`);
99+
100+
$.export(
101+
"$summary",
102+
`Successfully created credential for ${response.recipient.name}`,
103+
);
104+
105+
return response;
106+
},
107+
};
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import certifier from "../../certifier.app.mjs";
2+
3+
export default {
4+
name: "Create Group",
5+
version: "0.0.1",
6+
key: "certifier-create-group",
7+
description:
8+
"Create a group. [See the documentation](https://developers.certifier.io/reference/create-a-group)",
9+
props: {
10+
certifier,
11+
name: {
12+
propDefinition: [
13+
certifier,
14+
"groupName",
15+
],
16+
},
17+
certificateDesignId: {
18+
propDefinition: [
19+
certifier,
20+
"certificateDesignId",
21+
],
22+
},
23+
badgeDesignId: {
24+
propDefinition: [
25+
certifier,
26+
"badgeDesignId",
27+
],
28+
},
29+
learningEventUrl: {
30+
propDefinition: [
31+
certifier,
32+
"learningEventUrl",
33+
],
34+
},
35+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
36+
alert: {
37+
type: "alert",
38+
alertType: "warning",
39+
content:
40+
"At least one of `Certificate Design` and `Badge Design` fields is required.",
41+
},
42+
},
43+
type: "action",
44+
methods: {},
45+
async run({ $ }) {
46+
const {
47+
certifier,
48+
name,
49+
certificateDesignId,
50+
badgeDesignId,
51+
learningEventUrl,
52+
} = this;
53+
54+
const response = await certifier.createGroup({
55+
$,
56+
data: {
57+
name,
58+
certificateDesignId,
59+
badgeDesignId,
60+
learningEventUrl,
61+
},
62+
});
63+
64+
$.export(
65+
"$summary",
66+
`Successfully created group ${response.name}`,
67+
);
68+
69+
return response;
70+
},
71+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import certifier from "../../certifier.app.mjs";
2+
3+
export default {
4+
name: "Issue Existing Draft Credential",
5+
version: "0.0.1",
6+
key: "certifier-issue-existing-draft-credential",
7+
description:
8+
"Issue an existing draft credential. [See the documentation](https://developers.certifier.io/reference/issue-a-credential)",
9+
props: {
10+
certifier,
11+
credentialId: {
12+
propDefinition: [
13+
certifier,
14+
"credentialId",
15+
],
16+
},
17+
},
18+
type: "action",
19+
methods: {},
20+
async run({ $ }) {
21+
const {
22+
certifier, credentialId,
23+
} = this;
24+
25+
const response = await certifier.issueCredential(credentialId, {
26+
$,
27+
});
28+
console.log(`Successfully issued credential with ID \`${response.id}\`.`);
29+
30+
$.export(
31+
"$summary",
32+
`Successfully issued credential for ${response.recipient.name}`,
33+
);
34+
35+
return response;
36+
},
37+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import certifier from "../../certifier.app.mjs";
2+
3+
export default {
4+
name: "Send Credential",
5+
version: "0.0.1",
6+
key: "certifier-send-credential",
7+
description:
8+
"Send an existing credential. [See the documentation](https://developers.certifier.io/reference/send-a-credential)",
9+
props: {
10+
certifier,
11+
credentialId: {
12+
propDefinition: [
13+
certifier,
14+
"credentialId",
15+
],
16+
},
17+
},
18+
type: "action",
19+
methods: {},
20+
async run({ $ }) {
21+
const {
22+
certifier, credentialId,
23+
} = this;
24+
25+
const response = await certifier.sendCredential(credentialId, {
26+
$,
27+
data: {
28+
deliveryMethod: "email",
29+
},
30+
});
31+
console.log(`Successfully sent credential with ID \`${response.id}\`.`);
32+
33+
$.export(
34+
"$summary",
35+
`Successfully sent credential for ${response.recipient.name}`,
36+
);
37+
38+
return response;
39+
},
40+
};

components/certifier/certifier.app.mjs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ export default {
3636
...args,
3737
});
3838
},
39+
searchDesigns(args = {}) {
40+
return this.callApi({
41+
path: "/designs",
42+
...args,
43+
});
44+
},
3945
createCredential(args = {}) {
4046
return this.callApi({
4147
method: "POST",
@@ -57,6 +63,13 @@ export default {
5763
...args,
5864
});
5965
},
66+
createGroup(args = {}) {
67+
return this.callApi({
68+
method: "POST",
69+
path: "/groups",
70+
...args,
71+
});
72+
},
6073
},
6174
propDefinitions: {
6275
groupId: {
@@ -121,5 +134,82 @@ export default {
121134
"The date (in `YYYY-MM-DD` format) of your credential's expiration. If not provided, expiry date from the group settings will be used (by default this field is empty).",
122135
optional: true,
123136
},
137+
groupName: {
138+
type: "string",
139+
label: "Group Name",
140+
description:
141+
"The group's name that is used as [group.name] attribute later on.",
142+
},
143+
certificateDesignId: {
144+
type: "string",
145+
label: "Certificate Design",
146+
description: "The unique certificate design's name.",
147+
optional: true,
148+
async options({ prevContext } = {}) {
149+
const response = await this.searchDesigns({
150+
params: {
151+
cursor: prevContext.cursor,
152+
},
153+
});
154+
const designs = response.data;
155+
const cursor = response.pagination.next;
156+
157+
return {
158+
options: designs
159+
.filter((design) => design.type === "certificate")
160+
.map(({
161+
id, name,
162+
}) => ({
163+
label: name,
164+
value: id,
165+
})),
166+
context: {
167+
cursor,
168+
},
169+
};
170+
},
171+
},
172+
badgeDesignId: {
173+
type: "string",
174+
label: "Badge Design",
175+
description: "The unique badge design's name.",
176+
optional: true,
177+
async options({ prevContext } = {}) {
178+
const response = await this.searchDesigns({
179+
params: {
180+
cursor: prevContext.cursor,
181+
},
182+
});
183+
const designs = response.data;
184+
const cursor = response.pagination.next;
185+
186+
return {
187+
options: designs
188+
.filter((design) => design.type === "badge")
189+
.map(({
190+
id, name,
191+
}) => ({
192+
label: name,
193+
value: id,
194+
})),
195+
context: {
196+
cursor,
197+
},
198+
};
199+
},
200+
},
201+
learningEventUrl: {
202+
type: "string",
203+
label: "Learning Event URL",
204+
description:
205+
"The learning event's URL that is shown in the digital wallet.",
206+
optional: true,
207+
},
208+
credentialId: {
209+
type: "string",
210+
label: "Credential ID",
211+
description:
212+
"The credential's unique ID.",
213+
},
124214
},
125215
};

0 commit comments

Comments
 (0)