Skip to content

Commit

Permalink
[Components] Certifier (#12467)
Browse files Browse the repository at this point in the history
* Add certifier app and certifier-issue-credential action

* Fix summary method

* Add README

* Implement code review changes

* Update pnpm-lock.yaml

* Bump certifier app version to 0.1.0
  • Loading branch information
mariczne authored Jun 21, 2024
1 parent 051ad0e commit d5566ea
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 8 deletions.
11 changes: 11 additions & 0 deletions components/certifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Overview

Certifier is a comprehensive SaaS platform that streamlines and automates the process of issuing and managing digital credentials. With its intuitive API, Certifier allows organizations to effortlessly generate, distribute, and monitor credentials, enhancing efficiency, security, and compliance in a variety of industries.

# Example Use Cases

- **Online Course Completion Certificates**: Automatically generate and distribute certificates to students upon course completion, enhancing the learning experience and recognition.

- **Event Participation Badges**: Issue personalized digital badges to attendees of webinars, conferences, or workshops, making it easy for them to share their achievements on social media.

- **Employee Recognition**: Streamline the process of awarding certificates or badges for employee achievements, such as "Employee of the Month" or training program completions.
140 changes: 140 additions & 0 deletions components/certifier/actions/issue-credential/issue-credential.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import certifier from "../../certifier.app.mjs";

export default {
name: "Issue Credential",
version: "0.0.1",
key: "certifier-issue-credential",
description:
"Create, issue and send a credential to a recipient. [See the documentation](https://developers.certifier.io/reference/create-issue-send-a-credential)",
props: {
certifier,
groupId: {
propDefinition: [
certifier,
"groupId",
],
// reloadProps is used so that customAttributes can be loaded
// However, note that in the Certifier app custom attributes
// are defined on a workspace level, not group
reloadProps: true,
},
recipientName: {
propDefinition: [
certifier,
"recipientName",
],
},
recipientEmail: {
propDefinition: [
certifier,
"recipientEmail",
],
},
issueCredential: {
propDefinition: [
certifier,
"issueCredential",
],
},
sendCredential: {
propDefinition: [
certifier,
"sendCredential",
],
},
issueDate: {
propDefinition: [
certifier,
"issueDate",
],
},
expiryDate: {
propDefinition: [
certifier,
"expiryDate",
],
},
},
async additionalProps() {
const attributes = await this.certifier.searchAttributes();
return attributes
.filter((attribute) => !attribute.isDefault)
.reduce(
(acc, attribute) => ({
...acc,
[attribute.tag]: {
type: "string",
label: `Custom Attribute: ${attribute.name}`,
optional: true,
},
}),
{},
);
},
type: "action",
methods: {},
async run({ $ }) {
const {
certifier,
groupId,
recipientName,
recipientEmail,
issueCredential,
sendCredential,
issueDate,
expiryDate,
} = this;

const customAttributes = Object.fromEntries(
Object.entries(this).filter(([
key,
]) => key.startsWith("custom.")),
);

let response = await certifier.createCredential({
$,
data: {
groupId: groupId,
recipient: {
email: recipientEmail,
name: recipientName,
},
customAttributes,
...(issueDate && {
issueDate,
}),
...(expiryDate && {
expiryDate,
}),
},
});

console.log(`Successfully created credential with ID \`${response.id}\`.`);

if (issueCredential) {
response = await certifier.issueCredential(response.id, {
$,
});

console.log(`Successfully issued credential with ID \`${response.id}\`.`);

if (sendCredential) {
response = await certifier.sendCredential(response.id, {
$,
data: {
deliveryMethod: "email",
},
});

console.log(`Successfully sent credential with ID \`${response.id}\`.`);
}
}

$.export(
"$summary",
`Successfully created credential for ${response.recipient.name}`,
);

return response;
},
};
124 changes: 119 additions & 5 deletions components/certifier/certifier.app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,125 @@
import { axios } from "@pipedream/platform";

export default {
type: "app",
app: "certifier",
propDefinitions: {},
methods: {
// this.$auth contains connected account data
authKeys() {
console.log(Object.keys(this.$auth));
getUrl(path, apiVersion = "v1") {
return `https://api.certifier.io/${apiVersion}${path}`;
},
getHeaders(headers = {}) {
return {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.$auth.access_token}`,
"Certifier-Version": "2022-10-26",
...headers,
};
},
callApi({
$ = this, path, headers, apiVersion, ...args
} = {}) {
return axios($, {
url: this.getUrl(path, apiVersion),
headers: this.getHeaders(headers),
...args,
});
},
searchGroups(args = {}) {
return this.callApi({
path: "/groups",
...args,
});
},
searchAttributes(args = {}) {
return this.callApi({
path: "/attributes",
...args,
});
},
createCredential(args = {}) {
return this.callApi({
method: "POST",
path: "/credentials",
...args,
});
},
issueCredential(id, args = {}) {
return this.callApi({
method: "POST",
path: `/credentials/${id}/issue`,
...args,
});
},
sendCredential(id, args = {}) {
return this.callApi({
method: "POST",
path: `/credentials/${id}/send`,
...args,
});
},
},
propDefinitions: {
groupId: {
type: "string",
label: "Group ID",
description: "The ID of the group",
async options({ prevContext } = {}) {
const response = await this.searchGroups({
params: {
cursor: prevContext.cursor,
},
});
const groups = response.data;
const cursor = response.pagination.next;

return {
options: groups.map(({
id, name,
}) => ({
label: name,
value: id,
})),
context: {
cursor,
},
};
},
},
recipientName: {
type: "string",
label: "Recipient Name",
description: "The name of the credential’s recipient.",
},
recipientEmail: {
type: "string",
label: "Recipient Email",
description: "The email of the credential’s recipient.",
},
issueCredential: {
type: "boolean",
label: "Issue Credential",
description:
"Whether to issue a credential (`true`) or create a draft (`false`) when the workflow is triggered (default `true`).",
},
sendCredential: {
type: "boolean",
label: "Send Credential",
description:
"Whether to send a credential to a recipient via email (`true`) or not (`false`) when the workflow is triggered (default is `true`). This step is only applicable if \"Issue Credential\" is set to `true`.",
},
issueDate: {
type: "string",
label: "Issue Date",
description:
"The date (in `YYYY-MM-DD` format) of your credential's issuance (by default this field is set to the day when the workflow is triggered).",
optional: true,
},
expiryDate: {
type: "string",
label: "Expiry Date",
description:
"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).",
optional: true,
},
},
};
};
7 changes: 5 additions & 2 deletions components/certifier/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipedream/certifier",
"version": "0.0.1",
"version": "0.1.0",
"description": "Pipedream Certifier Components",
"main": "certifier.app.mjs",
"keywords": [
Expand All @@ -11,5 +11,8 @@
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.0"
}
}
}
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d5566ea

Please sign in to comment.