diff --git a/components/guru/.gitignore b/components/guru/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/guru/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/guru/actions/add-tag-to-card/add-tag-to-card.mjs b/components/guru/actions/add-tag-to-card/add-tag-to-card.mjs new file mode 100644 index 0000000000000..42afb2333a610 --- /dev/null +++ b/components/guru/actions/add-tag-to-card/add-tag-to-card.mjs @@ -0,0 +1,37 @@ +import guru from "../../guru.app.mjs"; + +export default { + key: "guru-add-tag-to-card", + name: "Add Tag to Card", + description: "Links an existing tag to a specified card in Guru. [See the documentation](https://developer.getguru.com/reference/getv1cardsgetextendedfact)", + version: "0.0.1", + type: "action", + props: { + guru, + cardId: { + propDefinition: [ + guru, + "cardId", + ], + }, + tags: { + propDefinition: [ + guru, + "tags", + ], + type: "string", + label: "Tag", + description: "The ID of the tag to add to the card", + }, + }, + async run({ $ }) { + const response = await this.guru.linkTagToCard({ + $, + cardId: this.cardId, + tagId: this.tags, + }); + + $.export("$summary", `Successfully linked tag ${this.tags} to card ${this.cardId}`); + return response; + }, +}; diff --git a/components/guru/actions/create-card/create-card.mjs b/components/guru/actions/create-card/create-card.mjs new file mode 100644 index 0000000000000..e59dfc47268bc --- /dev/null +++ b/components/guru/actions/create-card/create-card.mjs @@ -0,0 +1,70 @@ +import { SHARE_STATUS_OPTIONS } from "../../common/constants.mjs"; +import { parseObject } from "../../common/utils.mjs"; +import guru from "../../guru.app.mjs"; + +export default { + key: "guru-create-card", + name: "Create Card", + description: "Creates a new card on your Guru account. [See the documentation](https://developer.getguru.com/reference/postv1cardscreateextendedfact)", + version: "0.0.1", + type: "action", + props: { + guru, + title: { + type: "string", + label: "Card Title", + description: "The title of the card to create", + }, + content: { + type: "string", + label: "Content", + description: "The content of the card to create", + }, + shareStatus: { + type: "string", + label: "Share Status", + description: "The share status of the card.", + options: SHARE_STATUS_OPTIONS, + optional: true, + }, + collection: { + propDefinition: [ + guru, + "collection", + ], + }, + folderIds: { + propDefinition: [ + guru, + "folderIds", + ], + }, + tags: { + propDefinition: [ + guru, + "tags", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.guru.createCard({ + $, + data: { + preferredPhrase: this.title, + content: this.content, + shareStatus: this.shareStatus, + collection: { + id: this.collection, + }, + folderIds: parseObject(this.folderIds), + tags: parseObject(this.tags)?.map((item) => ({ + id: item, + })), + }, + }); + + $.export("$summary", `Created card "${this.title}" successfully`); + return response; + }, +}; diff --git a/components/guru/actions/export-card-to-pdf/export-card-to-pdf.mjs b/components/guru/actions/export-card-to-pdf/export-card-to-pdf.mjs new file mode 100644 index 0000000000000..ff8337b0abbe9 --- /dev/null +++ b/components/guru/actions/export-card-to-pdf/export-card-to-pdf.mjs @@ -0,0 +1,41 @@ +import fs from "fs"; +import stream from "stream"; +import { promisify } from "util"; +import guru from "../../guru.app.mjs"; + +export default { + key: "guru-export-card-to-pdf", + name: "Export Card to PDF", + description: "Export a specific card identified by its ID to a PDF file. [See the documentation](https://developer.getguru.com/docs/download-cards-to-pdf)", + version: "0.0.1", + type: "action", + props: { + guru, + cardId: { + propDefinition: [ + guru, + "cardId", + ], + }, + }, + async run({ $ }) { + const { + headers, data, + } = await this.guru.exportCardToPdf({ + $, + cardId: this.cardId, + returnFullResponse: true, + }); + + const fileName = headers["content-disposition"]?.split("filename=")[1]?.split("/")[1].slice(0, -1); + const filePath = `/tmp/${fileName}`; + + const pipeline = promisify(stream.pipeline); + await pipeline(data, fs.createWriteStream(filePath)); + + $.export("$summary", `Successfully exported card ID ${this.cardId} to PDF.`); + return { + filePath, + }; + }, +}; diff --git a/components/guru/app/guru.app.ts b/components/guru/app/guru.app.ts deleted file mode 100644 index 364108a439141..0000000000000 --- a/components/guru/app/guru.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "guru", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/guru/common/constants.mjs b/components/guru/common/constants.mjs new file mode 100644 index 0000000000000..0a065a9330795 --- /dev/null +++ b/components/guru/common/constants.mjs @@ -0,0 +1,4 @@ +export const SHARE_STATUS_OPTIONS = [ + "TEAM", + "PRIVATE", +]; diff --git a/components/guru/common/utils.mjs b/components/guru/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/guru/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/guru/guru.app.mjs b/components/guru/guru.app.mjs new file mode 100644 index 0000000000000..8dd6b6ab1377e --- /dev/null +++ b/components/guru/guru.app.mjs @@ -0,0 +1,190 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "guru", + propDefinitions: { + collection: { + type: "string", + label: "Collection", + description: "The collection to create the card in", + async options() { + const data = await this.listCollections(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + folderIds: { + type: "string[]", + label: "Folder Ids", + description: "The IDs of the folders to create the card in", + async options() { + const data = await this.listFolders(); + + return data.map(({ + id: value, title: label, + }) => ({ + label, + value, + })); + }, + }, + tags: { + type: "string[]", + label: "Tags", + description: "The IDs of the tags to add to the card", + async options() { + const { team: { id: teamId } } = await this.whoAmI(); + const data = await this.listTags({ + teamId, + }); + + return data[0]?.tags.map(({ + id: value, value: label, + }) => ({ + label, + value, + })); + }, + }, + cardId: { + type: "string", + label: "Card ID", + description: "The ID of the card", + async options({ prevContext }) { + const { + data, headers, + } = await this.listCards({ + params: { + token: prevContext.token, + }, + }); + let token; + + if (headers.link) { + const link = headers.link.split(">")[0].slice(1); + const url = new URL(link); + const params = new URLSearchParams(url.search); + token = params.get("token"); + } + return { + options: data.map(({ + id: value, preferredPhrase: label, + }) => ({ + label, + value, + })), + context: { + token, + }, + }; + }, + }, + + folderId: { + type: "string", + label: "Folder ID", + description: "The ID of the folder to export to PDF", + }, + }, + methods: { + _baseUrl() { + return "https://api.getguru.com/api/v1"; + }, + _auth() { + return { + username: `${this.$auth.username}`, + password: `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + auth: this._auth(), + ...opts, + }); + }, + whoAmI(opts = {}) { + return this._makeRequest({ + path: "/whoami", + ...opts, + }); + }, + createCard(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/cards/extended", + ...opts, + }); + }, + linkTagToCard({ + cardId, tagId, ...opts + }) { + return this._makeRequest({ + method: "PUT", + path: `/cards/${cardId}/tags/${tagId}`, + headers: { + "Accept": "application/json", + "Content-Type": "application/json", + }, + ...opts, + }); + }, + exportCardToPdf({ + cardId, ...opts + }) { + return this._makeRequest({ + path: `/cards/${cardId}/pdf`, + responseType: "stream", + ...opts, + }); + }, + listCollections(opts = {}) { + return this._makeRequest({ + path: "/collections", + ...opts, + }); + }, + listFolders(opts = {}) { + return this._makeRequest({ + path: "/folders", + ...opts, + }); + }, + listTags({ + teamId, ...opts + }) { + return this._makeRequest({ + path: `/teams/${teamId}/tagcategories`, + ...opts, + }); + }, + listCards(opts = {}) { + return this._makeRequest({ + path: "/search/cardmgr", + returnFullResponse: true, + ...opts, + }); + }, + createWebhook(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + ...opts, + }); + }, + deleteWebhook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + }, + }, +}; diff --git a/components/guru/package.json b/components/guru/package.json index c50c8c39acbbb..8fcf089f37ec8 100644 --- a/components/guru/package.json +++ b/components/guru/package.json @@ -1,18 +1,18 @@ { "name": "@pipedream/guru", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream Guru Components", - "main": "dist/app/guru.app.mjs", + "main": "guru.app.mjs", "keywords": [ "pipedream", "guru" ], - "files": [ - "dist" - ], "homepage": "https://pipedream.com/apps/guru", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/guru/sources/announcement-read-instant/announcement-read-instant.mjs b/components/guru/sources/announcement-read-instant/announcement-read-instant.mjs new file mode 100644 index 0000000000000..13929def4a12e --- /dev/null +++ b/components/guru/sources/announcement-read-instant/announcement-read-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "guru-announcement-read-instant", + name: "New Announcement Read (Instant)", + description: "Emit new event when a user clicks on the \"I read it\" button in an Announcement. [See the documentation](https://developer.getguru.com/docs/creating-a-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "alert-read"; + }, + getSummary(body) { + return `Announcement Read: ${body.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/guru/sources/announcement-read-instant/test-event.mjs b/components/guru/sources/announcement-read-instant/test-event.mjs new file mode 100644 index 0000000000000..9566c050c32a7 --- /dev/null +++ b/components/guru/sources/announcement-read-instant/test-event.mjs @@ -0,0 +1,11 @@ +export default { + "id":"64753163-9817-4500-9651-96177c32e3d1", + "eventType":"alert-read", + "user":"bob@getguru.com", + "eventDate":"2021-04-13T13:53:00.000+0000", + "properties":{ + "cardId":"64753163-9817-4500-9651-96177c32e3d1", + "collectionId":"64753163-9817-4500-9651-96177c32e3d1", + "source": "UI" + } +} \ No newline at end of file diff --git a/components/guru/sources/card-updated-instant/card-updated-instant.mjs b/components/guru/sources/card-updated-instant/card-updated-instant.mjs new file mode 100644 index 0000000000000..bc38c7143ab18 --- /dev/null +++ b/components/guru/sources/card-updated-instant/card-updated-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "guru-card-updated-instant", + name: "Card Updated (Instant)", + description: "Emit new event when a user makes an edit to a card. [See the documentation](https://developer.getguru.com/docs/creating-a-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "card-updated"; + }, + getSummary(body) { + return `Card Updated: ${body.properties.cardId}`; + }, + }, + sampleEmit, +}; diff --git a/components/guru/sources/card-updated-instant/test-event.mjs b/components/guru/sources/card-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..fabb48c22966c --- /dev/null +++ b/components/guru/sources/card-updated-instant/test-event.mjs @@ -0,0 +1,11 @@ +export default { + "id":"64753163-9817-4500-9651-96177c32e3d1", + "eventType":"card-updated", + "user":"bob@getguru.com", + "eventDate":"2021-04-13T13:53:00.000+0000", + "properties":{ + "cardId":"64753163-9817-4500-9651-96177c32e3d1", + "collectionId":"64753163-9817-4500-9651-96177c32e3d1", + "source": "UI" + } +} \ No newline at end of file diff --git a/components/guru/sources/common/base.mjs b/components/guru/sources/common/base.mjs new file mode 100644 index 0000000000000..547db9a268add --- /dev/null +++ b/components/guru/sources/common/base.mjs @@ -0,0 +1,44 @@ +import guru from "../../guru.app.mjs"; + +export default { + props: { + guru, + http: "$.interface.http", + db: "$.service.db", + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + getExtraData() { + return {}; + }, + }, + hooks: { + async activate() { + const response = await this.guru.createWebhook({ + data: { + targetUrl: this.http.endpoint, + deliveryMode: "SINGLE", + status: "ENABLED", + filter: this.getEventType(), + }, + }); + this._setHookId(response.id); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.guru.deleteWebhook(webhookId); + }, + }, + async run({ body }) { + this.$emit(body, { + id: body.id, + summary: this.getSummary(body), + ts: Date.parse(body.eventDate), + }); + }, +}; diff --git a/components/guru/sources/new-card-instant/new-card-instant.mjs b/components/guru/sources/new-card-instant/new-card-instant.mjs new file mode 100644 index 0000000000000..58898cfffd35a --- /dev/null +++ b/components/guru/sources/new-card-instant/new-card-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "guru-new-card-instant", + name: "New Card Created (Instant)", + description: "Emit new event when a new card is published. [See the documentation](https://developer.getguru.com/docs/creating-a-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "card-created"; + }, + getSummary(body) { + return `New Card: ${body.properties.cardId}`; + }, + }, + sampleEmit, +}; diff --git a/components/guru/sources/new-card-instant/test-event.mjs b/components/guru/sources/new-card-instant/test-event.mjs new file mode 100644 index 0000000000000..93956d75958fe --- /dev/null +++ b/components/guru/sources/new-card-instant/test-event.mjs @@ -0,0 +1,11 @@ +export default { + "id":"64753163-9817-4500-9651-96177c32e3d1", + "eventType":"card-created", + "user":"bob@getguru.com", + "eventDate":"2021-04-13T13:53:00.000+0000", + "properties":{ + "cardId":"64753163-9817-4500-9651-96177c32e3d1", + "collectionId":"64753163-9817-4500-9651-96177c32e3d1", + "source": "UI" + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5ecaa8caadd1..853a28d965062 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5815,7 +5815,11 @@ importers: components/gupshup: {} - components/guru: {} + components/guru: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/h_supertools_analytics_tool: dependencies: