From d5566ea8c3d1ad776fb227ebdf327ac3285995a3 Mon Sep 17 00:00:00 2001 From: Marcin Kwiek Date: Fri, 21 Jun 2024 19:10:37 +0200 Subject: [PATCH] [Components] Certifier (#12467) * 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 --- components/certifier/README.md | 11 ++ .../issue-credential/issue-credential.mjs | 140 ++++++++++++++++++ components/certifier/certifier.app.mjs | 124 +++++++++++++++- components/certifier/package.json | 7 +- pnpm-lock.yaml | 5 +- 5 files changed, 279 insertions(+), 8 deletions(-) create mode 100644 components/certifier/README.md create mode 100644 components/certifier/actions/issue-credential/issue-credential.mjs diff --git a/components/certifier/README.md b/components/certifier/README.md new file mode 100644 index 0000000000000..d87a176095897 --- /dev/null +++ b/components/certifier/README.md @@ -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. diff --git a/components/certifier/actions/issue-credential/issue-credential.mjs b/components/certifier/actions/issue-credential/issue-credential.mjs new file mode 100644 index 0000000000000..6a862cbe6dce4 --- /dev/null +++ b/components/certifier/actions/issue-credential/issue-credential.mjs @@ -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; + }, +}; diff --git a/components/certifier/certifier.app.mjs b/components/certifier/certifier.app.mjs index 2ad1d943264e7..c904d412dae85 100644 --- a/components/certifier/certifier.app.mjs +++ b/components/certifier/certifier.app.mjs @@ -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, }, }, -}; \ No newline at end of file +}; diff --git a/components/certifier/package.json b/components/certifier/package.json index 5bc895100d14d..a65b18de7a6d5 100644 --- a/components/certifier/package.json +++ b/components/certifier/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/certifier", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Certifier Components", "main": "certifier.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.0" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6003aea20b4a4..82a2c35b6f574 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1292,7 +1292,10 @@ importers: '@pipedream/platform': 1.5.1 components/certifier: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.0 + dependencies: + '@pipedream/platform': 3.0.0 components/cflow: specifiers: {}