diff --git a/.eslintrc b/.eslintrc index c14d14a653d0d..ef96a1e33c839 100644 --- a/.eslintrc +++ b/.eslintrc @@ -33,7 +33,7 @@ "pipedream/props-description": "warn", "pipedream/source-name": "warn", "pipedream/source-description": "warn", - "pipedream/no-ts-version": "error" + "pipedream/no-ts-version": "warn" } }, { diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 64761ad36101e..b69e9b6171497 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4.1.1 name: Checkout repo with: # See https://github.com/actions/checkout#checkout-v2 diff --git a/.github/workflows/publish-components.yaml b/.github/workflows/publish-components.yaml index a87db198f25b4..a893b25014e45 100644 --- a/.github/workflows/publish-components.yaml +++ b/.github/workflows/publish-components.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -27,7 +27,7 @@ jobs: - name: Install dependencies run: pnpm install -r --no-frozen-lockfile - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ @@ -115,7 +115,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -131,7 +131,7 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/publish-marketplace-content.yaml b/.github/workflows/publish-marketplace-content.yaml index 2989f76dc4c83..49e463413b7ea 100644 --- a/.github/workflows/publish-marketplace-content.yaml +++ b/.github/workflows/publish-marketplace-content.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -27,7 +27,7 @@ jobs: - name: Install dependencies run: pnpm install -r --no-frozen-lockfile - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/publish-packages.yaml b/.github/workflows/publish-packages.yaml index 4fcb63dee6fe4..397ceda0f9ca2 100644 --- a/.github/workflows/publish-packages.yaml +++ b/.github/workflows/publish-packages.yaml @@ -8,11 +8,11 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 - - uses: actions/setup-node@v3.8.1 + - uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/pull-request-checks.yaml b/.github/workflows/pull-request-checks.yaml index 597834df12de4..fb5a82cc91986 100644 --- a/.github/workflows/pull-request-checks.yaml +++ b/.github/workflows/pull-request-checks.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4.1.1 name: Checkout repo with: # See https://github.com/actions/checkout#checkout-v2 @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4.1.1 name: Checkout - uses: jitterbit/get-changed-files@v1 id: changed_files @@ -77,7 +77,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 with: # Full git history is needed to get a proper list of changed files # within `super-linter` @@ -99,7 +99,7 @@ jobs: - name: Install dependencies run: pnpm install -r - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ @@ -133,7 +133,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -148,7 +148,7 @@ jobs: key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - - uses: actions/setup-node@v3.8.1 + - uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ @@ -167,7 +167,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -183,7 +183,7 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ @@ -256,7 +256,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - uses: pnpm/action-setup@v2.4.0 with: version: 7.33.6 @@ -272,7 +272,7 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - name: Setup Node Env - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 with: node-version: 14 registry-url: https://registry.npmjs.org/ diff --git a/blog/mario/poetry.lock b/blog/mario/poetry.lock index 9e3b6ebbb3f40..77e4076238107 100644 --- a/blog/mario/poetry.lock +++ b/blog/mario/poetry.lock @@ -1722,13 +1722,13 @@ files = [ [[package]] name = "langchain" -version = "0.0.312" +version = "0.0.317" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langchain-0.0.312-py3-none-any.whl", hash = "sha256:2c7ea6e80195b8747c25ca4b905bd4814f26f47719a27edcb369cb6cd2186df3"}, - {file = "langchain-0.0.312.tar.gz", hash = "sha256:4629233c158f23dcfb0cbc249b27d7d8bde1e71ce1d8972d53ae54c7504fc78a"}, + {file = "langchain-0.0.317-py3-none-any.whl", hash = "sha256:bca76a7d9fbf18bc284b1bf8884c3a88c8f17dc8168ba2123132033b0efdd8df"}, + {file = "langchain-0.0.317.tar.gz", hash = "sha256:c0e0e835e749d62af86340a3e07fb10d65c9e0c8f66a2d443cded81dbb85835a"}, ] [package.dependencies] @@ -1753,7 +1753,7 @@ cli = ["typer (>=0.9.0,<0.10.0)"] cohere = ["cohere (>=4,<5)"] docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"] embeddings = ["sentence-transformers (>=2,<3)"] -extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "amazon-textract-caller (<2)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "dashvector (>=1.0.1,<2.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "gql (>=3.4.1,<4.0.0)", "html2text (>=2020.1.16,<2021.0.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (>=0,<1)", "openapi-schema-pydantic (>=1.2,<2.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "amazon-textract-caller (<2)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "dashvector (>=1.0.1,<2.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "gql (>=3.4.1,<4.0.0)", "html2text (>=2020.1.16,<2021.0.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "lxml (>=4.9.2,<5.0.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (>=0,<1)", "openapi-schema-pydantic (>=1.2,<2.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] javascript = ["esprima (>=4.0.1,<5.0.0)"] llms = ["clarifai (>=9.1.0)", "cohere (>=4,<5)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (>=0,<1)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"] openai = ["openai (>=0,<1)", "tiktoken (>=0.3.2,<0.6.0)"] @@ -4540,17 +4540,17 @@ dev = ["flake8 (<4.0.0)", "flake8-annotations", "flake8-bugbear", "flake8-commas [[package]] name = "urllib3" -version = "1.26.15" +version = "1.26.18" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, + {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] @@ -4815,4 +4815,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "71bd29d8b803573bb707b2e2820977abe44650bcdd192301ce1a7f122c1cb823" +content-hash = "0cb69284fca2fb454ee72e0093f5e41b83f63698beb2ac6adf5aabc813d4b1da" diff --git a/blog/mario/pyproject.toml b/blog/mario/pyproject.toml index 4c4e0a1b3640c..555fd4c0bc784 100644 --- a/blog/mario/pyproject.toml +++ b/blog/mario/pyproject.toml @@ -8,7 +8,7 @@ packages = [{include = "public_q_and_a"}] [tool.poetry.dependencies] python = "^3.10" -langchain = "^0.0.312" +langchain = "^0.0.317" openai = "^0.27.2" jupyter = "^1.0.0" pathlib = "^1.0.1" diff --git a/components/gist/app/gist.app.ts b/components/abyssale/abyssale.app.mjs similarity index 65% rename from components/gist/app/gist.app.ts rename to components/abyssale/abyssale.app.mjs index 8fa1ce70a1575..439756adfe061 100644 --- a/components/gist/app/gist.app.ts +++ b/components/abyssale/abyssale.app.mjs @@ -1,8 +1,6 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ +export default { type: "app", - app: "gist", + app: "abyssale", propDefinitions: {}, methods: { // this.$auth contains connected account data @@ -10,4 +8,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); +}; diff --git a/components/abyssale/package.json b/components/abyssale/package.json new file mode 100644 index 0000000000000..ec1b7cdfad398 --- /dev/null +++ b/components/abyssale/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/abyssale", + "version": "0.0.1", + "description": "Pipedream Abyssale Components", + "main": "abyssale.app.mjs", + "keywords": [ + "pipedream", + "abyssale" + ], + "homepage": "https://pipedream.com/apps/abyssale", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/accredible/.gitignore b/components/accredible/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/accredible/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/accredible/accredible.app.mjs b/components/accredible/accredible.app.mjs new file mode 100644 index 0000000000000..a98f16fcdd429 --- /dev/null +++ b/components/accredible/accredible.app.mjs @@ -0,0 +1,199 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; +import utils from "./common/utils.mjs"; + +export default { + type: "app", + app: "accredible", + propDefinitions: { + credentialId: { + type: "string", + label: "Credential ID", + description: "The ID of the existing credential", + async options({ page }) { + const { credentials } = await this.searchCredentials({ + data: { + query: { + "created_at[gte]": utils.getDateFormatted(undefined, 1), + }, + page: { + from: page * constants.DEFAULT_LIMIT, + size: constants.DEFAULT_LIMIT, + }, + }, + }); + return credentials.map(({ + id: value, recipient_name: label, + }) => ({ + label, + value, + })); + }, + }, + groupId: { + type: "string", + label: "Group ID", + description: "The ID of the group", + async options({ page }) { + const { groups } = await this.searchGroups({ + params: { + page: page + 1, + }, + }); + return groups.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + recipientEmail: { + type: "string", + label: "Recipient Email", + description: "The email of the recipient", + }, + recipientName: { + type: "string", + label: "Recipient Name", + description: "The name of the recipient", + }, + credentialData: { + type: "object", + label: "Credential Data", + description: "The data of the credential", + optional: true, + }, + }, + methods: { + getUrl(path, apiVersion = constants.API.V1) { + return `${constants.BASE_URL}${apiVersion}${path}`; + }, + getHeaders(headers) { + return { + "Content-Type": "application/json", + "Authorization": `Token token=${this.$auth.api_key}`, + ...headers, + }; + }, + makeRequest({ + $ = this, path, headers, apiVersion, ...args + } = {}) { + return axios($, { + url: this.getUrl(path, apiVersion), + headers: this.getHeaders(headers), + ...args, + }); + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + put(args = {}) { + return this.makeRequest({ + method: "put", + ...args, + }); + }, + delete(args = {}) { + return this.makeRequest({ + method: "delete", + ...args, + }); + }, + getCredential({ + credentialId, ...args + } = {}) { + return this.makeRequest({ + path: `/credentials/${credentialId}`, + ...args, + }); + }, + createCredential(args = {}) { + return this.post({ + path: "/credentials", + ...args, + }); + }, + updateCredential({ + credentialId, ...args + } = {}) { + return this.put({ + path: `/credentials/${credentialId}`, + ...args, + }); + }, + deleteCredential({ + credentialId, ...args + } = {}) { + return this.delete({ + path: `/credentials/${credentialId}`, + ...args, + }); + }, + searchGroups(args = {}) { + return this.post({ + path: "/issuer/groups/search", + ...args, + }); + }, + searchCredentials(args = {}) { + return this.post({ + path: "/credentials/search", + apiVersion: constants.API.V2, + ...args, + }); + }, + async *getIterations({ + resourceFn, + resourceFnArgs, + resourceName, + max = constants.DEFAULT_MAX, + }) { + let from = 0; + let resourcesCount = 0; + + while (true) { + const response = + await resourceFn({ + ...resourceFnArgs, + data: { + ...resourceFnArgs?.data, + page: { + size: constants.DEFAULT_LIMIT, + from, + }, + }, + }); + + const nextResources = resourceName && response[resourceName] || response; + + if (!nextResources?.length) { + console.log("No more resources found"); + return; + } + + for (const resource of nextResources) { + yield resource; + resourcesCount += 1; + + if (resourcesCount >= max) { + return; + } + } + + if (nextResources.length < constants.DEFAULT_LIMIT) { + console.log("Less resources than limit found"); + return; + } + + from += constants.DEFAULT_LIMIT; + } + }, + paginate(args = {}) { + return utils.iterate(this.getIterations(args)); + }, + }, +}; diff --git a/components/accredible/actions/create-credential/create-credential.mjs b/components/accredible/actions/create-credential/create-credential.mjs new file mode 100644 index 0000000000000..ecb7dd9282aec --- /dev/null +++ b/components/accredible/actions/create-credential/create-credential.mjs @@ -0,0 +1,61 @@ +import accredible from "../../accredible.app.mjs"; + +export default { + key: "accredible-create-credential", + name: "Create Credential", + description: "Issue a new credential to a given recipient. [See the documentation](https://accrediblecredentialapi.docs.apiary.io/#reference/credentials/credentials/create-a-new-credential)", + version: "0.0.1", + type: "action", + props: { + accredible, + email: { + propDefinition: [ + accredible, + "recipientEmail", + ], + }, + name: { + propDefinition: [ + accredible, + "recipientName", + ], + }, + groupId: { + propDefinition: [ + accredible, + "groupId", + ], + }, + credentialData: { + propDefinition: [ + accredible, + "credentialData", + ], + }, + }, + async run({ $ }) { + const { + accredible, + email, + name, + groupId, + credentialData, + } = this; + + const response = await accredible.createCredential({ + $, + data: { + credential: { + recipient: { + email, + name, + }, + group_id: groupId, + ...credentialData, + }, + }, + }); + $.export("$summary", `Successfully created credential with ID \`${response.credential.id}\`.`); + return response; + }, +}; diff --git a/components/accredible/actions/delete-credential/delete-credential.mjs b/components/accredible/actions/delete-credential/delete-credential.mjs new file mode 100644 index 0000000000000..075038f03c63e --- /dev/null +++ b/components/accredible/actions/delete-credential/delete-credential.mjs @@ -0,0 +1,31 @@ +import accredible from "../../accredible.app.mjs"; + +export default { + key: "accredible-delete-credential", + name: "Delete Credential", + description: "Remove a specific credential from the system. [See the documentation](https://accrediblecredentialapi.docs.apiary.io/#reference/credentials/credential/delete-a-credential)", + version: "0.0.1", + type: "action", + props: { + accredible, + credentialId: { + propDefinition: [ + accredible, + "credentialId", + ], + }, + }, + async run({ $ }) { + const { + accredible, + credentialId, + } = this; + + const response = await accredible.deleteCredential({ + $, + credentialId, + }); + $.export("$summary", `Successfully deleted credential with ID: \`${response.credential.id}\`.`); + return response; + }, +}; diff --git a/components/accredible/actions/update-credential/update-credential.mjs b/components/accredible/actions/update-credential/update-credential.mjs new file mode 100644 index 0000000000000..0b751ab8d76f6 --- /dev/null +++ b/components/accredible/actions/update-credential/update-credential.mjs @@ -0,0 +1,70 @@ +import accredible from "../../accredible.app.mjs"; + +export default { + key: "accredible-update-credential", + name: "Update Credential", + description: "Modify the details of an existing credential. [See the documentation](https://accrediblecredentialapi.docs.apiary.io/#reference/credentials/credential/update-a-credential)", + version: "0.0.1", + type: "action", + props: { + accredible, + credentialId: { + propDefinition: [ + accredible, + "credentialId", + ], + }, + email: { + propDefinition: [ + accredible, + "recipientEmail", + ], + }, + name: { + propDefinition: [ + accredible, + "recipientName", + ], + }, + groupId: { + optional: true, + propDefinition: [ + accredible, + "groupId", + ], + }, + credentialData: { + propDefinition: [ + accredible, + "credentialData", + ], + }, + }, + async run({ $ }) { + const { + accredible, + credentialId, + email, + name, + groupId, + credentialData, + } = this; + + const response = await accredible.updateCredential({ + $, + credentialId, + data: { + credential: { + recipient: { + email, + name, + }, + group_id: groupId, + ...credentialData, + }, + }, + }); + $.export("$summary", `Successfully updated credential with ID: \`${response.credential.id}\`.`); + return response; + }, +}; diff --git a/components/accredible/app/accredible.app.ts b/components/accredible/app/accredible.app.ts deleted file mode 100644 index e838244493345..0000000000000 --- a/components/accredible/app/accredible.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "accredible", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/accredible/common/constants.mjs b/components/accredible/common/constants.mjs new file mode 100644 index 0000000000000..88a6f14fc33fb --- /dev/null +++ b/components/accredible/common/constants.mjs @@ -0,0 +1,18 @@ +const BASE_URL = "https://api.accredible.com"; +const API = { + V1: "/v1", + V2: "/v2", +}; +const LAST_ISSUED_ON = "lastIssuedOn"; +const LAST_UPDATED_AT = "lastUpdatedAt"; +const DEFAULT_LIMIT = 50; +const DEFAULT_MAX = 600; + +export default { + BASE_URL, + API, + DEFAULT_LIMIT, + DEFAULT_MAX, + LAST_ISSUED_ON, + LAST_UPDATED_AT, +}; diff --git a/components/accredible/common/utils.mjs b/components/accredible/common/utils.mjs new file mode 100644 index 0000000000000..cf6006750a290 --- /dev/null +++ b/components/accredible/common/utils.mjs @@ -0,0 +1,20 @@ +async function iterate(iterations) { + const items = []; + for await (const item of iterations) { + items.push(item); + } + return items; +} + +function getDateFormatted(dateStr, yearsAgo = 0) { + const date = dateStr && new Date(dateStr) || new Date(); + const year = date.getFullYear() - yearsAgo; + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${year}-${month}-${day}`; +} + +export default { + iterate, + getDateFormatted, +}; diff --git a/components/accredible/package.json b/components/accredible/package.json index 931c3496dc99d..b2ec58ea7ffbc 100644 --- a/components/accredible/package.json +++ b/components/accredible/package.json @@ -1,16 +1,21 @@ { "name": "@pipedream/accredible", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream Accredible Components", - "main": "dist/app/accredible.app.mjs", + "main": "accredible.app.mjs", "keywords": [ "pipedream", "accredible" ], - "files": ["dist"], + "files": [ + "dist" + ], "homepage": "https://pipedream.com/apps/accredible", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } } diff --git a/components/accredible/sources/common/base.mjs b/components/accredible/sources/common/base.mjs new file mode 100644 index 0000000000000..4247cf51b8255 --- /dev/null +++ b/components/accredible/sources/common/base.mjs @@ -0,0 +1,14 @@ +import { ConfigurationError } from "@pipedream/platform"; +import app from "../../accredible.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + }, + methods: { + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, +}; diff --git a/components/accredible/sources/common/polling.mjs b/components/accredible/sources/common/polling.mjs new file mode 100644 index 0000000000000..be55a890cb6b0 --- /dev/null +++ b/components/accredible/sources/common/polling.mjs @@ -0,0 +1,81 @@ +import { + ConfigurationError, + DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import common from "./base.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + ...common, + props: { + ...common.props, + timer: { + type: "$.interface.timer", + label: "Polling schedule", + description: "How often to poll the API", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + ...common.methods, + setLastDate() { + throw new ConfigurationError("setLastDate is not implemented"); + }, + getLastDate() { + throw new ConfigurationError("getLastDate is not implemented"); + }, + getDateFormatted: utils.getDateFormatted, + sortFn(a, b) { + // Sort by created_at in ascending order by default to then call reverse() + return new Date(a.created_at) - new Date(b.created_at); + }, + getResourceName() { + throw new ConfigurationError("getResourceName is not implemented"); + }, + getResourceFn() { + throw new ConfigurationError("getResourceFn is not implemented"); + }, + getResourceFnArgs() { + throw new ConfigurationError("getResourceFnArgs is not implemented"); + }, + processEvent(resource) { + const meta = this.generateMeta(resource); + this.$emit(resource, meta); + }, + async processResources(resources) { + const { + sortFn, + processEvent, + } = this; + + const sortedResources = Array.from(resources).sort(sortFn); + + const [ + lastResource, + ] = Array.from(sortedResources).reverse(); + + this.setLastDate(lastResource); + + return sortedResources.forEach(processEvent); + }, + }, + async run() { + const { + app, + getResourceFn, + getResourceFnArgs, + getResourceName, + processResources, + } = this; + + const resources = await app.paginate({ + resourceFn: getResourceFn(), + resourceFnArgs: getResourceFnArgs(), + resourceName: getResourceName(), + }); + + processResources(resources); + }, +}; diff --git a/components/accredible/sources/issued-credential/issued-credential.mjs b/components/accredible/sources/issued-credential/issued-credential.mjs new file mode 100644 index 0000000000000..8a7db35894a33 --- /dev/null +++ b/components/accredible/sources/issued-credential/issued-credential.mjs @@ -0,0 +1,53 @@ +import common from "../common/polling.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "accredible-issued-credential", + name: "Issued Credential", + description: "This source triggers when a new credential is issued to a recipient. [See the documentation](https://accrediblecredentialapi.docs.apiary.io/#reference/credentials/search-credentials-v2/search-for-credentials).", + type: "source", + version: "0.0.1", + dedupe: "unique", + methods: { + ...common.methods, + setLastDate(resource) { + this.db.set(constants.LAST_ISSUED_ON, resource?.issued_on); + }, + getLastDate() { + return this.db.get(constants.LAST_ISSUED_ON); + }, + sortFn(a, b) { + // Sort by updated_at in ascending order + return new Date(a.updated_at) - new Date(b.updated_at); + }, + getResourceName() { + return "credentials"; + }, + getResourceFn() { + return this.app.searchCredentials; + }, + getResourceFnArgs() { + const lastDate = this.getLastDate(); + + const lastIssuedOn = lastDate + ? this.getDateFormatted(lastDate) + : this.getDateFormatted(undefined, 1); + + return { + data: { + query: { + "issued_on[gte]": lastIssuedOn, + }, + }, + }; + }, + generateMeta(resource) { + return { + id: resource.id, + summary: `Credential Issued: ${resource.recipient_name}`, + ts: Date.parse(resource.issued_on), + }; + }, + }, +}; diff --git a/components/accredible/sources/updated-credential/updated-credential.mjs b/components/accredible/sources/updated-credential/updated-credential.mjs new file mode 100644 index 0000000000000..ea9dcf9cdd694 --- /dev/null +++ b/components/accredible/sources/updated-credential/updated-credential.mjs @@ -0,0 +1,54 @@ +import common from "../common/polling.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "accredible-updated-credential", + name: "Updated Credential", + description: "Emit new event when an existing credential's details are updated or modified. [See the documentation](https://accrediblecredentialapi.docs.apiary.io/#reference/credentials/search-credentials-v2/search-for-credentials).", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + setLastDate(resource) { + this.db.set(constants.LAST_UPDATED_AT, resource?.issued_on); + }, + getLastDate() { + return this.db.get(constants.LAST_UPDATED_AT); + }, + sortFn(a, b) { + // Sort by updated_at in ascending order + return new Date(a.updated_at) - new Date(b.updated_at); + }, + getResourceName() { + return "credentials"; + }, + getResourceFn() { + return this.app.searchCredentials; + }, + getResourceFnArgs() { + const lastDate = this.getLastDate(); + + const lastUpdatedAt = lastDate + ? this.getDateFormatted(lastDate) + : this.getDateFormatted(undefined, 1); + + return { + data: { + query: { + "updated_at[gte]": lastUpdatedAt, + }, + }, + }; + }, + generateMeta(resource) { + const ts = Date.parse(resource.updated_at); + return { + id: `${resource.id}-${ts}`, + summary: `Credential Updated: ${resource.recipient_name}`, + ts, + }; + }, + }, +}; diff --git a/components/piggy/app/piggy.app.ts b/components/adobe_pdf_services/adobe_pdf_services.app.mjs similarity index 65% rename from components/piggy/app/piggy.app.ts rename to components/adobe_pdf_services/adobe_pdf_services.app.mjs index 9f8d28a54b484..c27515df7d710 100644 --- a/components/piggy/app/piggy.app.ts +++ b/components/adobe_pdf_services/adobe_pdf_services.app.mjs @@ -1,8 +1,6 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ +export default { type: "app", - app: "piggy", + app: "adobe_pdf_services", propDefinitions: {}, methods: { // this.$auth contains connected account data @@ -10,4 +8,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); \ No newline at end of file +}; diff --git a/components/adobe_pdf_services/package.json b/components/adobe_pdf_services/package.json new file mode 100644 index 0000000000000..4dc961b0bab11 --- /dev/null +++ b/components/adobe_pdf_services/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/adobe_pdf_services", + "version": "0.0.1", + "description": "Pipedream Adobe PDF Services Components", + "main": "adobe_pdf_services.app.mjs", + "keywords": [ + "pipedream", + "adobe_pdf_services" + ], + "homepage": "https://pipedream.com/apps/adobe_pdf_services", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/airops/app/airops.app.ts b/components/adobe_sign/adobe_sign.app.mjs similarity index 64% rename from components/airops/app/airops.app.ts rename to components/adobe_sign/adobe_sign.app.mjs index 855d5b0dffe0d..87557d38370ef 100644 --- a/components/airops/app/airops.app.ts +++ b/components/adobe_sign/adobe_sign.app.mjs @@ -1,8 +1,6 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ +export default { type: "app", - app: "airops", + app: "adobe_sign", propDefinitions: {}, methods: { // this.$auth contains connected account data @@ -10,4 +8,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); \ No newline at end of file +}; diff --git a/components/adobe_sign/package.json b/components/adobe_sign/package.json new file mode 100644 index 0000000000000..3f050e368972f --- /dev/null +++ b/components/adobe_sign/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/adobe_sign", + "version": "0.0.1", + "description": "Pipedream Adobe Acrobat Sign Components", + "main": "adobe_sign.app.mjs", + "keywords": [ + "pipedream", + "adobe_sign" + ], + "homepage": "https://pipedream.com/apps/adobe_sign", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/modeck/app/modeck.app.ts b/components/aftership/aftership.app.mjs similarity index 64% rename from components/modeck/app/modeck.app.ts rename to components/aftership/aftership.app.mjs index 659c873d79cb6..20ba19664ad51 100644 --- a/components/modeck/app/modeck.app.ts +++ b/components/aftership/aftership.app.mjs @@ -1,8 +1,6 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ +export default { type: "app", - app: "modeck", + app: "aftership", propDefinitions: {}, methods: { // this.$auth contains connected account data @@ -10,4 +8,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); \ No newline at end of file +}; \ No newline at end of file diff --git a/components/aftership/package.json b/components/aftership/package.json new file mode 100644 index 0000000000000..18417aa2e3b85 --- /dev/null +++ b/components/aftership/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/aftership", + "version": "0.0.1", + "description": "Pipedream AfterShip Components", + "main": "aftership.app.mjs", + "keywords": [ + "pipedream", + "aftership" + ], + "homepage": "https://pipedream.com/apps/aftership", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/airmeet/.gitignore b/components/airmeet/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/airmeet/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/airmeet/actions/add-authorized-attendee/add-authorized-attendee.mjs b/components/airmeet/actions/add-authorized-attendee/add-authorized-attendee.mjs new file mode 100644 index 0000000000000..cf1c0b00dfe09 --- /dev/null +++ b/components/airmeet/actions/add-authorized-attendee/add-authorized-attendee.mjs @@ -0,0 +1,50 @@ +import app from "../../airmeet.app.mjs"; + +export default { + name: "Add Authorized Attendee", + version: "0.0.1", + key: "airmeet-add-authorized-attendee", + description: "Add authorized Attendee. [See the documentation](https://help.airmeet.com/support/solutions/articles/82000467794-airmeet-public-apis-v2-0#5.1-Add-Authorized-Attendee)", + type: "action", + props: { + app, + airmeetId: { + propDefinition: [ + app, + "airmeetId", + ], + }, + email: { + type: "string", + label: "Email", + description: "Email of the atendee", + }, + firstName: { + type: "string", + label: "First name", + description: "First name of the atendee", + }, + lastName: { + type: "string", + label: "Last name", + description: "Last name of the atendee", + }, + }, + async run({ $ }) { + const response = await this.app.addAuthorizedAttendee({ + $, + airmeetId: this.airmeetId, + data: { + email: this.email, + firstName: this.firstName, + lastName: this.lastName, + }, + }); + + if (response) { + $.export("$summary", `Successfully added authorized attendee with email \`${response.email}\``); + } + + return response; + }, +}; diff --git a/components/airmeet/actions/common/constants.mjs b/components/airmeet/actions/common/constants.mjs new file mode 100644 index 0000000000000..04672ddf96276 --- /dev/null +++ b/components/airmeet/actions/common/constants.mjs @@ -0,0 +1,64 @@ +export default { + TIME_ZONES: [ + { + label: "America/New_York", + value: "America/New_York", + }, + { + label: "America/Los_Angeles", + value: "America/Los_Angeles", + }, + { + label: "America/Chicago", + value: "America/Chicago", + }, + { + label: "America/Denver", + value: "America/Denver", + }, + { + label: "America/Toronto", + value: "America/Toronto", + }, + { + label: "Europe/London", + value: "Europe/London", + }, + { + label: "Europe/Paris", + value: "Europe/Paris", + }, + { + label: "Asia/Tokyo", + value: "Asia/Tokyo", + }, + { + label: "Asia/Shanghai", + value: "Asia/Shanghai", + }, + { + label: "Australia/Sydney", + value: "Australia/Sydney", + }, + { + label: "Africa/Johannesburg", + value: "Africa/Johannesburg", + }, + { + label: "Pacific/Honolulu", + value: "Pacific/Honolulu", + }, + { + label: "Asia/Dubai", + value: "Asia/Dubai", + }, + { + label: "Asia/Kolkata", + value: "Asia/Kolkata", + }, + { + label: "Europe/Berlin", + value: "Europe/Berlin", + }, + ], +}; diff --git a/components/airmeet/actions/create-airmeet/create-airmeet.mjs b/components/airmeet/actions/create-airmeet/create-airmeet.mjs new file mode 100644 index 0000000000000..75012522a7387 --- /dev/null +++ b/components/airmeet/actions/create-airmeet/create-airmeet.mjs @@ -0,0 +1,69 @@ +import dayjs from "dayjs"; +import app from "../../airmeet.app.mjs"; +import constants from "../common/constants.mjs"; + +export default { + name: "Create Airmeet", + version: "0.0.1", + key: "airmeet-create-airmeet", + description: "Creates an airmeet. [See the documentation](https://help.airmeet.com/support/solutions/articles/82000467794-airmeet-public-apis-v2-0#6.1-Create-Airmeet)", + type: "action", + props: { + app, + hostEmail: { + type: "string", + label: "Host Email", + description: "Email of the host", + }, + eventName: { + type: "string", + label: "Event Name", + description: "Name of the event", + }, + shortDesc: { + type: "string", + label: "Short Description", + description: "Short description of the event", + }, + startTime: { + type: "string", + label: "Start Time", + description: "Start time for the event in milliseconds or ISO 8601. E.g. `1697458790918` or `2023-10-16T12:18:38+00:00`", + }, + endTime: { + type: "string", + label: "End Time", + description: "End time for the event in milliseconds or ISO 8601. E.g. `1697458790918` or `2023-10-16T12:18:38+00:00`", + }, + timezone: { + type: "string", + label: "Timezone", + description: "Timezone for the event in the canonical tz name. E.g. 'Asia/Kolkata'", + options: constants.TIME_ZONES, + }, + }, + async run({ $ }) { + const startTime = dayjs(this.startTime).valueOf(); + const endTime = dayjs(this.endTime).valueOf(); + + const response = await this.app.createAirmeet({ + $, + data: { + hostEmail: this.hostEmail, + eventName: this.eventName, + shortDesc: this.shortDesc, + timing: { + startTime: startTime, + endTime: endTime, + timezone: this.timezone, + }, + }, + }); + + if (response) { + $.export("$summary", `Successfully created airmeet with UUID \`${response.uuid}\``); + } + + return response; + }, +}; diff --git a/components/airmeet/airmeet.app.mjs b/components/airmeet/airmeet.app.mjs new file mode 100644 index 0000000000000..8c0dfe5d4977d --- /dev/null +++ b/components/airmeet/airmeet.app.mjs @@ -0,0 +1,66 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "airmeet", + propDefinitions: { + airmeetId: { + type: "string", + label: "Airmeet ID", + description: "The Airmeet ID", + async options() { + const { data: airmeets } = await this.getAirmeets(); + + return airmeets.map((airmeet) => ({ + value: airmeet.uid, + label: airmeet.name, + })); + }, + }, + }, + methods: { + _oauthAccessToken() { + return this.$auth.oauth_access_token; + }, + _region() { + return this.$auth.region; + }, + _apiUrl() { + return `https://${this._region()}.airmeet.com/prod`; + }, + async _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + ...args, + headers: { + ...args.headers, + "X-Airmeet-Access-Token": this._oauthAccessToken(), + }, + }); + }, + createAirmeet(args = {}) { + return this._makeRequest({ + path: "/airmeet", + method: "post", + ...args, + }); + }, + addAuthorizedAttendee({ + airmeetId, ...args + }) { + return this._makeRequest({ + path: `/airmeet/${airmeetId}/attendee`, + method: "post", + ...args, + }); + }, + getAirmeets(args = {}) { + return this._makeRequest({ + path: "/airmeets", + ...args, + }); + }, + }, +}; diff --git a/components/airmeet/app/airmeet.app.ts b/components/airmeet/app/airmeet.app.ts deleted file mode 100644 index f770d975a2baa..0000000000000 --- a/components/airmeet/app/airmeet.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "airmeet", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/airmeet/package.json b/components/airmeet/package.json index c35ac9a512cb2..2355ecad816b2 100644 --- a/components/airmeet/package.json +++ b/components/airmeet/package.json @@ -1,16 +1,19 @@ { "name": "@pipedream/airmeet", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Airmeet Components", - "main": "dist/app/airmeet.app.mjs", + "main": "airmeet.app.mjs", "keywords": [ "pipedream", "airmeet" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/airmeet", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "dayjs": "^1.11.10" } -} \ No newline at end of file +} diff --git a/components/airops/.gitignore b/components/airops/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/airops/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/airops/actions/run-workflow/run-workflow.mjs b/components/airops/actions/run-workflow/run-workflow.mjs new file mode 100644 index 0000000000000..21efa541816ef --- /dev/null +++ b/components/airops/actions/run-workflow/run-workflow.mjs @@ -0,0 +1,49 @@ +import app from "../../airops.app.mjs"; + +export default { + name: "Run Workflow", + version: "0.0.1", + key: "airops-run-workflow", + description: "Run a workflow of an app. See the [sync documentation](https://docs.airops.com/reference/create-execution) or [async documentation](https://docs.airops.com/reference/create-async-execution)", + type: "action", + props: { + app, + appId: { + label: "App ID", + type: "string", + description: "The app ID. [You can get you app ID here](https://docs.airops.com/reference/uuid)", + }, + inputs: { + label: "Inputs", + type: "object", + description: "Input fields for executing the app", + }, + asynchronous: { + label: "Run Asynchronous", + type: "boolean", + description: "Execute the workflow asynchronous", + default: false, + optional: true, + }, + }, + async run({ $ }) { + const inputs = typeof this.inputs === "string" + ? JSON.parse(this.inputs) + : this.inputs; + + const response = await this.app.runWorkflow({ + $, + appId: this.appId, + async: this.asynchronous, + data: { + inputs, + }, + }); + + if (response) { + $.export("$summary", "Successfully ran workflow"); + } + + return response; + }, +}; diff --git a/components/airops/airops.app.mjs b/components/airops/airops.app.mjs new file mode 100644 index 0000000000000..e5c7e6fdcf1e6 --- /dev/null +++ b/components/airops/airops.app.mjs @@ -0,0 +1,38 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "airops", + propDefinitions: {}, + methods: { + _workspaceApiKey() { + return this.$auth.workspace_api_key; + }, + _apiUrl() { + return "https://app.airops.com/public_api"; + }, + async _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + ...args, + headers: { + ...args.headers, + Authorization: `Bearer ${this._workspaceApiKey()}`, + }, + }); + }, + async runWorkflow({ + appId, async = false, ...args + }) { + return this._makeRequest({ + path: `/airops_apps/${appId}/${async + ? "async_execute" + : "execute"}`, + method: "post", + ...args, + }); + }, + }, +}; diff --git a/components/airops/package.json b/components/airops/package.json index ea724841f5962..974e8162d3438 100644 --- a/components/airops/package.json +++ b/components/airops/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/airops", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream AirOps Components", - "main": "dist/app/airops.app.mjs", + "main": "airops.app.mjs", "keywords": [ "pipedream", "airops" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/airops", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/airtable_oauth/actions/search-records/search-records.mjs b/components/airtable_oauth/actions/search-records/search-records.mjs index dcc8a97936de6..af5d9e58c50ad 100644 --- a/components/airtable_oauth/actions/search-records/search-records.mjs +++ b/components/airtable_oauth/actions/search-records/search-records.mjs @@ -5,7 +5,7 @@ export default { key: "airtable_oauth-search-records", name: "Search Records", description: "Searches for a record by field value. Search Field must accept string values. [See the documentation](https://airtable.com/developers/web/api/list-records)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { ...common.props, @@ -36,7 +36,7 @@ export default { }, async run({ $ }) { const params = { - filterByFormula: `FIND("${this.value}", ${this.fieldName})`, + filterByFormula: `FIND("${this.value}", {${this.fieldName}})`, }; if (this.searchFormula) { params.filterByFormula = `AND(${params.filterByFormula}, ${this.searchFormula})`; diff --git a/components/airtable_oauth/package.json b/components/airtable_oauth/package.json index 6ef5e0eafa651..b5cafc1a6391c 100644 --- a/components/airtable_oauth/package.json +++ b/components/airtable_oauth/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/airtable_oauth", - "version": "0.1.2", + "version": "0.2.0", "description": "Pipedream Airtable (OAuth) Components", "main": "airtable_oauth.app.mjs", "keywords": [ diff --git a/components/algomo/actions/send-message-to-chatbot/send-message-to-chatbot.mjs b/components/algomo/actions/send-message-to-chatbot/send-message-to-chatbot.mjs new file mode 100644 index 0000000000000..613e27c6e3a2b --- /dev/null +++ b/components/algomo/actions/send-message-to-chatbot/send-message-to-chatbot.mjs @@ -0,0 +1,47 @@ +import app from "../../algomo.app.mjs"; + +export default { + key: "algomo-send-message-to-chatbot", + name: "Send Message To Chatbot", + description: "Send a message to a specific Algomo chatbot and get the response. [See the documentation](https://help.algomo.com/docs/api-access/using%20our%20apis#api-call-for-bot-response-generation)", + version: "0.0.1", + type: "action", + props: { + app, + messageText: { + type: "string", + label: "Message Text", + description: "The message that you wish to generate a response for", + }, + conversationId: { + type: "string", + label: "Conversation ID", + description: "A user-defined identifier for threading conversations. This allows the bot to refer to previous messages when responding, providing more contextually relevant answers. If conversationId isn't provided, one will be generated for you", + optional: true, + }, + }, + methods: { + sendMessageToChatbot(args = {}) { + return this.app.post({ + path: "/get-bot-response", + ...args, + }); + }, + }, + run({ $: step }) { + const { + sendMessageToChatbot, + messageText, + conversationId, + } = this; + + return sendMessageToChatbot({ + step, + data: { + messageText, + conversationId, + }, + summary: (response) => `Successfully sent message to chatbot with conversation ID \`${response.metadata.conversationId}\``, + }); + }, +}; diff --git a/components/algomo/algomo.app.mjs b/components/algomo/algomo.app.mjs new file mode 100644 index 0000000000000..730df958730b0 --- /dev/null +++ b/components/algomo/algomo.app.mjs @@ -0,0 +1,65 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + +export default { + type: "app", + app: "algomo", + methods: { + exportSummary(step) { + if (!step?.export) { + throw new ConfigurationError("The summary method should be bind to the step object aka `$`"); + } + return (msg = "") => step.export(constants.SUMMARY_LABEL, msg); + }, + getUrl(path) { + return `${constants.BASE_URL}${constants.VERSION_PATH}${path}`; + }, + getHeaders(headers) { + return { + ...headers, + "Content-Type": "application/json", + "Accept": "application/json", + "Authorization": `Bearer ${this.$auth.api_token}`, + }; + }, + getData(data) { + return { + botId: this.$auth.chatbot_id, + ...data, + }; + }, + async makeRequest({ + step = this, path, headers, summary, data, ...args + } = {}) { + const { + getUrl, + getHeaders, + exportSummary, + getData, + } = this; + + const config = { + url: getUrl(path), + headers: getHeaders(headers), + data: getData(data), + ...args, + }; + + const response = await axios(step, config); + + if (typeof summary === "function") { + exportSummary(step)(summary(response)); + } + + return response; + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + }, +}; diff --git a/components/algomo/common/constants.mjs b/components/algomo/common/constants.mjs new file mode 100644 index 0000000000000..fb6ccd7adcf55 --- /dev/null +++ b/components/algomo/common/constants.mjs @@ -0,0 +1,9 @@ +const SUMMARY_LABEL = "$summary"; +const BASE_URL = "https://app.algomo.com"; +const VERSION_PATH = "/api/v2/external/api-access"; + +export default { + SUMMARY_LABEL, + BASE_URL, + VERSION_PATH, +}; diff --git a/components/algomo/package.json b/components/algomo/package.json new file mode 100644 index 0000000000000..964bb26d0a547 --- /dev/null +++ b/components/algomo/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/algomo", + "version": "0.1.0", + "description": "Pipedream Algomo Components", + "main": "algomo.app.mjs", + "keywords": [ + "pipedream", + "algomo" + ], + "homepage": "https://pipedream.com/apps/algomo", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} diff --git a/components/astica_ai/astica_ai.app.mjs b/components/astica_ai/astica_ai.app.mjs new file mode 100644 index 0000000000000..4ab3d1c8b6c38 --- /dev/null +++ b/components/astica_ai/astica_ai.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "astica_ai", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/astica_ai/package.json b/components/astica_ai/package.json new file mode 100644 index 0000000000000..7d3e3e6b8b8ae --- /dev/null +++ b/components/astica_ai/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/astica_ai", + "version": "0.0.1", + "description": "Pipedream astica.ai Components", + "main": "astica_ai.app.mjs", + "keywords": [ + "pipedream", + "astica_ai" + ], + "homepage": "https://pipedream.com/apps/astica_ai", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/beehiiv/actions/create-subscriber/create-subscriber.ts b/components/beehiiv/actions/create-subscriber/create-subscriber.ts index 390b57d81665a..892d5171b1510 100644 --- a/components/beehiiv/actions/create-subscriber/create-subscriber.ts +++ b/components/beehiiv/actions/create-subscriber/create-subscriber.ts @@ -5,7 +5,7 @@ export default defineAction({ name: "Create Subscriber", description: "Create a new subscriber. [See docs](https://www.beehiiv.com/developers/docs)", key: "beehiiv-create-subscriber", - version: "0.0.1", + version: "0.0.2", type: "action", props: { app, @@ -49,7 +49,7 @@ export default defineAction({ utm_source: this.utmSource, }; const response = await this.app.createSubscriber($, param); - $.export("$summary", `Successfully created a new subscriber with id: ${response.id}`); + $.export("$summary", `Successfully created a new subscriber with id: ${response.data.id}`); return response; }, }); diff --git a/components/beehiiv/actions/list-all-publications/list-all-publications.ts b/components/beehiiv/actions/list-all-publications/list-all-publications.ts index 4a3d02542b099..77328ac8cdafb 100644 --- a/components/beehiiv/actions/list-all-publications/list-all-publications.ts +++ b/components/beehiiv/actions/list-all-publications/list-all-publications.ts @@ -5,7 +5,7 @@ export default defineAction({ name: "List All Publications", description: "Get a list of all your publications. [See docs](https://www.beehiiv.com/developers/docs)", key: "beehiiv-list-all-publications", - version: "0.0.1", + version: "0.0.2", type: "action", props: { app, diff --git a/components/beehiiv/app/beehiiv.app.ts b/components/beehiiv/app/beehiiv.app.ts index 04c50693da738..09f1f273eea43 100644 --- a/components/beehiiv/app/beehiiv.app.ts +++ b/components/beehiiv/app/beehiiv.app.ts @@ -21,7 +21,7 @@ export default defineApp({ reactivateExisting: { type: "boolean", label: "Reactivate Existing", - description: "Whether or not to reactivate the subscriber if they have already unsubscribed. **This option should be used only if the subscriber is knowingly resubscribing**. default: `false`.", + description: "Whether or not to reactivate the subscriber if they have already unsubscribed. **This option should be used only if the subscriber is knowingly resubscribing**. default: `false`.", optional: true, }, sendWelcomeEmail: { @@ -38,12 +38,16 @@ export default defineApp({ }, }, methods: { - _getUrl(path) { - return `https://api.beehiiv.com/v1${path}`; + _getUrl(path, params = {}) { + let formattedPath = path; + for (const [key, value] of Object.entries(params)) { + formattedPath = formattedPath.replace(`{${key}}`, value); + } + return `https://api.beehiiv.com/v2${formattedPath}`; }, _getHeaders(headers = {}) { return { - "X-ApiKey": this.$auth.api_key, + "Authorization": `Bearer ${this.$auth.api_key}`, "Content-Type": "application/json", ...headers, }; @@ -51,14 +55,15 @@ export default defineApp({ _getRequestParams(opts: any) { return { ...opts, - url: this._getUrl(opts.path), + url: this._getUrl(opts.path, opts.params), headers: this._getHeaders(), }; }, async createSubscriber($ = this, param) { const response = await axios($, this._getRequestParams({ method: "POST", - path: "/subscribers", + path: "/publications/{publicationId}/subscriptions", + params: { publicationId: param.publication_id }, data: param, })); return response; @@ -68,7 +73,7 @@ export default defineApp({ method: "GET", path: "/publications", })); - return response; + return { publications: response.data }; }, async getPublicationOpts() { const { publications } = await this.listPublications(this); diff --git a/components/beehiiv/package.json b/components/beehiiv/package.json index 50949c519a8eb..310e2b32a1417 100644 --- a/components/beehiiv/package.json +++ b/components/beehiiv/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/beehiiv", - "version": "0.0.4", + "version": "0.0.5", "description": "Pipedream Beehiiv Components", "main": "dist/app/beehiiv.app.mjs", "keywords": [ diff --git a/components/booqable/actions/create-customer/create-customer.mjs b/components/booqable/actions/create-customer/create-customer.mjs new file mode 100644 index 0000000000000..6725eab899f96 --- /dev/null +++ b/components/booqable/actions/create-customer/create-customer.mjs @@ -0,0 +1,50 @@ +import app from "../../booqable.app.mjs"; + +export default { + key: "booqable-create-customer", + name: "Create Customer", + description: "Create a new customer in Booqable. [See the documentation](https://developers.booqable.com/#create-a-new-customer)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + propDefinition: [ + app, + "name", + ], + }, + email: { + propDefinition: [ + app, + "email", + ], + }, + }, + methods: { + createCustomer(args = {}) { + return this.app.post({ + path: "/customers", + ...args, + }); + }, + }, + run({ $: step }) { + const { + createCustomer, + name, + email, + } = this; + + return createCustomer({ + step, + data: { + customer: { + name, + email, + }, + }, + summary: (response) => `Successfully created customer with ID: \`${response.customer.id}\``, + }); + }, +}; diff --git a/components/booqable/actions/update-customer/update-customer.mjs b/components/booqable/actions/update-customer/update-customer.mjs new file mode 100644 index 0000000000000..6d931e29745d5 --- /dev/null +++ b/components/booqable/actions/update-customer/update-customer.mjs @@ -0,0 +1,62 @@ +import app from "../../booqable.app.mjs"; + +export default { + key: "booqable-update-customer", + name: "Update Customer", + description: "Updates the details of an existing customer in Booqable. [See the documentation](https://developers.booqable.com/#update-customer)", + version: "0.0.1", + type: "action", + props: { + app, + customerId: { + propDefinition: [ + app, + "customerId", + ], + }, + name: { + optional: true, + propDefinition: [ + app, + "name", + ], + }, + email: { + optional: true, + propDefinition: [ + app, + "email", + ], + }, + }, + methods: { + updateCustomer({ + customerId, ...args + } = {}) { + return this.app.put({ + path: `/customers/${customerId}`, + ...args, + }); + }, + }, + async run({ $: step }) { + const { + updateCustomer, + customerId, + name, + email, + } = this; + + return updateCustomer({ + step, + customerId, + data: { + customer: { + name, + email, + }, + }, + summary: (response) => `Successfully updated customer with ID: \`${response.customer.id}\``, + }); + }, +}; diff --git a/components/booqable/booqable.app.mjs b/components/booqable/booqable.app.mjs index 2e0b47172de77..9987b6025b5fd 100644 --- a/components/booqable/booqable.app.mjs +++ b/components/booqable/booqable.app.mjs @@ -1,11 +1,151 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import constants from "./common/constants.mjs"; +import utils from "./common/utils.mjs"; + export default { type: "app", app: "booqable", - propDefinitions: {}, + propDefinitions: { + name: { + type: "string", + label: "Name", + description: "The name of the customer", + }, + email: { + type: "string", + label: "Email", + description: "The email of the customer", + }, + customerId: { + type: "string", + label: "Customer ID", + description: "Select the customer to update", + async options({ page }) { + const { customers } = await this.listCustomers({ + params: { + page, + }, + }); + return customers.map(({ + name: label, id: value, + }) => ({ + label, + value, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + exportSummary(step) { + if (!step?.export) { + throw new ConfigurationError("The summary method should be bind to the step object aka `$`"); + } + return (msg = "") => step.export(constants.SUMMARY_LABEL, msg); + }, + getBaseUrl() { + const baseUrl = + constants.BASE_URL + .replace(constants.SUBDOMAIN_PLACEHOLDER, this.$auth.company_slug); + return `${baseUrl}${constants.VERSION_PATH}`; + }, + getParams(params) { + return { + api_key: this.$auth.api_key, + ...params, + }; + }, + async makeRequest({ + step = this, path, summary, params, ...args + } = {}) { + const config = { + ...args, + url: this.getBaseUrl() + path, + params: this.getParams(params), + }; + + const response = await axios(step, config); + + if (typeof(summary) === "function") { + this.exportSummary(step)(summary(response)); + } + + return response; + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + put(args = {}) { + return this.makeRequest({ + method: "put", + ...args, + }); + }, + listCustomers(args = {}) { + return this.makeRequest({ + path: "/customers", + ...args, + }); + }, + getCustomer({ + customerId, ...args + } = {}) { + return this.makeRequest({ + path: `/customers/${customerId}`, + ...args, + }); + }, + listOrders(args = {}) { + return this.makeRequest({ + path: "/orders", + ...args, + }); + }, + async *getIterations({ + resourceFn, + resourceFnArgs, + resourceName, + max = constants.DEFAULT_MAX, + }) { + let page = 1; + let resourcesCount = 0; + + while (true) { + const response = + await resourceFn({ + ...resourceFnArgs, + params: { + ...resourceFnArgs?.params, + page, + }, + }); + + console.log("response!!!", JSON.stringify(response, null, 2)); + const nextResources = resourceName && response[resourceName] || response; + + if (!nextResources?.length) { + console.log("No more resources found"); + return; + } + + for (const resource of nextResources) { + yield resource; + resourcesCount += 1; + + if (resourcesCount >= max) { + return; + } + } + + page += 1; + } + }, + paginate(args = {}) { + return utils.iterate(this.getIterations(args)); }, }, }; diff --git a/components/booqable/common/constants.mjs b/components/booqable/common/constants.mjs new file mode 100644 index 0000000000000..7f991ae4f88e8 --- /dev/null +++ b/components/booqable/common/constants.mjs @@ -0,0 +1,15 @@ +const SUMMARY_LABEL = "$summary"; +const SUBDOMAIN_PLACEHOLDER = "{subdomain}"; +const BASE_URL = `https://${SUBDOMAIN_PLACEHOLDER}.booqable.com`; +const VERSION_PATH = "/api/1"; +const DEFAULT_MAX = 600; +const DEFAULT_LIMIT = 60; + +export default { + SUMMARY_LABEL, + SUBDOMAIN_PLACEHOLDER, + BASE_URL, + VERSION_PATH, + DEFAULT_MAX, + DEFAULT_LIMIT, +}; diff --git a/components/booqable/common/utils.mjs b/components/booqable/common/utils.mjs new file mode 100644 index 0000000000000..903b2593ed3c2 --- /dev/null +++ b/components/booqable/common/utils.mjs @@ -0,0 +1,11 @@ +async function iterate(iterations) { + const items = []; + for await (const item of iterations) { + items.push(item); + } + return items; +} + +export default { + iterate, +}; diff --git a/components/booqable/package.json b/components/booqable/package.json new file mode 100644 index 0000000000000..5a8333dc6d17d --- /dev/null +++ b/components/booqable/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/booqable", + "version": "0.1.0", + "description": "Pipedream Booqable Components", + "main": "booqable.app.mjs", + "keywords": [ + "pipedream", + "booqable" + ], + "homepage": "https://pipedream.com/apps/booqable", + "author": "Pipedream (https://pipedream.com/)", + "dependencies": { + "@pipedream/platform": "^1.5.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/components/booqable/sources/common/base.mjs b/components/booqable/sources/common/base.mjs new file mode 100644 index 0000000000000..e53cd36143f57 --- /dev/null +++ b/components/booqable/sources/common/base.mjs @@ -0,0 +1,14 @@ +import { ConfigurationError } from "@pipedream/platform"; +import app from "../../booqable.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + }, + methods: { + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, +}; diff --git a/components/booqable/sources/common/polling.mjs b/components/booqable/sources/common/polling.mjs new file mode 100644 index 0000000000000..488f0e7f137ed --- /dev/null +++ b/components/booqable/sources/common/polling.mjs @@ -0,0 +1,62 @@ +import { + ConfigurationError, + DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import common from "./base.mjs"; + +export default { + ...common, + props: { + ...common.props, + timer: { + type: "$.interface.timer", + label: "Polling schedule", + description: "How often to poll the API", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + ...common.methods, + isResourceRelevant() { + return true; + }, + getResourceName() { + throw new ConfigurationError("getResourceName is not implemented"); + }, + getResourceFn() { + throw new ConfigurationError("getResourceFn is not implemented"); + }, + getResourceFnArgs() { + throw new ConfigurationError("getResourceFnArgs is not implemented"); + }, + processEvent(resource) { + const meta = this.generateMeta(resource); + this.$emit(resource, meta); + }, + async processResources(resources) { + Array.from(resources) + .reverse() + .filter(this.isResourceRelevant) + .forEach(this.processEvent); + }, + }, + async run() { + const { + app, + getResourceFn, + getResourceFnArgs, + getResourceName, + processResources, + } = this; + + const resources = await app.paginate({ + resourceFn: getResourceFn(), + resourceFnArgs: getResourceFnArgs(), + resourceName: getResourceName(), + }); + + processResources(resources); + }, +}; diff --git a/components/booqable/sources/new-customer/new-customer.mjs b/components/booqable/sources/new-customer/new-customer.mjs new file mode 100644 index 0000000000000..9120276eb758a --- /dev/null +++ b/components/booqable/sources/new-customer/new-customer.mjs @@ -0,0 +1,35 @@ +import common from "../common/polling.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "booqable-new-customer", + name: "New Customer", + description: "Emits a new event anytime there is a new customer. [See the documentation](https://developers.booqable.com/#list-all-customers)", + type: "source", + version: "0.0.1", + dedupe: "unique", + methods: { + ...common.methods, + getResourceName() { + return "customers"; + }, + getResourceFn() { + return this.app.listCustomers; + }, + getResourceFnArgs() { + return { + params: { + per: constants.DEFAULT_LIMIT, + }, + }; + }, + generateMeta(resource) { + return { + id: resource.id, + summary: `New Customer: ${resource.name}`, + ts: Date.parse(resource.created_at), + }; + }, + }, +}; diff --git a/components/booqable/sources/order-reserved/order-reserved.mjs b/components/booqable/sources/order-reserved/order-reserved.mjs new file mode 100644 index 0000000000000..6038f451e272e --- /dev/null +++ b/components/booqable/sources/order-reserved/order-reserved.mjs @@ -0,0 +1,39 @@ +import common from "../common/polling.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "booqable-order-reserved", + name: "Order Reserved", + description: "Emits an event when an order changes status to reserved in Booqable. [See the documentation](https://developers.booqable.com/#list-all-orders)", + type: "source", + version: "0.0.1", + dedupe: "unique", + methods: { + ...common.methods, + isResourceRelevant(resource) { + return resource?.status === "reserved"; + }, + getResourceName() { + return "orders"; + }, + getResourceFn() { + return this.app.listOrders; + }, + getResourceFnArgs() { + return { + params: { + per: constants.DEFAULT_LIMIT, + }, + }; + }, + generateMeta(resource) { + const ts = Date.parse(resource.updated_at); + return { + id: `${resource.id}-${ts}`, + summary: `Order ${resource.number} was reserved`, + ts, + }; + }, + }, +}; diff --git a/components/brilliant_directories/brilliant_directories.app.mjs b/components/brilliant_directories/brilliant_directories.app.mjs new file mode 100644 index 0000000000000..064f14bded019 --- /dev/null +++ b/components/brilliant_directories/brilliant_directories.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "brilliant_directories", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/brilliant_directories/package.json b/components/brilliant_directories/package.json new file mode 100644 index 0000000000000..bc4aa94e2c05e --- /dev/null +++ b/components/brilliant_directories/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/brilliant_directories", + "version": "0.0.1", + "description": "Pipedream Brilliant Directories Components", + "main": "brilliant_directories.app.mjs", + "keywords": [ + "pipedream", + "brilliant_directories" + ], + "homepage": "https://pipedream.com/apps/brilliant_directories", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/browse_ai/browse_ai.app.mjs b/components/browse_ai/browse_ai.app.mjs new file mode 100644 index 0000000000000..9106b6d871b7e --- /dev/null +++ b/components/browse_ai/browse_ai.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "browse_ai", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/browse_ai/package.json b/components/browse_ai/package.json new file mode 100644 index 0000000000000..c5121dfb4c590 --- /dev/null +++ b/components/browse_ai/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/browse_ai", + "version": "0.0.1", + "description": "Pipedream Browse AI Components", + "main": "browse_ai.app.mjs", + "keywords": [ + "pipedream", + "browse_ai" + ], + "homepage": "https://pipedream.com/apps/browse_ai", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/builderall_mailingboss/builderall_mailingboss.app.mjs b/components/builderall_mailingboss/builderall_mailingboss.app.mjs new file mode 100644 index 0000000000000..c86b8d3447315 --- /dev/null +++ b/components/builderall_mailingboss/builderall_mailingboss.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "builderall_mailingboss", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/builderall_mailingboss/package.json b/components/builderall_mailingboss/package.json new file mode 100644 index 0000000000000..d61567396c335 --- /dev/null +++ b/components/builderall_mailingboss/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/builderall_mailingboss", + "version": "0.0.1", + "description": "Pipedream Builderall Mailingboss Components", + "main": "builderall_mailingboss.app.mjs", + "keywords": [ + "pipedream", + "builderall_mailingboss" + ], + "homepage": "https://pipedream.com/apps/builderall_mailingboss", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/callpage/callpage.app.mjs b/components/callpage/callpage.app.mjs new file mode 100644 index 0000000000000..1aff0ae69010a --- /dev/null +++ b/components/callpage/callpage.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "callpage", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/callpage/package.json b/components/callpage/package.json new file mode 100644 index 0000000000000..b501e80ee5e51 --- /dev/null +++ b/components/callpage/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/callpage", + "version": "0.0.1", + "description": "Pipedream CallPage Components", + "main": "callpage.app.mjs", + "keywords": [ + "pipedream", + "callpage" + ], + "homepage": "https://pipedream.com/apps/callpage", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/centralstationcrm/actions/create-deal/create-deal.mjs b/components/centralstationcrm/actions/create-deal/create-deal.mjs new file mode 100644 index 0000000000000..cd3e7c3b937f7 --- /dev/null +++ b/components/centralstationcrm/actions/create-deal/create-deal.mjs @@ -0,0 +1,75 @@ +import centralstationcrm from "../../centralstationcrm.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "centralstationcrm-create-deal", + name: "Create Deal", + description: "Creates a new deal in CentralStationCRM. [See the documentation](https://api.centralstationcrm.net/api-docs/index.html)", + version: "0.0.1", + type: "action", + props: { + centralstationcrm, + name: { + type: "string", + label: "Name", + description: "Name of the deal", + }, + value: { + type: "string", + label: "Value", + description: "Value of the deal", + }, + valueType: { + type: "string", + label: "Value Type", + description: "The type of billing", + options: constants.VALUE_TYPE, + }, + targetDate: { + type: "string", + label: "Target Date", + description: "The date when the deal shall be won", + }, + valueCount: { + type: "string", + label: "Value Count", + description: "Only relevant if the value_type is not `total`", + optional: true, + }, + currentState: { + type: "string", + label: "Current State", + description: "State of the deal", + options: constants.CURRENT_STATE, + optional: true, + }, + responsibleUserId: { + propDefinition: [ + centralstationcrm, + "responsibleUserId", + ], + }, + }, + async run({ $ }) { + const { deal } = await this.centralstationcrm.createDeal({ + data: { + deal: { + name: this.name, + value: this.value, + value_type: this.valueType, + target_date: this.targetDate, + value_count: this.valueCount, + current_state: this.currentState, + user_id: this.responsibleUserId, + }, + }, + $, + }); + + if (deal?.id) { + $.export("summary", `Successully created deal with ID ${deal.id}.`); + } + + return deal; + }, +}; diff --git a/components/centralstationcrm/actions/create-person/create-person.mjs b/components/centralstationcrm/actions/create-person/create-person.mjs new file mode 100644 index 0000000000000..93cf408222753 --- /dev/null +++ b/components/centralstationcrm/actions/create-person/create-person.mjs @@ -0,0 +1,70 @@ +import centralstationcrm from "../../centralstationcrm.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "centralstationcrm-create-person", + name: "Create Person", + description: "Creates a new person in CentralStationCRM. [See the documentation](https://api.centralstationcrm.net/api-docs/index.html)", + version: "0.0.1", + type: "action", + props: { + centralstationcrm, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the person", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the person", + }, + title: { + type: "string", + label: "Title", + description: "The title of the person", + optional: true, + }, + gender: { + type: "string", + label: "Gender", + description: "The gender of the person", + options: constants.GENDER, + optional: true, + }, + background: { + type: "string", + label: "Background", + description: "Details about the person eg. hobbies", + optional: true, + }, + responsibleUserId: { + propDefinition: [ + centralstationcrm, + "responsibleUserId", + ], + }, + }, + async run({ $ }) { + const { person } = await this.centralstationcrm.createPerson({ + data: { + person: { + first_name: this.firstName, + name: this.lastName, + title: this.title, + gender: this.gender, + background: this.background, + user_id: this.responsibleUserId, + }, + }, + $, + }); + + if (person?.id) { + $.export("summary", `Successully created person with ID ${person.id}.`); + } + + return person; + }, +}; diff --git a/components/centralstationcrm/centralstationcrm.app.mjs b/components/centralstationcrm/centralstationcrm.app.mjs new file mode 100644 index 0000000000000..dd1b13a72042b --- /dev/null +++ b/components/centralstationcrm/centralstationcrm.app.mjs @@ -0,0 +1,101 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + +export default { + type: "app", + app: "centralstationcrm", + propDefinitions: { + responsibleUserId: { + type: "string", + label: "Responsible User", + description: "Identifier of the responsible user", + optional: true, + async options({ page }) { + const users = await this.listUsers({ + params: { + page: page + 1, + active: "true", + }, + }); + return users?.map(({ user }) => ({ + value: user.id, + label: `${user.first} ${user.name}`, + })) || []; + }, + }, + }, + methods: { + _baseUrl() { + return `https://${this.$auth.account_name}.centralstationcrm.net/api`; + }, + _headers() { + return { + "X-apikey": `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + listUsers(args = {}) { + return this._makeRequest({ + path: "/users.json", + ...args, + }); + }, + listPeople(args = {}) { + return this._makeRequest({ + path: "/people.json", + ...args, + }); + }, + listDeals(args = {}) { + return this._makeRequest({ + path: "/deals.json", + ...args, + }); + }, + createPerson(args = {}) { + return this._makeRequest({ + path: "/people.json", + method: "POST", + ...args, + }); + }, + createDeal(args = {}) { + return this._makeRequest({ + path: "/deals.json", + method: "POST", + ...args, + }); + }, + async *paginate({ + resourceFn, params = {}, + }) { + params = { + ...params, + page: 1, + perpage: constants.DEFAULT_LIMIT, + }; + let total = 0; + + do { + const items = await resourceFn({ + params, + }); + for (const item of items) { + yield item; + } + total = items?.length; + params.page++; + } while (total === params.perpage); + }, + }, +}; diff --git a/components/centralstationcrm/common/constants.mjs b/components/centralstationcrm/common/constants.mjs new file mode 100644 index 0000000000000..9c852d5ac294c --- /dev/null +++ b/components/centralstationcrm/common/constants.mjs @@ -0,0 +1,65 @@ +const DEFAULT_LIMIT = 25; + +const GENDER = [ + { + value: "gender_unknown", + label: "Unknown", + }, + { + value: "male_user", + label: "Male", + }, + { + value: "female_user", + label: "Female", + }, + { + value: "family_user", + label: "Family", + }, + { + value: "diverse_user", + label: "Diverse", + }, +]; + +const VALUE_TYPE = [ + "total", + "monthly", + "hourly", + "daily", +]; + +const CURRENT_STATE = [ + { + value: "open_0", + label: "New", + }, + { + value: "open_25", + label: "25%", + }, + { + value: "open_50", + label: "50%", + }, + { + value: "open_75", + label: "75%", + }, + { + value: "won", + label: "Won", + }, + { + value: "lost", + label: "Lost", + }, +]; + +export default { + DEFAULT_LIMIT, + GENDER, + VALUE_TYPE, + CURRENT_STATE, +}; diff --git a/components/centralstationcrm/package.json b/components/centralstationcrm/package.json new file mode 100644 index 0000000000000..2b1218edac9a0 --- /dev/null +++ b/components/centralstationcrm/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/centralstationcrm", + "version": "0.1.0", + "description": "Pipedream CentralStationCRM Components", + "main": "centralstationcrm.app.mjs", + "keywords": [ + "pipedream", + "centralstationcrm" + ], + "homepage": "https://pipedream.com/apps/centralstationcrm", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" + } +} diff --git a/components/centralstationcrm/sources/common/base.mjs b/components/centralstationcrm/sources/common/base.mjs new file mode 100644 index 0000000000000..b541fa5531c80 --- /dev/null +++ b/components/centralstationcrm/sources/common/base.mjs @@ -0,0 +1,67 @@ +import centralstationcrm from "../../centralstationcrm.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + centralstationcrm, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + await this.processEvents(25); + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getResourceFn() { + throw new Error("getResourceFn is not implemented"); + }, + getResourceType() { + throw new Error("getResourceType is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + async processEvents(max) { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + let count = 0; + + const items = this.centralstationcrm.paginate({ + resourceFn: this.getResourceFn(), + }); + + for await (const item of items) { + const resource = item[this.getResourceType()]; + const ts = Date.parse(resource.created_at); + if (ts > lastTs) { + const meta = this.generateMeta(resource); + this.$emit(resource, meta); + if (ts > maxTs) { + maxTs = ts; + } + count++; + if (max && count >= max) { + break; + } + } + } + + this._setLastTs(maxTs); + }, + }, + async run() { + await this.processEvents(); + }, +}; diff --git a/components/centralstationcrm/sources/new-deal-created/new-deal-created.mjs b/components/centralstationcrm/sources/new-deal-created/new-deal-created.mjs new file mode 100644 index 0000000000000..3f2f73bdd7858 --- /dev/null +++ b/components/centralstationcrm/sources/new-deal-created/new-deal-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "centralstationcrm-new-deal-created", + name: "New Deal Created", + description: "Emit new event when a new deal is created in CentralStationCRM.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.centralstationcrm.listDeals; + }, + getResourceType() { + return "deal"; + }, + generateMeta(deal) { + return { + id: deal.id, + summary: deal.name, + ts: Date.parse(deal.created_at), + }; + }, + }, +}; diff --git a/components/centralstationcrm/sources/new-person-created/new-person-created.mjs b/components/centralstationcrm/sources/new-person-created/new-person-created.mjs new file mode 100644 index 0000000000000..8b2580bfd74e3 --- /dev/null +++ b/components/centralstationcrm/sources/new-person-created/new-person-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "centralstationcrm-new-person-created", + name: "New Person Created", + description: "Emit new event when a new person is created in CentralStationCRM.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.centralstationcrm.listPeople; + }, + getResourceType() { + return "person"; + }, + generateMeta(person) { + return { + id: person.id, + summary: `${person.first_name} ${person.name}`, + ts: Date.parse(person.created_at), + }; + }, + }, +}; diff --git a/components/chmeetings/chmeetings.app.mjs b/components/chmeetings/chmeetings.app.mjs new file mode 100644 index 0000000000000..7cc4513d6983c --- /dev/null +++ b/components/chmeetings/chmeetings.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "chmeetings", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/chmeetings/package.json b/components/chmeetings/package.json new file mode 100644 index 0000000000000..9777b846770fd --- /dev/null +++ b/components/chmeetings/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/chmeetings", + "version": "0.0.1", + "description": "Pipedream ChMeetings Components", + "main": "chmeetings.app.mjs", + "keywords": [ + "pipedream", + "chmeetings" + ], + "homepage": "https://pipedream.com/apps/chmeetings", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs b/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs new file mode 100644 index 0000000000000..fa59c5021de1d --- /dev/null +++ b/components/clevertap/actions/create-or-update-user/create-or-update-user.mjs @@ -0,0 +1,57 @@ +import app from "../../clevertap.app.mjs"; + +export default { + name: "Create Or Update User", + version: "0.0.1", + key: "clevertap-create-or-update-user", + description: "Create or update an user. [See the documentation](https://developer.clevertap.com/docs/upload-user-profiles-api)", + type: "action", + props: { + app, + email: { + type: "string", + label: "Email", + description: "Email of the user", + }, + name: { + type: "string", + label: "Name", + description: "Name of the user", + }, + phone: { + type: "string", + label: "Phone Number, e.g +15555555555", + description: "Phone number of the user", + }, + identity: { + type: "string", + label: "Identity", + description: "Identifier of the user", + }, + }, + async run({ $ }) { + const response = await this.app.uploadEvent({ + $, + data: { + d: [ + { + $source: "Pipedream", + type: "profile", + identity: this.identity, + profileData: { + Name: this.name, + Email: this.email, + Phone: this.phone, + }, + }, + ], + }, + }); + + if (response.status === "success") { + $.export("$summary", "Successfully created or updated user"); + } + + return response; + }, +}; diff --git a/components/clevertap/actions/upload-events/upload-events.mjs b/components/clevertap/actions/upload-events/upload-events.mjs new file mode 100644 index 0000000000000..1857f3c825c6b --- /dev/null +++ b/components/clevertap/actions/upload-events/upload-events.mjs @@ -0,0 +1,53 @@ +import app from "../../clevertap.app.mjs"; + +export default { + name: "Upload Events", + version: "0.0.1", + key: "clevertap-upload-events", + description: "Upload events. [See the documentation](https://developer.clevertap.com/docs/upload-events-api)", + type: "action", + props: { + app, + eventName: { + type: "string", + label: "Event Name", + description: "Name of the event", + }, + identity: { + type: "string", + label: "Identity", + description: "Identifier of the user", + }, + eventData: { + label: "Event Data", + type: "object", + description: "The event data to be uploaded. E.g. `{ \"Product name\": \"Casio Watch\", \"Category\": \"Mens Watch\" }`", + }, + }, + async run({ $ }) { + const eventData = typeof this.eventData === "string" + ? JSON.parse(this.eventData) + : this.eventData; + + const response = await this.app.uploadEvent({ + $, + data: { + d: [ + { + $source: "Pipedream", + type: "event", + evtName: this.eventName, + identity: this.identity, + evtData: eventData, + }, + ], + }, + }); + + if (response.status === "success") { + $.export("$summary", "Successfully uploaded event"); + } + + return response; + }, +}; diff --git a/components/clevertap/clevertap.app.mjs b/components/clevertap/clevertap.app.mjs index e3f69ba26f4ca..608198ca82db4 100644 --- a/components/clevertap/clevertap.app.mjs +++ b/components/clevertap/clevertap.app.mjs @@ -1,11 +1,41 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "clevertap", propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _projectId() { + return this.$auth.project_id; + }, + _passCode() { + return this.$auth.pass_code; + }, + _region() { + return this.$auth.region; + }, + _apiUrl() { + return `https://${this._region()}.clevertap.com/1`; + }, + async _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + ...args, + headers: { + ...args.headers, + "X-CleverTap-Account-Id": this._projectId(), + "X-CleverTap-Passcode": this._passCode(), + }, + }); + }, + async uploadEvent(args = {}) { + return this._makeRequest({ + path: "/upload", + method: "post", + ...args, + }); }, }, }; diff --git a/components/clevertap/package.json b/components/clevertap/package.json index 00c36e7c7765e..a4c4ed44a5aa7 100644 --- a/components/clevertap/package.json +++ b/components/clevertap/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/clevertap", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream CleverTap Components", "main": "clevertap.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/click2mail2/click2mail2.app.mjs b/components/click2mail2/click2mail2.app.mjs new file mode 100644 index 0000000000000..9e268359f16b4 --- /dev/null +++ b/components/click2mail2/click2mail2.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "click2mail2", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/click2mail2/package.json b/components/click2mail2/package.json new file mode 100644 index 0000000000000..2672ffdca9cba --- /dev/null +++ b/components/click2mail2/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/click2mail2", + "version": "0.0.1", + "description": "Pipedream Click2Mail Components", + "main": "click2mail2.app.mjs", + "keywords": [ + "pipedream", + "click2mail2" + ], + "homepage": "https://pipedream.com/apps/click2mail2", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/clockwork_recruiting/actions/add-person-address/add-person-address.mjs b/components/clockwork_recruiting/actions/add-person-address/add-person-address.mjs new file mode 100644 index 0000000000000..a353993759072 --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-address/add-person-address.mjs @@ -0,0 +1,89 @@ +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-address", + name: "Add Person Address", + version: "0.0.1", + description: "Create an address to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Addresses/post_people__person_id__addresses)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + city: { + type: "string", + label: "City", + description: "The address' city.", + }, + country: { + type: "string", + label: "Country", + description: "The address' country.", + optional: true, + }, + postalCode: { + type: "string", + label: "Postal Code", + description: "The address' postal code.", + optional: true, + }, + state: { + type: "string", + label: "State", + description: "The address' state.", + }, + street: { + type: "string", + label: "Street", + description: "The address' street.", + optional: true, + }, + street2: { + type: "string", + label: "Street 2", + description: "The address' street 2.", + optional: true, + }, + location: { + type: "string", + label: "Location", + description: "The address' type.", + optional: true, + }, + regionId: { + propDefinition: [ + app, + "regionId", + ], + optional: true, + }, + }, + async run({ $ }) { + const { + app, + personId, + regionId, + street2, + ...data + } = this; + + const response = await app.createAddress({ + $, + personId, + data: { + address: { + ...data, + region_id: regionId, + street_2: street2, + }, + }, + }); + + $.export("$summary", `Successfully created new address with ID ${response.personAddress?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/add-person-email/add-person-email.mjs b/components/clockwork_recruiting/actions/add-person-email/add-person-email.mjs new file mode 100644 index 0000000000000..c4235f56c211e --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-email/add-person-email.mjs @@ -0,0 +1,48 @@ +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-email", + name: "Add Person Email", + version: "0.0.1", + description: "Create an email address to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Emails)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + address: { + type: "string", + label: "Email", + description: "The email address.", + optional: true, + }, + location: { + type: "string", + label: "Location", + description: "The email's type.", + optional: true, + }, + }, + async run({ $ }) { + const { + app, + personId, + ...data + } = this; + + const response = await app.createEmail({ + $, + personId, + data: { + email: data, + }, + }); + + $.export("$summary", `Successfully created new email with ID ${response.personEmailAddress?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/add-person-linkedin/add-person-linkedin.mjs b/components/clockwork_recruiting/actions/add-person-linkedin/add-person-linkedin.mjs new file mode 100644 index 0000000000000..ad25f1483623a --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-linkedin/add-person-linkedin.mjs @@ -0,0 +1,37 @@ +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-linkedin", + name: "Add Linkedin URL", + version: "0.0.1", + description: "Add a linkedin URL to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Linkedin/post_people__person_id__linkedin_urls)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + linkedinUrl: { + type: "string", + label: "Linkedin URL", + description: "The url of the person's linkedin.", + }, + }, + async run({ $ }) { + const response = await this.app.createLinkedinUrl({ + $, + personId: this.personId, + data: { + linkedin_url: { + url: this.linkedinUrl, + }, + }, + }); + + $.export("$summary", `Successfully created new linkedin URL with ID ${response.personLinkedinUrl?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/add-person-phone-number/add-person-phone-number.mjs b/components/clockwork_recruiting/actions/add-person-phone-number/add-person-phone-number.mjs new file mode 100644 index 0000000000000..1228d261382d0 --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-phone-number/add-person-phone-number.mjs @@ -0,0 +1,51 @@ +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-phone-number", + name: "Add Phone Number", + version: "0.0.1", + description: "Add a phone number to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Phone%20Numbers/post_people__person_id__phone_numbers)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + digits: { + type: "string", + label: "Digits", + description: "The digits of the phone number.", + }, + extension: { + type: "string", + label: "Extension", + description: "The extension of the phone number.", + }, + location: { + type: "string", + label: "Type", + description: "The type of the phone number.", + }, + }, + async run({ $ }) { + const { + app, + personId, + ...data + } = this; + + const response = await app.createPhoneNumber({ + $, + personId, + data: { + phone_number: data, + }, + }); + + $.export("$summary", `Successfully created new phone number with ID ${response.personPhoneNumber?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/add-person-position/add-person-position.mjs b/components/clockwork_recruiting/actions/add-person-position/add-person-position.mjs new file mode 100644 index 0000000000000..7f74d6bea0da0 --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-position/add-person-position.mjs @@ -0,0 +1,84 @@ +import { ConfigurationError } from "@pipedream/platform"; +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-position", + name: "Add Position", + version: "0.0.1", + description: "Add a position to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Positions/post_people__person_id__positions)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + title: { + type: "string", + label: "Title", + description: "The title of the position.", + optional: true, + }, + startMonth: { + type: "integer", + label: "Start Month", + description: "The month that the person will start the position.", + optional: true, + }, + startYear: { + type: "integer", + label: "Start Year", + description: "The year that the person will start the position.", + optional: true, + }, + endMonth: { + type: "integer", + label: "End Month", + description: "The month that the person will leave the position.", + optional: true, + }, + endYear: { + type: "integer", + label: "End Year", + description: "The year that the person will leave the position.", + optional: true, + }, + companyId: { + propDefinition: [ + app, + "companyId", + ], + optional: true, + }, + companyName: { + type: "string", + label: "Company Name", + description: "The name of the company where the position is open. **If `Company Id` is set, this field will be overwritten**", + optional: true, + }, + }, + async run({ $ }) { + const { + app, + personId, + ...data + } = this; + + if (!Object.entries(data).length) { + throw new ConfigurationError("At least one field must be provided."); + } + + const response = await app.createPosition({ + $, + personId, + data: { + position: data, + }, + }); + + $.export("$summary", `Successfully created new position with ID ${response.position?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/add-person-tag/add-person-tag.mjs b/components/clockwork_recruiting/actions/add-person-tag/add-person-tag.mjs new file mode 100644 index 0000000000000..5562e0046a102 --- /dev/null +++ b/components/clockwork_recruiting/actions/add-person-tag/add-person-tag.mjs @@ -0,0 +1,47 @@ +import app from "../../clockwork_recruiting.app.mjs"; + +export default { + key: "clockwork_recruiting-add-person-tag", + name: "Add Person Tag", + version: "0.0.1", + description: "Add a tag to a specific person. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/Person%20Tags)", + type: "action", + props: { + app, + personId: { + propDefinition: [ + app, + "personId", + ], + }, + tagId: { + propDefinition: [ + app, + "tagIds", + ], + label: "Tag Id", + description: "The id of the tag you want to add.", + type: "string", + }, + }, + async run({ $ }) { + const { + app, + personId, + tagId, + } = this; + + const response = await app.addPersonTag({ + $, + personId, + data: { + person_tag: { + tag_id: tagId, + }, + }, + }); + + $.export("$summary", `Successfully created new tag with ID ${response.personTag?.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/actions/create-person/create-person.mjs b/components/clockwork_recruiting/actions/create-person/create-person.mjs new file mode 100644 index 0000000000000..b1317e416a666 --- /dev/null +++ b/components/clockwork_recruiting/actions/create-person/create-person.mjs @@ -0,0 +1,91 @@ +import app from "../../clockwork_recruiting.app.mjs"; +import { parseArray } from "../../common/utils.mjs"; + +export default { + key: "clockwork_recruiting-create-person", + name: "Create A Person", + version: "0.0.1", + description: "Create a new person with provided data. [See the documentation](https://app.swaggerhub.com/apis-docs/clockwork-recruiting/cw-public-api/3.0.0#/People/post_people)", + type: "action", + props: { + app, + assistantName: { + type: "string", + label: "Assistant Name", + description: "The person's assistant name.", + optional: true, + }, + biography: { + type: "string", + label: "Biography", + description: "The Biography that is pulled over by a Third Party or entered by a User.", + optional: true, + }, + doNotContact: { + type: "boolean", + label: "Do Not Contact", + description: "If the candidate is in the middle of a process then you can toggle Do Not Contact.", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The person's name.", + optional: true, + }, + nickName: { + type: "string", + label: "Nickname", + description: "The person's nickname.", + optional: true, + }, + noRelocation: { + type: "boolean", + label: "No Relocation", + description: "If the candidate is willing to Relocate.", + optional: true, + }, + skypeName: { + type: "string", + label: "Skype Name", + description: "The name of the skype account.", + optional: true, + }, + tagNames: { + propDefinition: [ + app, + "tagNames", + ], + optional: true, + }, + tagIds: { + propDefinition: [ + app, + "tagIds", + ], + optional: true, + }, + }, + async run({ $ }) { + const { + app, + tagNames, + tagIds, + ...data + } = this; + + const response = await app.createPerson({ + $, + data: { + person: { + ...data, + tag_names: tagNames && parseArray(tagNames), + tag_ids: tagIds && parseArray(tagIds), + }, + }, + }); + + $.export("$summary", `Successfully created new person with ID ${response.id}`); + return response; + }, +}; diff --git a/components/clockwork_recruiting/clockwork_recruiting.app.mjs b/components/clockwork_recruiting/clockwork_recruiting.app.mjs index cee53a0932f1e..28d7c33b4ccf9 100644 --- a/components/clockwork_recruiting/clockwork_recruiting.app.mjs +++ b/components/clockwork_recruiting/clockwork_recruiting.app.mjs @@ -1,11 +1,237 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "clockwork_recruiting", - propDefinitions: {}, + propDefinitions: { + companyId: { + type: "string", + label: "Company Id", + description: "The id of the company where the position is open.", + async options({ page }) { + const { companies } = await this.listCompanies({ + params: { + limit: LIMIT, + offset: page * LIMIT, + }, + }); + + return companies.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + personId: { + type: "string", + label: "Person Id", + description: "The id of the person you want to use.", + async options({ page }) { + const { people } = await this.listPeople({ + params: { + limit: LIMIT, + offset: page * LIMIT, + }, + }); + + return people.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + regionId: { + type: "string", + label: "Region Id", + description: "The address' region", + async options({ page }) { + const { regions } = await this.listRegions({ + params: { + limit: LIMIT, + offset: page * LIMIT, + }, + }); + + return regions.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + tagNames: { + type: "string[]", + label: "Tag Names", + description: "A list of tag names.", + async options({ page }) { + const { tags } = await this.listTags({ + params: { + limit: LIMIT, + offset: page * LIMIT, + }, + }); + + return tags.map(({ name }) => name); + }, + }, + tagIds: { + type: "string[]", + label: "Tag Ids", + description: "A list of tag Ids.", + async options({ page }) { + const { tags } = await this.listTags({ + params: { + limit: LIMIT, + offset: page * LIMIT, + }, + }); + + return tags.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _firmName() { + return this.$auth.firm_name; + }, + _firmServiceKey() { + return this.$auth.firm_service_key; + }, + _apiKey() { + return this.$auth.api_key; + }, + _apiSecret() { + return this.$auth.api_secret; + }, + _apiUrl() { + return `https://api.clockworkrecruiting.com/v3.0/${this._firmName()}`; + }, + _getHeaders() { + const auth_hash = Buffer.from(`${this._apiKey()}:${this._apiSecret()}`).toString("base64"); + return { + "Authorization": `Token ${auth_hash}`, + "X-API-Key": this._firmServiceKey(), + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + headers: this._getHeaders(), + ...opts, + }; + + return axios($, config); + }, + addPersonTag({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/tags`, + ...args, + }); + }, + createAddress({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/addresses`, + ...args, + }); + }, + createEmail({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/email_addresses`, + ...args, + }); + }, + createLinkedinUrl({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/linkedin_urls`, + ...args, + }); + }, + createPerson(args = {}) { + return this._makeRequest({ + method: "POST", + path: "people", + ...args, + }); + }, + createPhoneNumber({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/phone_numbers`, + ...args, + }); + }, + createPosition({ + personId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `people/${personId}/positions`, + ...args, + }); + }, + listCompanies(args = {}) { + return this._makeRequest({ + path: "companies", + ...args, + }); + }, + listPeople(args = {}) { + return this._makeRequest({ + path: "people", + ...args, + }); + }, + listRegions(args = {}) { + return this._makeRequest({ + path: "regions", + ...args, + }); + }, + listTags(args = {}) { + return this._makeRequest({ + path: "tags", + ...args, + }); + }, + createHook(args = {}) { + return this._makeRequest({ + method: "POST", + path: "webhooks", + ...args, + }); + }, + deleteHook(hookId) { + return this._makeRequest({ + method: "DELETE", + path: `webhooks/${hookId}`, + }); }, }, }; diff --git a/components/clockwork_recruiting/common/constants.mjs b/components/clockwork_recruiting/common/constants.mjs new file mode 100644 index 0000000000000..ea830c15a04cb --- /dev/null +++ b/components/clockwork_recruiting/common/constants.mjs @@ -0,0 +1 @@ +export const LIMIT = 100; diff --git a/components/clockwork_recruiting/common/utils.mjs b/components/clockwork_recruiting/common/utils.mjs new file mode 100644 index 0000000000000..d8768ca91e214 --- /dev/null +++ b/components/clockwork_recruiting/common/utils.mjs @@ -0,0 +1,6 @@ +export const parseArray = (arr) => { + if (!Array.isArray(arr)) { + return JSON.parse(arr); + } + return arr; +}; diff --git a/components/clockwork_recruiting/package.json b/components/clockwork_recruiting/package.json index e0defcf6c4d88..ae83ca99a8eb6 100644 --- a/components/clockwork_recruiting/package.json +++ b/components/clockwork_recruiting/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/clockwork_recruiting", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Clockwork Recruiting Components", "main": "clockwork_recruiting.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/clockwork_recruiting/sources/candidate-status-change/candidate-status-change.mjs b/components/clockwork_recruiting/sources/candidate-status-change/candidate-status-change.mjs new file mode 100644 index 0000000000000..e8a3e5a58f64d --- /dev/null +++ b/components/clockwork_recruiting/sources/candidate-status-change/candidate-status-change.mjs @@ -0,0 +1,67 @@ +import app from "../../clockwork_recruiting.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "clockwork_recruiting-candidate-status-change", + name: "New Candidate Status Change (Instant)", + description: "Emit new event when a candidate status is changed.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + app, + http: "$.interface.http", + db: "$.service.db", + }, + hooks: { + async activate() { + const { id } = await this.app.createHook({ + data: { + hook_url: this.http.endpoint, + }, + }); + + this._setHookId(id); + }, + async deactivate() { + const id = this._getHookId("hookId"); + await this.app.deleteHook(id); + }, + }, + methods: { + emitEvent(body) { + const { + event_name: event, + candidacy, + } = body; + + if (event === "candidacy_updated") { + const { + id, status_id: statusId, updated_at: updatedAt, + } = candidacy; + + this.$emit(body, { + id: id + statusId, + summary: `The status of the candidate with id ${id} has been updated!`, + ts: new Date(updatedAt), + }); + } + }, + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + _getLastStatusId() { + return this.db.get("lastStatusId"); + }, + _setLastStatusId(statusId) { + this.db.set("lastStatusId", statusId); + }, + }, + async run({ body }) { + this.emitEvent(body); + }, + sampleEmit, +}; diff --git a/components/clockwork_recruiting/sources/candidate-status-change/test-event.mjs b/components/clockwork_recruiting/sources/candidate-status-change/test-event.mjs new file mode 100644 index 0000000000000..441795fa82935 --- /dev/null +++ b/components/clockwork_recruiting/sources/candidate-status-change/test-event.mjs @@ -0,0 +1,19 @@ +export default { + "candidacy":{ + "assessment_note_id":null, + "color_category":null, + "created_at":"2023-10-02T15:52:27Z", + "created_user_id":"12345678-233d-4fd5-9618-12345678343", + "id":"12345678-233d-4fd5-9618-12345678343", + "next_steps_note_id":null, + "overview_note_id":null, + "person_id":"12345678-233d-4fd5-9618-12345678343", + "project_id":"12345678-233d-4fd5-9618-12345678343", + "rank":14, + "status_id":"12345678-233d-4fd5-9618-12345678343", + "stoplight_status":"red", + "updated_at":"2023-10-02T15:54:34Z", + "updated_at_display":"2023-10-02T15:54:32Z" + }, + "event_name":"candidacy_updated" +} \ No newline at end of file diff --git a/components/cloudflare_r2/cloudflare_r2.app.mjs b/components/cloudflare_r2/cloudflare_r2.app.mjs new file mode 100644 index 0000000000000..3d950aa51fc2c --- /dev/null +++ b/components/cloudflare_r2/cloudflare_r2.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "cloudflare_r2", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/cloudflare_r2/package.json b/components/cloudflare_r2/package.json new file mode 100644 index 0000000000000..52cf0759c587d --- /dev/null +++ b/components/cloudflare_r2/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/cloudflare_r2", + "version": "0.0.1", + "description": "Pipedream Cloudflare R2 Components", + "main": "cloudflare_r2.app.mjs", + "keywords": [ + "pipedream", + "cloudflare_r2" + ], + "homepage": "https://pipedream.com/apps/cloudflare_r2", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/codereadr/codereadr.app.mjs b/components/codereadr/codereadr.app.mjs new file mode 100644 index 0000000000000..1e3789c92d46a --- /dev/null +++ b/components/codereadr/codereadr.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "codereadr", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/codereadr/package.json b/components/codereadr/package.json new file mode 100644 index 0000000000000..53fd27f8114ff --- /dev/null +++ b/components/codereadr/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/codereadr", + "version": "0.0.1", + "description": "Pipedream CodeREADr Components", + "main": "codereadr.app.mjs", + "keywords": [ + "pipedream", + "codereadr" + ], + "homepage": "https://pipedream.com/apps/codereadr", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/cometly/cometly.app.mjs b/components/cometly/cometly.app.mjs new file mode 100644 index 0000000000000..49a7755a6b44a --- /dev/null +++ b/components/cometly/cometly.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "cometly", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/cometly/package.json b/components/cometly/package.json new file mode 100644 index 0000000000000..cb9f19fc8b6fc --- /dev/null +++ b/components/cometly/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/cometly", + "version": "0.0.1", + "description": "Pipedream Cometly Components", + "main": "cometly.app.mjs", + "keywords": [ + "pipedream", + "cometly" + ], + "homepage": "https://pipedream.com/apps/cometly", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/convenia/convenia.app.mjs b/components/convenia/convenia.app.mjs new file mode 100644 index 0000000000000..5f112bc67b20a --- /dev/null +++ b/components/convenia/convenia.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "convenia", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/convenia/package.json b/components/convenia/package.json new file mode 100644 index 0000000000000..15b9d85c116e2 --- /dev/null +++ b/components/convenia/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/convenia", + "version": "0.0.1", + "description": "Pipedream Convenia Components", + "main": "convenia.app.mjs", + "keywords": [ + "pipedream", + "convenia" + ], + "homepage": "https://pipedream.com/apps/convenia", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/copperx/actions/create-customer/create-customer.mjs b/components/copperx/actions/create-customer/create-customer.mjs new file mode 100644 index 0000000000000..c3bfce8e0be90 --- /dev/null +++ b/components/copperx/actions/create-customer/create-customer.mjs @@ -0,0 +1,178 @@ +import copperx from "../../copperx.app.mjs"; + +export default { + key: "copperx-create-customer", + name: "Create Customer", + version: "0.0.1", + description: "Create a new customer [See the documentation](https://copperx.readme.io/reference/customercontroller_create)", + type: "action", + props: { + copperx, + email: { + type: "string", + label: "Email", + description: "The customer's email.", + }, + name: { + type: "string", + label: "Name", + description: "The customer's name.", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The customer's phone.", + optional: true, + }, + addressLine1: { + type: "string", + label: "Address Line 1", + description: "The customer's address line 1.", + }, + addressLine2: { + type: "string", + label: "Address Line 2", + description: "The customer's address line 2.", + optional: true, + }, + addressCity: { + type: "string", + label: "City", + description: "The city of the customer's address.", + }, + addressState: { + type: "string", + label: "State", + description: "The state of the customer's address.", + }, + addressPostalCode: { + type: "string", + label: "Postal Code", + description: "The postal code of the customer's address.", + }, + addressCountry: { + type: "string", + label: "Country", + description: "The country of the customer's address.", + }, + customerReferenceId: { + type: "string", + label: "Customer Reference Id.", + description: "The customer's reference Id.", + optional: true, + }, + shippingAddressName: { + type: "string", + label: "Shipping Address Name", + description: "The name of the shipping address receiver.", + optional: true, + }, + shippingAddressEmail: { + type: "string", + label: "Shipping Address Email", + description: "The email of the shipping address.", + optional: true, + }, + shippingAddressPhone: { + type: "string", + label: "Shipping Address Phone", + description: "The phone of the shipping address.", + optional: true, + }, + shippingAddressLine1: { + type: "string", + label: "Shipping Address Line 1", + description: "The shipping address line 1.", + optional: true, + }, + shippingAddressLine2: { + type: "string", + label: "Shipping Address Line 2", + description: "The shipping address line 2.", + optional: true, + }, + shippingAddressCity: { + type: "string", + label: "Shipping Address City", + description: "The city of the shipping address.", + optional: true, + }, + shippingAddressState: { + type: "string", + label: "Shipping Address State", + description: "The state of the shipping address.", + optional: true, + }, + shippingAddressPostalCode: { + type: "string", + label: "Shipping Address Postal Code", + description: "The postal code of the shipping address.", + optional: true, + }, + shippingAddressCountry: { + type: "string", + label: "Shipping Address Country", + description: "The country of the shipping address.", + optional: true, + }, + metadata: { + type: "object", + label: "Metadata", + description: "Customer's metadata.", + optional: true, + }, + }, + async run({ $ }) { + const { + copperx, + addressLine1, + addressLine2, + addressCity, + addressState, + addressPostalCode, + addressCountry, + shippingAddressName, + shippingAddressEmail, + shippingAddressPhone, + shippingAddressLine1, + shippingAddressLine2, + shippingAddressCity, + shippingAddressState, + shippingAddressPostalCode, + shippingAddressCountry, + ...data + } = this; + + const response = await copperx.createCustomer({ + $, + data: { + "address": { + "line1": addressLine1, + "line2": addressLine2, + "country": addressCountry, + "postalCode": addressPostalCode, + "state": addressState, + "city": addressCity, + }, + "shipping": { + "address": { + "country": shippingAddressCountry, + "postalCode": shippingAddressPostalCode, + "state": shippingAddressState, + "city": shippingAddressCity, + "line2": shippingAddressLine2, + "line1": shippingAddressLine1, + }, + "phone": shippingAddressPhone, + "email": shippingAddressEmail, + "name": shippingAddressName, + }, + ...data, + }, + }); + + $.export("$summary", `A new customer with Id: ${response.id} was successfully created!`); + return response; + }, +}; diff --git a/components/copperx/actions/create-invoice/create-invoice.mjs b/components/copperx/actions/create-invoice/create-invoice.mjs new file mode 100644 index 0000000000000..21c1f1bc3980d --- /dev/null +++ b/components/copperx/actions/create-invoice/create-invoice.mjs @@ -0,0 +1,97 @@ +import { + objectToArray, parseString, +} from "../../common/utils.mjs"; +import copperx from "../../copperx.app.mjs"; + +export default { + key: "copperx-create-invoice", + name: "Create Invoice", + version: "0.0.1", + description: "Create a new invoice [See the documentation](https://copperx.readme.io/reference/invoicecontroller_create)", + type: "action", + props: { + copperx, + description: { + type: "string", + label: "Description", + description: "The invoice's description.", + optional: true, + }, + customFields: { + type: "object", + label: "Custom Fields", + description: "The invoice's custom fields.", + optional: true, + }, + dueDate: { + type: "string", + label: "Due Date", + description: "The invoice's due date.", + optional: true, + }, + footer: { + type: "string", + label: "Footer", + description: "The additional invoice's footer.", + optional: true, + }, + fromInvoiceId: { + type: "string", + label: "From Invoice Id", + description: "The invoice Id of this invoice is from.", + optional: true, + }, + metadata: { + type: "object", + label: "Metadata", + description: "The additional invoice's metadata.", + optional: true, + }, + clientReferenceId: { + type: "string", + label: "Client Reference Id.", + description: "The client's reference Id.", + optional: true, + }, + customerId: { + propDefinition: [ + copperx, + "customerId", + ], + optional: true, + }, + lineItems: { + type: "string", + label: "Line Items", + description: "The line items to be used in checkout session [See the documentation to further information about the Line Items object](https://copperx.readme.io/reference/invoicecontroller_create).", + }, + paymentSetting: { + type: "string", + label: "Payment Setting", + description: "The list of chains allowed for the payment. If not provided, all chains supported by the organization are enabled. [See the documentation to further information about the Payment Settings object](https://copperx.readme.io/reference/invoicecontroller_create).", + optional: true, + }, + }, + async run({ $ }) { + const { + copperx, + customFields, + lineItems, + paymentSetting, + ...data + } = this; + + const response = await copperx.createInvoice({ + $, + data: { + customFields: customFields && objectToArray(customFields), + lineItems: lineItems && parseString(lineItems), + paymentSetting: paymentSetting && parseString(paymentSetting), + ...data, + }, + }); + + $.export("$summary", `A new invoice with Id: ${response.id} was successfully created!`); + return response; + }, +}; diff --git a/components/copperx/common/utils.mjs b/components/copperx/common/utils.mjs new file mode 100644 index 0000000000000..aa8a90cfc4c07 --- /dev/null +++ b/components/copperx/common/utils.mjs @@ -0,0 +1,30 @@ +import { ConfigurationError } from "@pipedream/platform"; + +export const objectToArray = (obj) => { + const objArray = []; + obj = parseString(obj); + + if (Object.entries(obj).length) { + for (const [ + key, + value, + ] of Object.entries(obj)) { + objArray.push({ + name: key, + value: value, + }); + } + } + return objArray; +}; + +export const parseString = (str) => { + try { + if (typeof str === "string") { + return JSON.parse(str); + } + return str; + } catch (e) { + throw new ConfigurationError(e); + } +}; diff --git a/components/copperx/copperx.app.mjs b/components/copperx/copperx.app.mjs index 43472a7e7ebcf..c15645ca8beac 100644 --- a/components/copperx/copperx.app.mjs +++ b/components/copperx/copperx.app.mjs @@ -1,11 +1,82 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "copperx", - propDefinitions: {}, + propDefinitions: { + customerId: { + type: "string", + label: "Customer Id", + description: "The id of the customer the invoice is for.", + async options({ page }) { + const { data } = await this.listCustomers({ + params: { + page: page + 1, + }, + }); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiUrl() { + return `https://${this.$auth.environment}/api/v1`; + }, + _getHeaders() { + return { + "Authorization": `Bearer ${this.$auth.api_key}`, + "accept": "application/json", + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + headers: this._getHeaders(), + ...opts, + }; + + return axios($, config); + }, + createCustomer(args = {}) { + return this._makeRequest({ + method: "POST", + path: "customers", + ...args, + }); + }, + createHook(args = {}) { + return this._makeRequest({ + method: "POST", + path: "webhook-endpoints", + ...args, + }); + }, + createInvoice(args = {}) { + return this._makeRequest({ + method: "POST", + path: "invoices", + ...args, + }); + }, + deleteHook(hookId) { + return this._makeRequest({ + method: "DELETE", + path: `webhook-endpoints/${hookId}`, + }); + }, + listCustomers(args = {}) { + return this._makeRequest({ + path: "customers", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/copperx/package.json b/components/copperx/package.json index 845e136aae561..ac0a2fd126e03 100644 --- a/components/copperx/package.json +++ b/components/copperx/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/copperx", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Copperx Components", "main": "copperx.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/copperx/sources/common/base.mjs b/components/copperx/sources/common/base.mjs new file mode 100644 index 0000000000000..5d289dc610ccf --- /dev/null +++ b/components/copperx/sources/common/base.mjs @@ -0,0 +1,51 @@ +import copperx from "../../copperx.app.mjs"; + +export default { + dedupe: "unique", + props: { + copperx, + http: "$.interface.http", + db: "$.service.db", + }, + hooks: { + async activate() { + const data = await this.copperx.createHook({ + data: { + description: "Pipedream-customer-subscription-created", + url: this.http.endpoint, + enabledEvents: { + events: this.getEvent(), + }, + }, + }); + + this._setHookId(data.id); + }, + async deactivate() { + const id = this._getHookId("hookId"); + await this.copperx.deleteHook(id); + }, + }, + methods: { + emitEvent(body) { + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta({ id }) { + return { + id, + summary: this.getSummary(id), + ts: new Date(), + }; + }, + }, + async run({ body }) { + this.emitEvent(body); + }, +}; diff --git a/components/copperx/sources/customer-subscription-created/customer-subscription-created.mjs b/components/copperx/sources/customer-subscription-created/customer-subscription-created.mjs new file mode 100644 index 0000000000000..fcc05f15c1e45 --- /dev/null +++ b/components/copperx/sources/customer-subscription-created/customer-subscription-created.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "copperx-customer-subscription-created", + name: "New Customer Subscription Created (Instant)", + description: "Emit new event when a new customer subcription is created.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "customer.subscription.created", + ]; + }, + getSummary(id) { + return `A new customer subscription with id: ${id} was created!`; + }, + }, + sampleEmit, +}; diff --git a/components/copperx/sources/customer-subscription-created/test-event.mjs b/components/copperx/sources/customer-subscription-created/test-event.mjs new file mode 100644 index 0000000000000..d8cad642d35b8 --- /dev/null +++ b/components/copperx/sources/customer-subscription-created/test-event.mjs @@ -0,0 +1,19 @@ +export default { + id: 'fdc5e9fb-52f8-4604-a8ec-0575c6996fa4', + apiVersion: '2023-01-11', + created: 1697735629533, + object: 'webhookEndpoint', + type: 'webhook_endpoint_test', + data: { + object: { + id: '366ff7e6-32af-4e2b-a94e-c6e8cab37762', + description: 'Pipedream-customer-subscription-created', + url: 'https://8d40d4079384e2e1ddd64e7af5fbd4b4.m.pipedream.net', + secret: 'wh_k8rxkaWJ0YN9kQGsqqb2lMNVQGjXpeEcwVIRNnvDJ0PleZwMhCXUepV8sg5zVjN6', + createdAt: '2023-10-19T17:13:30.922Z', + updatedAt: '2023-10-19T17:13:30.922Z', + enabledEvents: [Object], + isDisabled: false + } + } +} \ No newline at end of file diff --git a/components/copperx/sources/invoice-paid/invoice-paid.mjs b/components/copperx/sources/invoice-paid/invoice-paid.mjs new file mode 100644 index 0000000000000..509987818247e --- /dev/null +++ b/components/copperx/sources/invoice-paid/invoice-paid.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "copperx-invoice-paid", + name: "New Invoice Paid (Instant)", + description: "Emit new event when an invoice is paid.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "invoice.marked_as_paid", + ]; + }, + getSummary(id) { + return `A new payment with id: ${id} was created!`; + }, + }, + sampleEmit, +}; diff --git a/components/copperx/sources/invoice-paid/test-event.mjs b/components/copperx/sources/invoice-paid/test-event.mjs new file mode 100644 index 0000000000000..4c64aea1c0741 --- /dev/null +++ b/components/copperx/sources/invoice-paid/test-event.mjs @@ -0,0 +1,23 @@ +export default { + "id": "088fbde6-6464-486e-815d-a00734f213fb", + "apiVersion": "2023-01-11", + "created": 1697735924778, + "object": "webhookEndpoint", + "type": "webhook_endpoint_test", + "data": { + "object": { + "id": "3e2b5a91-521b-44ad-836e-e9bf44892a14", + "description": "Pipedream-customer-subscription-created", + "url": "https://e37d5e73f3b01d2043c1fa2f31a24875.m.pipedream.net", + "secret": "wh_NMDGsudIDW2AQ91TGEDQYzFIlWZSqdubyrgUQoVTBcjMh1j2vWPF7lPXm5w22vWq", + "createdAt": "2023-10-19T17:18:20.697Z", + "updatedAt": "2023-10-19T17:18:20.697Z", + "enabledEvents": { + "events": [ + "invoice.marked_as_paid" + ] + }, + "isDisabled": false + } + } +} \ No newline at end of file diff --git a/components/discogs/discogs.app.mjs b/components/discogs/discogs.app.mjs new file mode 100644 index 0000000000000..0c75866cc76b4 --- /dev/null +++ b/components/discogs/discogs.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "discogs", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/discogs/package.json b/components/discogs/package.json new file mode 100644 index 0000000000000..6d8eefcf6b1dd --- /dev/null +++ b/components/discogs/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/discogs", + "version": "0.0.1", + "description": "Pipedream Discogs Components", + "main": "discogs.app.mjs", + "keywords": [ + "pipedream", + "discogs" + ], + "homepage": "https://pipedream.com/apps/discogs", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/doppler/doppler.app.mjs b/components/doppler/doppler.app.mjs new file mode 100644 index 0000000000000..10b93849e531c --- /dev/null +++ b/components/doppler/doppler.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "doppler", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/doppler/package.json b/components/doppler/package.json new file mode 100644 index 0000000000000..aa7e54f4a84db --- /dev/null +++ b/components/doppler/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/doppler", + "version": "0.0.1", + "description": "Pipedream Doppler Components", + "main": "doppler.app.mjs", + "keywords": [ + "pipedream", + "doppler" + ], + "homepage": "https://pipedream.com/apps/doppler", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/elasticemail/elasticemail.app.mjs b/components/elasticemail/elasticemail.app.mjs new file mode 100644 index 0000000000000..d22006f411036 --- /dev/null +++ b/components/elasticemail/elasticemail.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "elasticemail", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/elasticemail/package.json b/components/elasticemail/package.json new file mode 100644 index 0000000000000..c679de4b99309 --- /dev/null +++ b/components/elasticemail/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/elasticemail", + "version": "0.0.1", + "description": "Pipedream ElasticEmail Components", + "main": "elasticemail.app.mjs", + "keywords": [ + "pipedream", + "elasticemail" + ], + "homepage": "https://pipedream.com/apps/elasticemail", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/emailable/emailable.app.mjs b/components/emailable/emailable.app.mjs new file mode 100644 index 0000000000000..6ff6fa26fd5fb --- /dev/null +++ b/components/emailable/emailable.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "emailable", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/emailable/package.json b/components/emailable/package.json new file mode 100644 index 0000000000000..e9ab915fda09d --- /dev/null +++ b/components/emailable/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/emailable", + "version": "0.0.1", + "description": "Pipedream Emailable Components", + "main": "emailable.app.mjs", + "keywords": [ + "pipedream", + "emailable" + ], + "homepage": "https://pipedream.com/apps/emailable", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/endorsal/endorsal.app.mjs b/components/endorsal/endorsal.app.mjs new file mode 100644 index 0000000000000..fdbf31a93fd77 --- /dev/null +++ b/components/endorsal/endorsal.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "endorsal", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/endorsal/package.json b/components/endorsal/package.json new file mode 100644 index 0000000000000..fca4c43dc5627 --- /dev/null +++ b/components/endorsal/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/endorsal", + "version": "0.0.1", + "description": "Pipedream Endorsal Components", + "main": "endorsal.app.mjs", + "keywords": [ + "pipedream", + "endorsal" + ], + "homepage": "https://pipedream.com/apps/endorsal", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/firebase_admin_sdk/actions/create-document/create-document.mjs b/components/firebase_admin_sdk/actions/create-document/create-document.mjs index a0486adee7c3b..b210ec4951c1c 100644 --- a/components/firebase_admin_sdk/actions/create-document/create-document.mjs +++ b/components/firebase_admin_sdk/actions/create-document/create-document.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-create-document", name: "Create Document", description: "Creates a New Document. [See the docs here](https://googleapis.dev/nodejs/firestore/latest/CollectionReference.html#add)", - version: "0.0.6", + version: "0.0.7", type: "action", props: { ...common.props, diff --git a/components/firebase_admin_sdk/actions/create-realtime-db-record/create-realtime-db-record.mjs b/components/firebase_admin_sdk/actions/create-realtime-db-record/create-realtime-db-record.mjs index 71fd2f96d8ea5..147c5f63eb431 100644 --- a/components/firebase_admin_sdk/actions/create-realtime-db-record/create-realtime-db-record.mjs +++ b/components/firebase_admin_sdk/actions/create-realtime-db-record/create-realtime-db-record.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-create-realtime-db-record", name: "Create Firebase Realtime Database Record", description: "Creates or replaces a child object within your Firebase Realtime Database. [See the docs here](https://firebase.google.com/docs/reference/js/database#update)", - version: "0.0.3", + version: "0.0.4", type: "action", props: { ...common.props, diff --git a/components/firebase_admin_sdk/actions/list-documents/list-documents.mjs b/components/firebase_admin_sdk/actions/list-documents/list-documents.mjs index a8a7cd62ad6e5..23b1587ee2340 100644 --- a/components/firebase_admin_sdk/actions/list-documents/list-documents.mjs +++ b/components/firebase_admin_sdk/actions/list-documents/list-documents.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-list-documents", name: "List Documents", description: "Lists documents in a collection. [See the docs here](https://googleapis.dev/nodejs/firestore/latest/CollectionReference.html#listDocuments)", - version: "0.0.3", + version: "0.0.4", type: "action", props: { ...common.props, diff --git a/components/firebase_admin_sdk/actions/replicate-event-firestore/replicate-event-firestore.mjs b/components/firebase_admin_sdk/actions/replicate-event-firestore/replicate-event-firestore.mjs index b0ec2d5828b47..cacf5f186c533 100644 --- a/components/firebase_admin_sdk/actions/replicate-event-firestore/replicate-event-firestore.mjs +++ b/components/firebase_admin_sdk/actions/replicate-event-firestore/replicate-event-firestore.mjs @@ -6,7 +6,7 @@ export default { key: "firebase_admin_sdk-replicate-event-firestore", name: "Save Event to Firestore", description: "Replicate event in Firestore", - version: "0.4.3", + version: "0.4.4", type: "action", props: { firebase_admin_sdk: { diff --git a/components/firebase_admin_sdk/actions/update-document/update-document.mjs b/components/firebase_admin_sdk/actions/update-document/update-document.mjs index 92d8a73567b5a..4f14c8d495012 100644 --- a/components/firebase_admin_sdk/actions/update-document/update-document.mjs +++ b/components/firebase_admin_sdk/actions/update-document/update-document.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-update-document", name: "Update Documents", description: "Updates a Document. [See the docs here](https://googleapis.dev/nodejs/firestore/latest/DocumentReference.html#update)", - version: "0.0.3", + version: "0.0.4", type: "action", props: { ...common.props, diff --git a/components/firebase_admin_sdk/firebase_admin_sdk.app.mjs b/components/firebase_admin_sdk/firebase_admin_sdk.app.mjs index f948143059947..33df00e83c102 100644 --- a/components/firebase_admin_sdk/firebase_admin_sdk.app.mjs +++ b/components/firebase_admin_sdk/firebase_admin_sdk.app.mjs @@ -77,6 +77,7 @@ export default { projectId, clientEmail, privateKey, + customDatabaseUrl, } = this.$auth; const formattedPrivateKey = privateKey.replace(/\\n/g, "\n"); return admin.initializeApp({ @@ -85,7 +86,7 @@ export default { clientEmail, privateKey: formattedPrivateKey, }), - databaseURL: `https://${projectId}-default-rtdb.${region}/`, + databaseURL: customDatabaseUrl || `https://${projectId}-default-rtdb.${region}/`, }); }, /** diff --git a/components/firebase_admin_sdk/package.json b/components/firebase_admin_sdk/package.json index 3a4177c86301f..6c5b4f7472d96 100644 --- a/components/firebase_admin_sdk/package.json +++ b/components/firebase_admin_sdk/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/firebase_admin_sdk", - "version": "0.0.6", + "version": "0.0.7", "description": "Pipedream Firebase Admin SDK Components", "main": "firebase_admin_sdk.app.mjs", "keywords": [ diff --git a/components/firebase_admin_sdk/sources/new-child-object/new-child-object.mjs b/components/firebase_admin_sdk/sources/new-child-object/new-child-object.mjs index 3cd353ec1035c..d09a779ac8be5 100644 --- a/components/firebase_admin_sdk/sources/new-child-object/new-child-object.mjs +++ b/components/firebase_admin_sdk/sources/new-child-object/new-child-object.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-new-child-object", name: "New Child Object in a Realtime Database", description: "Emit new event when a new child object is discovered within a specific path", - version: "0.0.5", + version: "0.0.6", type: "source", dedupe: "unique", props: { diff --git a/components/firebase_admin_sdk/sources/new-doc-in-firestore-collection/new-doc-in-firestore-collection.mjs b/components/firebase_admin_sdk/sources/new-doc-in-firestore-collection/new-doc-in-firestore-collection.mjs index 7e225fe83af76..262d6d6827a52 100644 --- a/components/firebase_admin_sdk/sources/new-doc-in-firestore-collection/new-doc-in-firestore-collection.mjs +++ b/components/firebase_admin_sdk/sources/new-doc-in-firestore-collection/new-doc-in-firestore-collection.mjs @@ -5,7 +5,7 @@ export default { key: "firebase_admin_sdk-new-doc-in-firestore-collection", name: "New Document in Firestore Collection", description: "Emit new event when a structured query returns new documents", - version: "0.0.5", + version: "0.0.6", type: "source", dedupe: "unique", props: { diff --git a/components/flodesk/actions/add-subscriber-to-segments/add-subscriber-to-segments.mjs b/components/flodesk/actions/add-subscriber-to-segments/add-subscriber-to-segments.mjs new file mode 100644 index 0000000000000..081556bae34bf --- /dev/null +++ b/components/flodesk/actions/add-subscriber-to-segments/add-subscriber-to-segments.mjs @@ -0,0 +1,39 @@ +import flodesk from "../../flodesk.app.mjs"; + +export default { + key: "flodesk-add-subscriber-to-segments", + name: "Add Subscriber To Segments", + description: "Add a subscriber to one or more segments in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/subscriber/operation/addSubscriberToSegments)", + version: "0.0.1", + type: "action", + props: { + flodesk, + subscriberId: { + propDefinition: [ + flodesk, + "subscriberId", + ], + }, + segmentIds: { + propDefinition: [ + flodesk, + "segmentIds", + ], + }, + }, + async run({ $ }) { + const response = await this.flodesk.addSubscriberToSegment({ + subscriberId: this.subscriberId, + data: { + segment_ids: this.segmentIds, + }, + $, + }); + + if (response?.id) { + $.export("$summary", "Successfully added subscriber to segment(s)."); + } + + return response; + }, +}; diff --git a/components/flodesk/actions/create-or-update-subscriber/create-or-update-subscriber.mjs b/components/flodesk/actions/create-or-update-subscriber/create-or-update-subscriber.mjs new file mode 100644 index 0000000000000..d345d1de21b0a --- /dev/null +++ b/components/flodesk/actions/create-or-update-subscriber/create-or-update-subscriber.mjs @@ -0,0 +1,89 @@ +import flodesk from "../../flodesk.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; +import lodash from "lodash"; + +export default { + key: "flodesk-create-or-update-subscriber", + name: "Create or Update Subscriber", + description: "Creates or updates a subscriber in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/subscriber/operation/createOrUpdateSubscriber)", + version: "0.0.1", + type: "action", + props: { + flodesk, + subscriberId: { + propDefinition: [ + flodesk, + "subscriberId", + ], + description: "The subscriber's id. Either `email` or `subscriberId` must be included.", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The subscriber's email. Either `email` or `subscriberId` must be included.", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The subscriber's first name.", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The subscriber's last name.", + optional: true, + }, + customFields: { + propDefinition: [ + flodesk, + "customFields", + ], + reloadProps: true, + }, + }, + async additionalProps() { + const props = {}; + if (!this.customFields?.length) { + return props; + } + for (const field of this.customFields) { + props[field.value] = { + type: "string", + label: `${field.label} Value`, + }; + } + return props; + }, + async run({ $ }) { + if (!this.subscriberId && !this.email) { + throw new ConfigurationError("Either `email` or `subscriberId` must be included."); + } + + const customFields = {}; + if (this.customFields?.length) { + for (const field of this.customFields) { + customFields[field.value] = this[field.value]; + } + } + + const response = await this.flodesk.createOrUpdateSubscriber({ + data: lodash.pickBy({ + id: this.subscriberId, + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + custom_fields: customFields, + }), + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully created or updated subscriber with ID ${response.id}`); + } + + return response; + }, +}; diff --git a/components/flodesk/actions/find-subscriber/find-subscriber.mjs b/components/flodesk/actions/find-subscriber/find-subscriber.mjs new file mode 100644 index 0000000000000..1d086064d5e7f --- /dev/null +++ b/components/flodesk/actions/find-subscriber/find-subscriber.mjs @@ -0,0 +1,29 @@ +import flodesk from "../../flodesk.app.mjs"; + +export default { + key: "flodesk-find-subscriber", + name: "Find Subscriber By Email", + description: "Find a subscriber by email address in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/subscriber/operation/retrieveSubscriber)", + version: "0.0.1", + type: "action", + props: { + flodesk, + email: { + type: "string", + label: "Email", + description: "Email address of the subscriber", + }, + }, + async run({ $ }) { + const response = await this.flodesk.findSubscriber({ + email: this.email, + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully retrieved subscriber with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/flodesk/flodesk.app.mjs b/components/flodesk/flodesk.app.mjs new file mode 100644 index 0000000000000..37de24f3b0c8b --- /dev/null +++ b/components/flodesk/flodesk.app.mjs @@ -0,0 +1,143 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "flodesk", + propDefinitions: { + subscriberId: { + type: "string", + label: "Subscriber", + description: "Identifier of the subscriber to update", + async options({ page }) { + const { data } = await this.listSubscribers({ + params: { + page: page + 1, + }, + }); + return data?.map(({ + id: value, email: label, + }) => ({ + value, + label, + })) || []; + }, + }, + segmentIds: { + type: "string[]", + label: "Segments", + description: "Array of segment Ids to add subscriber to", + async options({ page }) { + const { data } = await this.listSegments({ + params: { + page: page + 1, + }, + }); + return data?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + customFields: { + type: "string[]", + label: "Custom Fields", + description: "Custom fields to enter values for", + withLabel: true, + optional: true, + async options({ page }) { + const { data } = await this.listCustomFields({ + params: { + page: page + 1, + }, + }); + return data?.map(({ + key: value, label, + }) => ({ + value, + label, + })) || []; + }, + }, + }, + methods: { + _baseUrl() { + return "https://api.flodesk.com/v1"; + }, + _headers() { + return { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + createWebhook(args = {}) { + return this._makeRequest({ + path: "/webhooks", + method: "POST", + ...args, + }); + }, + deleteWebhook({ + hookId, ...args + }) { + return this._makeRequest({ + path: `/webhooks/${hookId}`, + method: "DELETE", + ...args, + }); + }, + listSegments(args = {}) { + return this._makeRequest({ + path: "/segments", + ...args, + }); + }, + listSubscribers(args = {}) { + return this._makeRequest({ + path: "/subscribers", + ...args, + }); + }, + listCustomFields(args = {}) { + return this._makeRequest({ + path: "/custom-fields", + ...args, + }); + }, + findSubscriber({ + email, ...args + }) { + return this._makeRequest({ + path: `/subscribers/${email}`, + ...args, + }); + }, + addSubscriberToSegment({ + subscriberId, ...args + }) { + return this._makeRequest({ + path: `/subscribers/${subscriberId}/segments`, + method: "POST", + ...args, + }); + }, + createOrUpdateSubscriber(args = {}) { + return this._makeRequest({ + path: "/subscribers", + method: "POST", + ...args, + }); + }, + }, +}; diff --git a/components/flodesk/package.json b/components/flodesk/package.json new file mode 100644 index 0000000000000..631c41717bec0 --- /dev/null +++ b/components/flodesk/package.json @@ -0,0 +1,19 @@ +{ + "name": "@pipedream/flodesk", + "version": "0.1.0", + "description": "Pipedream Flodesk Components", + "main": "flodesk.app.mjs", + "keywords": [ + "pipedream", + "flodesk" + ], + "homepage": "https://pipedream.com/apps/flodesk", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "lodash": "^4.17.21" + } +} diff --git a/components/flodesk/sources/common/base.mjs b/components/flodesk/sources/common/base.mjs new file mode 100644 index 0000000000000..27f41eabd46c3 --- /dev/null +++ b/components/flodesk/sources/common/base.mjs @@ -0,0 +1,48 @@ +import flodesk from "../../flodesk.app.mjs"; + +export default { + props: { + flodesk, + db: "$.service.db", + http: "$.interface.http", + }, + hooks: { + async activate() { + const response = await this.flodesk.createWebhook({ + data: { + name: "Pipedream Webhook", + post_url: this.http.endpoint, + events: this.getEvents(), + }, + }); + this._setHookId(response?.id); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.flodesk.deleteWebhook({ + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + getEvents() { + throw new Error("getEvents is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run(event) { + const { body } = event; + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, +}; diff --git a/components/flodesk/sources/subscriber-added-to-segment/subscriber-added-to-segment.mjs b/components/flodesk/sources/subscriber-added-to-segment/subscriber-added-to-segment.mjs new file mode 100644 index 0000000000000..a5536dc4b0595 --- /dev/null +++ b/components/flodesk/sources/subscriber-added-to-segment/subscriber-added-to-segment.mjs @@ -0,0 +1,28 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "flodesk-subscriber-added-to-segment", + name: "Subscriber Added To Segment", + description: "Emit new event when a subscriber is added to a segment in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/webhook-event/operation/subscriber.added_to_segment)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "subscriber.added_to_segment", + ]; + }, + generateMeta(event) { + return { + id: `${event.subscriber.id}-${event.segment.id}`, + summary: `Subscriber ${event.subscriber.id} added to segment ${event.segment.id}`, + ts: Date.parse(event.event_time), + }; + }, + }, + sampleEmit, +}; diff --git a/components/flodesk/sources/subscriber-added-to-segment/test-event.mjs b/components/flodesk/sources/subscriber-added-to-segment/test-event.mjs new file mode 100644 index 0000000000000..8dcea4482d434 --- /dev/null +++ b/components/flodesk/sources/subscriber-added-to-segment/test-event.mjs @@ -0,0 +1,30 @@ +export default { + "event_name": "string", + "event_time": "2019-08-24T14:15:22Z", + "subscriber": { + "id": "string", + "status": "active", + "email": "string", + "source": "manual", + "first_name": "string", + "last_name": "string", + "segments": [ + { + "id": "string", + "name": "string" + } + ], + "custom_fields": { + "property1": "string", + "property2": "string" + }, + "optin_ip": "string", + "optin_timestamp": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z" + }, + "segment": { + "id": "string", + "name": "string" + }, + "webhook_id": "string" +} \ No newline at end of file diff --git a/components/flodesk/sources/subscriber-created/subscriber-created.mjs b/components/flodesk/sources/subscriber-created/subscriber-created.mjs new file mode 100644 index 0000000000000..af34ca68dd8a6 --- /dev/null +++ b/components/flodesk/sources/subscriber-created/subscriber-created.mjs @@ -0,0 +1,28 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "flodesk-subscriber-created", + name: "Subscriber Created", + description: "Emit new event when a subscriber is created in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/webhook-event/operation/subscriber.created)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "subscriber.created", + ]; + }, + generateMeta(event) { + return { + id: event.subscriber.id, + summary: `New Subscriber ${event.subscriber.id}`, + ts: Date.parse(event.event_time), + }; + }, + }, + sampleEmit, +}; diff --git a/components/flodesk/sources/subscriber-created/test-event.mjs b/components/flodesk/sources/subscriber-created/test-event.mjs new file mode 100644 index 0000000000000..449737c259712 --- /dev/null +++ b/components/flodesk/sources/subscriber-created/test-event.mjs @@ -0,0 +1,26 @@ +export default { + "event_name": "string", + "event_time": "2019-08-24T14:15:22Z", + "subscriber": { + "id": "string", + "status": "active", + "email": "string", + "source": "manual", + "first_name": "string", + "last_name": "string", + "segments": [ + { + "id": "string", + "name": "string" + } + ], + "custom_fields": { + "property1": "string", + "property2": "string" + }, + "optin_ip": "string", + "optin_timestamp": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z" + }, + "webhook_id": "string" +} \ No newline at end of file diff --git a/components/flodesk/sources/subscriber-unsubscribed/subscriber-unsubscribed.mjs b/components/flodesk/sources/subscriber-unsubscribed/subscriber-unsubscribed.mjs new file mode 100644 index 0000000000000..0e649664e9ceb --- /dev/null +++ b/components/flodesk/sources/subscriber-unsubscribed/subscriber-unsubscribed.mjs @@ -0,0 +1,28 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "flodesk-subscriber-unsubscribed", + name: "Subscriber Unsubscribed", + description: "Emit new event when a subscriber is unsubscribed in Flodesk. [See the documentation](https://developers.flodesk.com/#tag/webhook-event/operation/subscriber.unsubscribed)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "subscriber.unsubscribed", + ]; + }, + generateMeta(event) { + return { + id: event.subscriber.id, + summary: `Subscriber Unsubscribed ${event.subscriber.id}`, + ts: Date.parse(event.event_time), + }; + }, + }, + sampleEmit, +}; diff --git a/components/flodesk/sources/subscriber-unsubscribed/test-event.mjs b/components/flodesk/sources/subscriber-unsubscribed/test-event.mjs new file mode 100644 index 0000000000000..449737c259712 --- /dev/null +++ b/components/flodesk/sources/subscriber-unsubscribed/test-event.mjs @@ -0,0 +1,26 @@ +export default { + "event_name": "string", + "event_time": "2019-08-24T14:15:22Z", + "subscriber": { + "id": "string", + "status": "active", + "email": "string", + "source": "manual", + "first_name": "string", + "last_name": "string", + "segments": [ + { + "id": "string", + "name": "string" + } + ], + "custom_fields": { + "property1": "string", + "property2": "string" + }, + "optin_ip": "string", + "optin_timestamp": "2019-08-24T14:15:22Z", + "created_at": "2019-08-24T14:15:22Z" + }, + "webhook_id": "string" +} \ No newline at end of file diff --git a/components/flutterwave/flutterwave.app.mjs b/components/flutterwave/flutterwave.app.mjs new file mode 100644 index 0000000000000..243a1ffc305ae --- /dev/null +++ b/components/flutterwave/flutterwave.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "flutterwave", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/flutterwave/package.json b/components/flutterwave/package.json new file mode 100644 index 0000000000000..febc804e7dca7 --- /dev/null +++ b/components/flutterwave/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/flutterwave", + "version": "0.0.1", + "description": "Pipedream Flutterwave Components", + "main": "flutterwave.app.mjs", + "keywords": [ + "pipedream", + "flutterwave" + ], + "homepage": "https://pipedream.com/apps/flutterwave", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/formbricks/formbricks.app.mjs b/components/formbricks/formbricks.app.mjs index 6cbc6e4c683c1..e82889e624277 100644 --- a/components/formbricks/formbricks.app.mjs +++ b/components/formbricks/formbricks.app.mjs @@ -1,11 +1,61 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "formbricks", - propDefinitions: {}, + propDefinitions: { + surveyIds: { + type: "string[]", + label: "Survey IDs", + description: "The survey(s) to watch for new responses. If not provided, events will be triggered for all surveys.", + async options() { + const surveys = await this.listSurveys(); + return surveys.map(({ + id, name, + }) => ({ + label: name, + value: id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + async _makeRequest({ + $ = this, + headers, + ...otherOpts + }) { + return axios($, { + ...otherOpts, + baseURL: `https://${this.$auth.hostname}/api/v1`, + headers: { + ...headers, + "x-api-key": this.$auth.api_key, + }, + }); + }, + async listSurveys() { + const { data } = await this._makeRequest({ + url: "/management/surveys", + }); + return data; + }, + async createWebhook(args) { + const { data } = await this._makeRequest({ + method: "POST", + url: "/webhooks", + ...args, + }); + return data; + }, + async deleteWebhook({ + id, ...args + }) { + return this._makeRequest({ + method: "DELETE", + url: `/webhooks/${id}`, + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/formbricks/package.json b/components/formbricks/package.json index d328ee16a8656..9207d4e6a4c3c 100644 --- a/components/formbricks/package.json +++ b/components/formbricks/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/formbricks", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Formbricks Components", "main": "formbricks.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/formbricks/sources/response-created/response-created.mjs b/components/formbricks/sources/response-created/response-created.mjs new file mode 100644 index 0000000000000..2426ea1d2b534 --- /dev/null +++ b/components/formbricks/sources/response-created/response-created.mjs @@ -0,0 +1,71 @@ +import formbricks from "../../formbricks.app.mjs"; + +export default { + key: "formbricks-response-created", + name: "Response Created", + description: + "Emit new event when a response is created for a survey. [See the documentation](https://formbricks.com/docs/api/management/webhooks)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + formbricks, + http: "$.interface.http", + db: "$.service.db", + surveyIds: { + propDefinition: [ + formbricks, + "surveyIds", + ], + }, + }, + methods: { + getTriggers() { + return [ + "responseCreated", + ]; + }, + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(value) { + this.db.set("webhookId", value); + }, + }, + hooks: { + async activate() { + const data = { + surveyIds: this.surveyIds, + triggers: this.getTriggers(), + url: this.http.endpoint, + }; + + const { id } = await this.formbricks.createWebhook({ + data, + }); + this._setWebhookId(id); + }, + async deactivate() { + const id = this._getWebhookId(); + if (id) { + await this.formbricks.deleteWebhook({ + id, + }); + } + }, + }, + async run({ body }) { + const { data } = body; + if (data) { + this.$emit(body, { + id: data.id, + summary: `New response by ${ + data.personAttributes?.email ?? + data.person?.attributes?.email ?? + "(unknown user)" + }`, + ts: Date.parse(data.createdAt), + }); + } + }, +}; diff --git a/components/function/function.app.mjs b/components/function/function.app.mjs new file mode 100644 index 0000000000000..49097e51c1c48 --- /dev/null +++ b/components/function/function.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "function", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/function/package.json b/components/function/package.json new file mode 100644 index 0000000000000..a3121d2734509 --- /dev/null +++ b/components/function/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/function", + "version": "0.0.1", + "description": "Pipedream Function Components", + "main": "function.app.mjs", + "keywords": [ + "pipedream", + "function" + ], + "homepage": "https://pipedream.com/apps/function", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/gist/.gitignore b/components/gist/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/gist/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/gist/actions/add-tag-to-contacts/add-tag-to-contacts.mjs b/components/gist/actions/add-tag-to-contacts/add-tag-to-contacts.mjs new file mode 100644 index 0000000000000..565c655cc7761 --- /dev/null +++ b/components/gist/actions/add-tag-to-contacts/add-tag-to-contacts.mjs @@ -0,0 +1,46 @@ +import gist from "../../gist.app.mjs"; + +export default { + key: "gist-add-tag-to-contacts", + name: "Add Tag To Existing Contacts", + description: "This Action lets you assign a tag to multiple contacts at once. If the tag does not already exist it will be created for you. [See docs](https://developers.getgist.com/api/#add-a-tag-to-contacts)", + type: "action", + version: "0.0.1", + props: { + gist, + contactId: { + propDefinition: [ + gist, + "contactId", + ], + type: "integer[]", + }, + tagId: { + propDefinition: [ + gist, + "tagId", + ], + withLabel: true, + }, + }, + async run({ $ }) { + const data = { + id: this.tagId.value, + name: this.tagId.label || this.tagId, + contacts: this.contactId.map((contactId) => { + return { + id: `${contactId}`, + }; + }), + }; + + const response = await this.gist.updateTagToContact({ + $, + data, + }); + + $.export("$summary", `Successfully added tag ${this.tagId.label || this.tagId} to contact`); + + return response; + }, +}; diff --git a/components/gist/actions/create-or-update-contact/create-or-update-contact.mjs b/components/gist/actions/create-or-update-contact/create-or-update-contact.mjs new file mode 100644 index 0000000000000..149202a570ec2 --- /dev/null +++ b/components/gist/actions/create-or-update-contact/create-or-update-contact.mjs @@ -0,0 +1,115 @@ +import { ConfigurationError } from "@pipedream/platform"; +import gist from "../../gist.app.mjs"; + +export default { + key: "gist-create-or-update-contact", + name: "Create Or Update Contact", + description: "Create or update a contact in Gist [See docs](https://developers.getgist.com/api/#create-or-update-a-contact)", + type: "action", + version: "0.0.1", + props: { + gist, + email: { + propDefinition: [ + gist, + "email", + ], + optional: true, + }, + userId: { + propDefinition: [ + gist, + "userId", + ], + optional: true, + }, + name: { + label: "Name", + description: "The full name of the contact", + type: "string", + optional: true, + }, + phone: { + label: "Phone", + description: "The contact number of the contact", + type: "string", + optional: true, + }, + signedUpAt: { + label: "Signed Up At", + description: "The time the contact signed up", + type: "string", + optional: true, + }, + lastSeenIp: { + label: "Last Seen Ip", + description: "The last IP address the contact visited your website from", + type: "string", + optional: true, + }, + lastSeenUserAgent: { + label: "Last Seen User Agent", + description: "The last contact agent the contact was seen using", + type: "string", + optional: true, + }, + customProperties: { + label: "Custom Properties", + description: "A JSON object containing the contact's custom properties. E.g. { \"name\": \"John Doe\" }", + type: "object", + optional: true, + }, + unsubscribedFromEmails: { + label: "Unsubscribed From Emails", + description: "If the contact has unsubscribed from emails or not", + type: "boolean", + optional: true, + }, + tagId: { + propDefinition: [ + gist, + "tagId", + ], + type: "string[]", + optional: true, + }, + }, + async run({ $ }) { + const { + userId, + signedUpAt, + lastSeenIp, + lastSeenUserAgent, + customProperties, + unsubscribedFromEmails, + tagId, + ...data + } = this; + + if (!userId && !this.email) { + throw new ConfigurationError("You must fill in at least **User Id** or **Email**"); + } + + const response = await this.gist.createOrUpdateContact({ + $, + data: { + id: userId, + signed_up_at: signedUpAt, + last_seen_ip: lastSeenIp, + last_seen_user_agent: lastSeenUserAgent, + custom_properties: customProperties, + unsubscribed_from_emails: unsubscribedFromEmails, + tags: tagId && tagId.map((tag) => tag.label), + ...data, + }, + }); + + const action = (response.contact?.created_at != response.contact?.updated_at) + ? "updated" + : "created"; + + $.export("$summary", `Successfully ${action} ${this.name || this.email} contact`); + + return response; + }, +}; diff --git a/components/gist/actions/get-contact/get-contact.mjs b/components/gist/actions/get-contact/get-contact.mjs new file mode 100644 index 0000000000000..0a8249e454b76 --- /dev/null +++ b/components/gist/actions/get-contact/get-contact.mjs @@ -0,0 +1,28 @@ +import gist from "../../gist.app.mjs"; + +export default { + key: "gist-get-contact", + name: "Get Contact", + description: "Find a Contact [See docs](https://developers.getgist.com/api/#add-a-tag-to-contacts)", + type: "action", + version: "0.0.1", + props: { + gist, + contactId: { + propDefinition: [ + gist, + "contactId", + ], + }, + }, + async run({ $ }) { + const response = await this.gist.getContact({ + $, + contactId: this.contactId, + }); + + $.export("$summary", "Successfully retrieved contact"); + + return response; + }, +}; diff --git a/components/gist/actions/remove-tag-from-contact/remove-tag-from-contact.mjs b/components/gist/actions/remove-tag-from-contact/remove-tag-from-contact.mjs new file mode 100644 index 0000000000000..bd89a7b257bba --- /dev/null +++ b/components/gist/actions/remove-tag-from-contact/remove-tag-from-contact.mjs @@ -0,0 +1,52 @@ +import gist from "../../gist.app.mjs"; + +export default { + key: "gist-remove-tag-from-contact", + name: "Remove Tag From Existing Contact", + description: "Remove a tag from an existing contact or contacts. [See docs](https://developers.getgist.com/api/#remove-a-tag-from-contacts)", + type: "action", + version: "0.0.1", + props: { + gist, + tagId: { + propDefinition: [ + gist, + "tagId", + ], + withLabel: true, + }, + contactId: { + propDefinition: [ + gist, + "contactId", + (c) => ({ + tagId: c.tagId.value, + }), + ], + type: "integer[]", + }, + }, + async run({ $ }) { + const data = { + id: this.tagId.value, + name: this.tagId.label, + contacts: this.contactId.map((contactId) => ({ + id: `${contactId}`, + untag: true, + })), + }; + + const response = await this.gist.updateTagToContact({ + $, + data, + }); + + $.export("$summary", `Successfully removed tag ${this.tagId.label} from contact${ + this.contactId.length > 1 + ? "s" + : "" + }`); + + return response; + }, +}; diff --git a/components/gist/gist.app.mjs b/components/gist/gist.app.mjs new file mode 100644 index 0000000000000..019f849ef2685 --- /dev/null +++ b/components/gist/gist.app.mjs @@ -0,0 +1,196 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "gist", + propDefinitions: { + contactId: { + label: "Contact Id", + description: "The Id of the contact that will be retrieved", + type: "integer", + async options({ + page, tagId, + }) { + const { contacts } = await this.listContacts({ + params: { + page: page + 1, + tag_id: tagId, + }, + }); + + return contacts.map(({ + name, email, id, + }) => ({ + label: `${email} ${name || ""}`, + value: id, + })); + }, + }, + tagId: { + label: "Tag", + description: "The tag that will be added", + type: "string", + withLabel: true, + async options({ page }) { + const { tags } = await this.listTags({ + params: { + page: page + 1, + }, + }); + + return tags.map(({ + name, id, + }) => ({ + label: name, + value: id, + })); + }, + }, + campaignId: { + label: "Campaign Id", + description: "The id of the campaign", + type: "string", + withLabel: true, + async options({ page }) { + const { campaigns } = await this.listCampaigns({ + params: { + page: page + 1, + }, + }); + + return campaigns.map(({ + name, id, + }) => ({ + label: name, + value: id, + })); + }, + }, + email: { + label: "Email", + description: "The contact's email address. Required on create. Required if no user_id is supplied on update.", + type: "string", + }, + userId: { + label: "User Id", + description: "A unique string identifier for the user. It is required to create Contact of type User. Required on create. Required if no email is supplied.", + type: "string", + async options({ page }) { + const { contacts } = await this.listUsers({ + params: { + page: page + 1, + }, + }); + + return contacts.map(({ + name, email, id: value, + }) => ({ + label: `${email} ${name || ""}`, + value, + })); + }, + }, + }, + methods: { + _getBaseUrl() { + return "https://api.getgist.com"; + }, + _getHeaders() { + return { + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...otherConfig + }) { + const config = { + url: `${this._getBaseUrl()}/${path}`, + headers: this._getHeaders(), + ...otherConfig, + }; + + return axios($, config); + }, + listContacts(args = {}) { + return this._makeRequest({ + path: "contacts", + ...args, + }); + }, + getContact({ + contactId, ...args + }) { + return this._makeRequest({ + path: `contacts/${contactId}`, + ...args, + }); + }, + listTags(args = {}) { + return this._makeRequest({ + path: "tags", + ...args, + }); + }, + listCampaigns(args = {}) { + return this._makeRequest({ + path: "campaigns", + ...args, + }); + }, + listUsers(args = {}) { + return this._makeRequest({ + path: "contacts", + ...args, + }); + }, + createOrUpdateContact(args = {}) { + return this._makeRequest({ + method: "POST", + path: "contacts", + ...args, + }); + }, + addOrRemoveContactInCampaign(args = {}) { + return this._makeRequest({ + method: "POST", + path: "campaigns", + ...args, + }); + }, + updateTagToContact(args = {}) { + return this._makeRequest({ + method: "POST", + path: "tags", + ...args, + }); + }, + async *paginate({ + fn, params = {}, maxResults = null, ...args + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.page = ++page; + params.per_page = 60; + + const { contacts } = await fn({ + params, + ...args, + }); + + for (const d of contacts) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = contacts.length; + + } while (hasMore); + }, + }, +}; diff --git a/components/gist/package.json b/components/gist/package.json index bcddb10cf6db1..4524d40bd9b35 100644 --- a/components/gist/package.json +++ b/components/gist/package.json @@ -1,18 +1,19 @@ { "name": "@pipedream/gist", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream Gist Components", - "main": "dist/app/gist.app.mjs", + "main": "gist.app.mjs", "keywords": [ "pipedream", "gist" ], - "files": [ - "dist" - ], "homepage": "https://pipedream.com/apps/gist", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } } + diff --git a/components/gist/sources/new-contact/new-contact.mjs b/components/gist/sources/new-contact/new-contact.mjs new file mode 100644 index 0000000000000..2f3331c360c30 --- /dev/null +++ b/components/gist/sources/new-contact/new-contact.mjs @@ -0,0 +1,73 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import gist from "../../gist.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "gist-new-contact", + name: "New Contact", + description: "Emit new event when a new contact is created", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + gist, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Gist API on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async startEvent(maxResults = 0) { + const lastId = this._getLastId(); + const items = this.gist.paginate({ + fn: this.gist.listContacts, + params: { + order_by: "id", + order: "desc", + }, + maxResults, + }); + + let responseArray = []; + + for await (const item of items) { + if (item.id <= lastId) break; + responseArray.push(item); + } + if (responseArray.length) { + this._setLastId(responseArray[0].id); + } + + for (const item of responseArray.reverse()) { + this.$emit( + item, + { + id: item.id, + summary: `New contact created: ${item.full_name || item.email} (${item.id})`, + ts: item.created_at, + }, + ); + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/gist/sources/new-contact/test-event.mjs b/components/gist/sources/new-contact/test-event.mjs new file mode 100644 index 0000000000000..4ed140cde6331 --- /dev/null +++ b/components/gist/sources/new-contact/test-event.mjs @@ -0,0 +1,54 @@ +export default { + "type": "user", + "id": 179351, + "name": "Rubamaga", + "email": "rubamaga@example.com", + "user_id": "100488", + "phone": null, + "created_at": 1502979465, + "signed_up_at": 0, + "last_seen_at": 1502979465, + "last_contacted_at": null, + "updated_at": 1540478268, + "session_count": null, + "avatar": "https://avatar.tobi.sh/rubamaga@example.com?size=120&type=svg&text=BR", + "landing_url": null, + "original_referrer": null, + "last_seen_ip": "", + "last_seen_user_agent": "", + "location_data": { + "city_name": "Paris", + "region_name": null, + "country_name": "France", + "country_code": "FR", + "continent_name": null, + "continent_code": null, + "latitude": 48.8712, + "longitude": 2.2710, + "postal_code": null, + "time_zone": null, + "utc_offset": null + }, + "segments": [ + { + "id": 578, + "name": "Test validation", + "created_at": 1512633515, + "updated_at": 1512633567 + } + ], + "tags": [ + { + "id": 79, + "name": "Completed" + } + ], + "social_profiles": [], + "custom_properties": { + "form_user": null, + "user_types": null, + "age": 23, + "first_name": null + }, + "unsubscribed_from_emails": "false" +} \ No newline at end of file diff --git a/components/github/actions/common/utils.mjs b/components/github/actions/common/utils.mjs index 01b24b87c86ae..e40078554e8e5 100644 --- a/components/github/actions/common/utils.mjs +++ b/components/github/actions/common/utils.mjs @@ -7,4 +7,14 @@ export default { repo: splited[0], }; }, + convertFiles(files) { + return Object.keys(files).reduce((acc, key) => { + acc[key] = files[key] + ? { + content: files[key], + } + : null; + return acc; + }, {}); + }, }; diff --git a/components/github/actions/create-branch/create-branch.mjs b/components/github/actions/create-branch/create-branch.mjs index 9b7d123453493..e6bd4cf7a54c7 100644 --- a/components/github/actions/create-branch/create-branch.mjs +++ b/components/github/actions/create-branch/create-branch.mjs @@ -5,7 +5,7 @@ export default { key: "github-create-branch", name: "Create Branch", description: "Create a new branch in a Github repo. [See docs here](https://docs.github.com/en/rest/git/refs?apiVersion=2022-11-28#create-a-reference)", - version: "0.0.4", + version: "0.0.6", type: "action", props: { github, diff --git a/components/github/actions/create-gist/create-gist.mjs b/components/github/actions/create-gist/create-gist.mjs new file mode 100644 index 0000000000000..7a667a3bf385b --- /dev/null +++ b/components/github/actions/create-gist/create-gist.mjs @@ -0,0 +1,38 @@ +import github from "../../github.app.mjs"; +import utils from "../../actions/common/utils.mjs"; + +export default { + key: "github-create-gist", + name: "Create Gist", + description: "Allows you to add a new gist with one or more files. [See docs here](https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#create-a-gist)", + version: "0.0.1", + type: "action", + props: { + github, + description: { + label: "Description", + description: "The description of the gist", + type: "string", + }, + files: { + label: "Files", + description: "The files that will be added to the gist. The key is the file name and the value is the content of the file. Ex: `{\"file1.txt\": \"content of file 1\", \"file2.txt\": \"content of file 2\"}`", + type: "object", + }, + public: { + label: "Public", + description: "Indicates whether the gist is public or not", + type: "boolean", + }, + }, + async run({ $ }) { + const res = await this.github.createGist({ + description: this.description, + files: utils.convertFiles(this.files), + public: this.public, + }); + + $.export("$summary", `Successfully created gist with ID "${res.id}".`); + return res; + }, +}; diff --git a/components/github/actions/create-issue-comment/create-issue-comment.mjs b/components/github/actions/create-issue-comment/create-issue-comment.mjs index b4c70aac04e79..c162251f6f090 100644 --- a/components/github/actions/create-issue-comment/create-issue-comment.mjs +++ b/components/github/actions/create-issue-comment/create-issue-comment.mjs @@ -4,7 +4,7 @@ export default { key: "github-create-issue-comment", name: "Create Issue Comment", description: "Create a new comment in a issue. [See docs here](https://docs.github.com/en/rest/issues/comments#create-an-issue-comment)", - version: "0.0.10", + version: "0.0.12", type: "action", props: { github, diff --git a/components/github/actions/create-issue/create-issue.mjs b/components/github/actions/create-issue/create-issue.mjs index d8d9f607d0be7..6a29241747c83 100644 --- a/components/github/actions/create-issue/create-issue.mjs +++ b/components/github/actions/create-issue/create-issue.mjs @@ -4,7 +4,7 @@ export default { key: "github-create-issue", name: "Create Issue", description: "Create a new issue in a Gihub repo. [See docs here](https://docs.github.com/en/rest/issues/issues#create-an-issue)", - version: "0.2.9", + version: "0.2.11", type: "action", props: { github, diff --git a/components/github/actions/create-or-update-file-contents/create-or-update-file-contents.mjs b/components/github/actions/create-or-update-file-contents/create-or-update-file-contents.mjs index e72aed55ce89f..b48aa042b2ac6 100644 --- a/components/github/actions/create-or-update-file-contents/create-or-update-file-contents.mjs +++ b/components/github/actions/create-or-update-file-contents/create-or-update-file-contents.mjs @@ -4,7 +4,7 @@ export default { key: "github-create-or-update-file-contents", name: "Create or update file contents", description: "Create or update a file in a repository. This will replace an existing file. [See docs here](https://docs.github.com/en/rest/repos/contents#create-or-update-file-contents)", - version: "0.0.6", + version: "0.0.8", type: "action", props: { github, diff --git a/components/github/actions/create-pull-request/create-pull-request.mjs b/components/github/actions/create-pull-request/create-pull-request.mjs new file mode 100644 index 0000000000000..bab1478eefeea --- /dev/null +++ b/components/github/actions/create-pull-request/create-pull-request.mjs @@ -0,0 +1,151 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { toSingleLineString } from "../../common/utils.mjs"; +import github from "../../github.app.mjs"; + +export default { + key: "github-create-pull-request", + name: "Create Pull Request", + description: toSingleLineString(` + Creates a new pull request for a specified repository. + [See docs here](https://docs.github.com/en/rest/pulls/pulls#create-a-pull-request) + `), + version: "0.0.3", + type: "action", + props: { + github, + repoFullname: { + propDefinition: [ + github, + "repoFullname", + ], + }, + head: { + propDefinition: [ + github, + "branch", + (c) => ({ + repoFullname: c.repoFullname, + }), + ], + label: "Head Branch", + description: toSingleLineString(` + The name of the branch where your changes are implemented. + For cross-repository pull requests in the same network, \`namespace\` head with a user like this: \`username:branch\`. + `), + }, + base: { + propDefinition: [ + github, + "branch", + (c) => ({ + repoFullname: c.repoFullname, + }), + ], + label: "Base Branch", + description: toSingleLineString(` + The name of the branch you want the changes pulled into. + This should be an existing branch on the current repository. + You cannot submit a pull request to one repository that requests a merge to a base of another repository. + `), + }, + org: { + propDefinition: [ + github, + "orgName", + ], + optional: true, + }, + headRepo: { + propDefinition: [ + github, + "repoOrg", + (c) => ({ + org: c.org, + }), + ], + label: "Head Repository's Name", + description: toSingleLineString(` + The name of the repository where the changes in the pull request were made. + This field is required for cross-repository pull requests if both repositories are owned by the same organization. + `), + optional: true, + }, + body: { + label: "Body", + description: "The contents of the pull request.", + type: "string", + optional: true, + }, + maintainerCanModify: { + label: "Maintainer Can Modify", + description: "Indicates whether [maintainers can modify](https://docs.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork/) the pull request.", + type: "boolean", + optional: true, + }, + draft: { + label: "Is Draft", + description: toSingleLineString(` + Indicates whether the pull request is a draft. + See "[Draft Pull Requests](https://docs.github.com/articles/about-pull-requests#draft-pull-requests)" in the GitHub Help documentation to learn more. + `), + type: "boolean", + optional: true, + }, + title: { + label: "Title", + description: "The title of the new pull request.", + type: "string", + optional: true, + }, + issue: { + propDefinition: [ + github, + "issueNumber", + (c) => ({ + repoFullname: c.repoFullname, + }), + ], + label: "Issue", + description: toSingleLineString(` + An issue in the repository to convert to a pull request. + The issue title, body, and comments will become the title, body, and comments on the new pull request. + `), + min: 1, + optional: true, + }, + }, + async run({ $ }) { + + if (!this.issue && !this.title) { + throw new ConfigurationError(toSingleLineString(` + Title is required if Issue is unspecified. + You can either specify a new pull request with Title or convert an existing issue to a pull request with Issue. + `)); + } + + if (this.issue && this.title) { + throw new ConfigurationError("You can't specify both Title and Issue at the same time."); + } + + const data = { + title: this.title, + head: this.head.split("/")[1], + base: this.base.split("/")[1], + head_repo: this.headRepo, + body: this.body, + maintainer_can_modify: this.maintainerCanModify, + draft: this.draft, + issue: this.issue, + }; + + const response = await this.github.createPullRequest({ + owner: this.owner, + repoFullname: this.repoFullname, + data: data, + }); + + $.export("$summary", `Successfully created pull request: ${response.title}.`); + + return response; + }, +}; diff --git a/components/github/actions/create-repository/create-repository.mjs b/components/github/actions/create-repository/create-repository.mjs index b19b7a78f6470..6ec6f30b77fed 100644 --- a/components/github/actions/create-repository/create-repository.mjs +++ b/components/github/actions/create-repository/create-repository.mjs @@ -4,7 +4,7 @@ export default { key: "github-create-repository", name: "Create Repository", description: "Creates a new repository for the authenticated user. [See docs here](https://docs.github.com/en/rest/repos/repos#create-a-repository-for-the-authenticated-user)", - version: "0.0.5", + version: "0.0.7", type: "action", props: { github, diff --git a/components/github/actions/get-issue-assignees/get-issue-assignees.mjs b/components/github/actions/get-issue-assignees/get-issue-assignees.mjs index 3f130701deb53..8b1da50754b51 100644 --- a/components/github/actions/get-issue-assignees/get-issue-assignees.mjs +++ b/components/github/actions/get-issue-assignees/get-issue-assignees.mjs @@ -4,7 +4,7 @@ export default { key: "github-get-issue-assignees", name: "Get Issue Assignees", description: "Get assignees for an issue in a Gihub repo. [See docs here](https://docs.github.com/en/rest/issues/issues#get-an-issue)", - version: "0.0.10", + version: "0.0.12", type: "action", props: { github, diff --git a/components/github/actions/get-repository-content/get-repository-content.mjs b/components/github/actions/get-repository-content/get-repository-content.mjs index 9d814dac1f978..ff20390519a38 100644 --- a/components/github/actions/get-repository-content/get-repository-content.mjs +++ b/components/github/actions/get-repository-content/get-repository-content.mjs @@ -8,7 +8,7 @@ export default { Get the content of a file or directory in a specific repository. [See docs here](https://docs.github.com/en/rest/repos/contents#get-repository-content) `), - version: "0.0.9", + version: "0.0.11", type: "action", props: { github, diff --git a/components/github/actions/get-repository/get-repository.mjs b/components/github/actions/get-repository/get-repository.mjs index da245abd40137..acb7c8af71d00 100644 --- a/components/github/actions/get-repository/get-repository.mjs +++ b/components/github/actions/get-repository/get-repository.mjs @@ -4,7 +4,7 @@ export default { key: "github-get-repository", name: "Get Repository", description: "Get specific repository. [See docs here](https://docs.github.com/en/rest/repos/repos#get-a-repository)", - version: "0.0.10", + version: "0.0.12", type: "action", props: { github, diff --git a/components/github/actions/get-reviewers/get-reviewers.mjs b/components/github/actions/get-reviewers/get-reviewers.mjs index e0be76300c8ea..447df921ec53e 100644 --- a/components/github/actions/get-reviewers/get-reviewers.mjs +++ b/components/github/actions/get-reviewers/get-reviewers.mjs @@ -6,7 +6,7 @@ export default { key: "github-get-reviewers", name: "Get Reviewers", description: "Get reviewers for a PR ([see docs](https://docs.github.com/en/rest/pulls/reviews#list-reviews-for-a-pull-request)) or Commit SHA ([see docs](https://docs.github.com/en/rest/commits/commits#list-pull-requests-associated-with-a-commit)).", - version: "0.0.10", + version: "0.0.12", type: "action", props: { github, diff --git a/components/github/actions/list-gists-for-a-user/list-gists-for-a-user.mjs b/components/github/actions/list-gists-for-a-user/list-gists-for-a-user.mjs new file mode 100644 index 0000000000000..12e2257f90ec8 --- /dev/null +++ b/components/github/actions/list-gists-for-a-user/list-gists-for-a-user.mjs @@ -0,0 +1,55 @@ +import github from "../../github.app.mjs"; + +export default { + key: "github-list-gists-for-a-user", + name: "List Gists for a User", + description: "Lists public gists for the specified user. [See docs here](https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-a-user)", + version: "0.0.1", + type: "action", + props: { + github, + username: { + label: "Username", + description: "The username of the user whose gists you want to list", + type: "string", + }, + since: { + label: "Since", + description: "Only show notifications updated after the given time. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.", + type: "string", + optional: true, + }, + }, + async run({ $ }) { + const PER_PAGE = 100; + const MAX_PAGES = 50; + let page = 1; + const data = []; + + while (true) { + const res = await this.github.listGistsFromUser(this.username, { + since: this.since, + per_page: PER_PAGE, + page, + }); + + if (!res || res.length === 0) { + break; + } + data.push(...res); + + page++; + if (page > MAX_PAGES) { + break; + } + } + + if (data.length === 0) { + $.export("$summary", `No gists found for user "${this.username}".`); + return; + } + + $.export("$summary", `Successfully fetched ${data.length} gist(s).`); + return data; + }, +}; diff --git a/components/github/actions/search-issues-and-pull-requests/search-issues-and-pull-requests.mjs b/components/github/actions/search-issues-and-pull-requests/search-issues-and-pull-requests.mjs index dc0502634ea6b..a973de7926279 100644 --- a/components/github/actions/search-issues-and-pull-requests/search-issues-and-pull-requests.mjs +++ b/components/github/actions/search-issues-and-pull-requests/search-issues-and-pull-requests.mjs @@ -4,7 +4,7 @@ export default { key: "github-search-issues-and-pull-requests", name: "Search Issues and Pull Requests", description: "Find issues and pull requests by state and keyword. [See docs here](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests)", - version: "0.1.9", + version: "0.1.11", type: "action", props: { github, diff --git a/components/github/actions/update-gist/update-gist.mjs b/components/github/actions/update-gist/update-gist.mjs new file mode 100644 index 0000000000000..904bdcb844407 --- /dev/null +++ b/components/github/actions/update-gist/update-gist.mjs @@ -0,0 +1,45 @@ +import github from "../../github.app.mjs"; +import utils from "../../actions/common/utils.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "github-update-gist", + name: "Update Gist", + description: "Allows you to update a gist's description and to update, delete, or rename gist files. Files from the previous version of the gist that aren't explicitly changed during an edit are unchanged. At least one of description or files is required. [See docs here](https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#update-a-gist)", + version: "0.0.1", + type: "action", + props: { + github, + gistId: { + propDefinition: [ + github, + "gistId", + ], + }, + description: { + label: "Description", + description: "The description of the gist", + type: "string", + optional: true, + }, + files: { + label: "Files", + description: "The gist files to be updated, renamed, or deleted. Each key must match the current filename (including extension) of the targeted gist file. For example: `hello.py`. To delete a file, set the whole file to null. For example: `hello.py : null`", + type: "object", + optional: true, + }, + }, + async run({ $ }) { + if (!this.description && !this.files) { + throw new ConfigurationError("At least one of description or files is required."); + } + + const res = await this.github.updateGist(this.gistId, { + description: this.description, + files: utils.convertFiles(this.files), + }); + + $.export("$summary", `Successfully updated gist with ID "${res.id}".`); + return res; + }, +}; diff --git a/components/github/actions/update-issue/update-issue.mjs b/components/github/actions/update-issue/update-issue.mjs index 5e1794311cf17..1610a69ecadbc 100644 --- a/components/github/actions/update-issue/update-issue.mjs +++ b/components/github/actions/update-issue/update-issue.mjs @@ -4,7 +4,7 @@ export default { key: "github-update-issue", name: "Update Issue", description: "Update a new issue in a Gihub repo. [See docs here](https://docs.github.com/en/rest/issues/issues#update-an-issue)", - version: "0.1.9", + version: "0.1.11", type: "action", props: { github, diff --git a/components/github/github.app.mjs b/components/github/github.app.mjs index feabb1424759c..66fbc05f1708b 100644 --- a/components/github/github.app.mjs +++ b/components/github/github.app.mjs @@ -1,529 +1,576 @@ -import { Octokit } from "@octokit/core"; -import { paginateRest } from "@octokit/plugin-paginate-rest"; -import queries from "./common/queries.mjs"; -import { axios } from "@pipedream/platform"; -import { ConfigurationError } from "@pipedream/platform"; - -const CustomOctokit = Octokit.plugin(paginateRest); - -export default { - type: "app", - app: "github", - propDefinitions: { - orgName: { - label: "Organization", - description: "The name of the Github organization. The name is not case sensitive.", - type: "string", - async options() { - const organizations = await this.getOrganizations(); - - return organizations.map((organization) => organization.login); - }, - }, - repoFullname: { - label: "Repository", - description: "The name of the repository. The name is not case sensitive", - type: "string", - async options({ org }) { - const repositories = await this.getRepos({ - org, - }); - - return repositories.map((repository) => repository.full_name); - }, - }, - repoOrg: { - label: "Organization Repository", - description: "The repository in a organization", - type: "string", - async options({ org }) { - const repositories = await this.getOrgRepos({ - org, - }); - return repositories.map((repository) => repository.full_name.split("/")[1]); - }, - }, - project: { - label: "Project", - description: "The project in a repository", - type: "integer", - async options({ repoFullname }) { - const projects = await this.getRepositoryProjects({ - repoFullname, - }); - - return projects.map((project) => ({ - label: project.name, - value: project.id, - })); - }, - }, - projectV2: { - label: "Project V2", - description: "The project (V2) in a repository", - type: "integer", - async options({ - prevContext, org, repo, - }) { - const cursor = prevContext?.cursor ?? null; - - const { - projects, nextCursor, - } = await this.getProjectsV2({ - repoOwner: org, - repoName: repo, - cursor, - }); - - if (cursor && projects.length === 0) { - return []; - } - - return { - options: projects.map((project) => ({ - label: project.title, - value: project.number, - })), - context: { - cursor: nextCursor, - }, - }; - }, - }, - status: { - label: "Item Status", - description: "The status for a project item", - type: "string", - async options({ - org, repo, project, - }) { - const { statuses } = await this.getProjectV2Statuses({ - repoOwner: org, - repoName: repo, - project, - }); - - return statuses.map((status) => ({ - label: status.name, - value: status.id, - })); - }, - }, - labels: { - label: "Labels", - description: "The labels", - type: "string[]", - async options({ repoFullname }) { - const labels = await this.getRepositoryLabels({ - repoFullname, - }); - - return labels.map((label) => label.name); - }, - }, - collaborators: { - label: "Collaborators", - description: "The collaborators", - type: "string[]", - async options({ repoFullname }) { - const collaborators = await this.getRepositoryCollaborators({ - repoFullname, - }); - - return collaborators.map((collaborator) => collaborator.login); - }, - }, - issueNumber: { - label: "Issue Number", - description: "The issue number", - type: "integer", - async options({ repoFullname }) { - const issues = await this.getRepositoryIssues({ - repoFullname, - }); - - return issues.map((issue) => ({ - label: issue.title, - value: +issue.number, - })); - }, - }, - branch: { - label: "Branch", - description: "Branch to monitor for new commits", - type: "string", - async options({ - page, repoFullname, - }) { - const branches = await this.getBranches({ - repoFullname, - params: { - page: page + 1, - }, - }); - - return branches.map((branch) => ({ - label: branch.name, - value: `${branch.commit.sha}/${branch.name}`, - })); - }, - }, - pullNumber: { - type: "integer", - label: "PR Number", - description: "A pull request number", - async options({ repoFullname }) { - const prs = await this.getRepositoryPullRequests({ - repoFullname, - }); - - return prs.map((pr) => ({ - label: pr.title, - value: +pr.number, - })); - }, - }, - column: { - label: "Column", - description: "The column in a project board", - type: "integer", - async options({ project }) { - const columns = await this.getProjectColumns({ - project, - }); - - return columns.map((column) => ({ - label: column.name, - value: column.id, - })); - }, - }, - teamId: { - label: "Team Id", - description: "The id of the team that will be granted access to this repository. This is only valid when creating a repository in an organization.", - type: "integer", - async options() { - const teams = await this.getTeams(); - return teams.map((team) => ({ - label: team.name, - value: team.id, - })); - }, - }, - }, - methods: { - _baseApiUrl() { - return "https://api.github.com"; - }, - _accessToken() { - return this.$auth.oauth_access_token; - }, - _client() { - const client = new CustomOctokit({ - auth: this._accessToken(), - }); - client.hook.error("request", this.handleRequestException); - return client; - }, - async _makeRequest({ - $ = this, - path, - headers = {}, - ...args - } = {}) { - return axios($, { - url: `${this._baseApiUrl()}${path}`, - headers: { - Authorization: `Bearer ${this._accessToken()}`, - Accept: "application/vnd.github+json", - ...headers, - }, - ...args, - }); - }, - async graphql(query, opts = {}) { - return this._client().graphql(query, opts); - }, - handleRequestException(exception) { - console.error(exception); - const status = exception?.status; - if (status && (status === 404 || status === 403)) { - throw new ConfigurationError(`The request failed with status "${status}". It is likely that your token doesn't have sufficient permissions to execute that request. [see mor information here](https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api?apiVersion=2022-11-28#about-authentication).`); - } - throw exception; - }, - async createWebhook({ - repoFullname, data, - }) { - const response = await this._client().request(`POST /repos/${repoFullname}/hooks`, data); - - return response.data; - }, - async createOrgWebhook({ - org, data, - }) { - const response = await this._client().request(`POST /orgs/${org}/hooks`, data); - - return response.data; - }, - async removeWebhook({ - repoFullname, webhookId, - }) { - return this._client().request(`DELETE /repos/${repoFullname}/hooks/${webhookId}`, {}); - }, - async removeOrgWebhook({ - org, webhookId, - }) { - return this._client().request(`DELETE /orgs/${org}/hooks/${webhookId}`, {}); - }, - async getOrganizations() { - const response = await this._client().request("GET /user/orgs", {}); - - return response.data; - }, - async getRepos() { - return this._client().paginate("GET /user/repos", {}); - }, - async getOrgRepos({ org }) { - return this._client().paginate(`GET /orgs/${org}/repos`, {}); - }, - async getRepo({ repoFullname }) { - const response = await this._client().request(`GET /repos/${repoFullname}`, {}); - - return response.data; - }, - async getRepoContent({ - repoFullname, - path, - mediaType, - }) { - return this._makeRequest({ - path: `/repos/${repoFullname}/contents/${path}`, - ...(mediaType && { - headers: { - Accept: mediaType, - }, - }), - }); - }, - async getRepositoryLabels({ repoFullname }) { - return this._client().paginate(`GET /repos/${repoFullname}/labels`, {}); - }, - async getRepositoryCollaborators({ repoFullname }) { - return this._client().paginate(`GET /repos/${repoFullname}/collaborators`, {}); - }, - async getRepositoryIssues({ repoFullname }) { - return this._client().paginate(`GET /repos/${repoFullname}/issues`, { - state: "all", - }); - }, - async getRepositoryProjects({ repoFullname }) { - return this._client().paginate(`GET /repos/${repoFullname}/projects`, {}); - }, - async getProjectsV2({ - repoOwner, repoName, cursor, - }) { - const response = await this.graphql(repoName - ? queries.projectsQuery - : queries.organizationProjectsQuery, { - repoOwner, - repoName, - cursor, - }); - return { - projects: response?.repository?.projectsV2?.nodes ?? - response?.organization?.projectsV2?.nodes, - nextCursor: response?.repository?.projectsV2?.pageInfo?.endCursor ?? - response?.organization?.projectsV2?.pageInfo?.endCursor, - }; - }, - async getProjectV2Statuses({ - repoOwner, repoName, project, - }) { - const response = await this.graphql(repoName ? - queries.statusFieldsQuery : - queries.organizationStatusFieldsQuery, { - repoOwner, - repoName, - project, - }); - - return { - statuses: response?.repository?.projectV2?.field?.options ?? - response?.organization?.projectV2?.field?.options, - }; - }, - async getProjectColumns({ project }) { - return this._client().paginate(`GET /projects/${project}/columns`, {}); - }, - async getGists() { - return this._client().paginate("GET /gists", {}); - }, - async getTeams() { - return this._client().paginate("GET /user/teams", {}); - }, - async getFilteredNotifications({ - reason, data, - }) { - const notifications = await this._client().paginate("GET /notifications", data); - - if (reason) { - return notifications.filter((notification) => notification.reason === reason); - } - - return notifications; - }, - async getAuthenticatedUser() { - const response = await this._client().request("GET /user", {}); - - return response.data; - }, - async getFromUrl({ url }) { - const response = await this._client().request(`GET ${url.replace(this._baseApiUrl(), "")}`, {}); - - return response.data; - }, - async createIssue({ - repoFullname, data, - }) { - const response = await this._client().request(`POST /repos/${repoFullname}/issues`, data); - - return response.data; - }, - async createBranch({ - repoFullname, data, - }) { - const response = await this._client().request(`POST /repos/${repoFullname}/git/refs`, data); - - return response.data; - }, - async createRepository({ data }) { - const response = await this._client().request("POST /user/repos", data); - - return response.data; - }, - async getIssue({ - repoFullname, issueNumber, - }) { - const response = await this._client().request(`GET /repos/${repoFullname}/issues/${issueNumber}`, {}); - - return response.data; - }, - async updateIssue({ - repoFullname, issueNumber, data, - }) { - const response = await this._client().request(`PATCH /repos/${repoFullname}/issues/${issueNumber}`, data); - - return response.data; - }, - async createIssueComment({ - repoFullname, issueNumber, data, - }) { - const response = await this._client().request(`POST /repos/${repoFullname}/issues/${issueNumber}/comments`, data); - - return response.data; - }, - async getIssueFromProjectCard({ - repoFullname, cardId, - }) { - const { data: card } = await this._client().request(`GET /projects/columns/cards/${cardId}`, {}); - if (!card?.content_url) { - console.log("No issue associated with this card"); - return; - } - const issueId = card.content_url.split("/").pop(); - const { data: issue } = await this._client().request(`GET /repos/${repoFullname}/issues/${issueId}`, {}); - return issue; - }, - async searchIssueAndPullRequests({ - query, maxResults, - }) { - let issues = []; - - for await (const response of this._client().paginate.iterator( - "GET /search/issues", - { - q: query, - per_page: 100, - }, - )) { - issues = issues.concat(response.data); - - if (issues.length >= maxResults) { - break; - } - } - - return issues; - }, - async getRepositoryPullRequests({ repoFullname }) { - return this._client().paginate(`GET /repos/${repoFullname}/pulls`, {}); - }, - async getPullRequestForCommit({ - repoFullname, sha, - }) { - const response = await this._client().request(`GET /repos/${repoFullname}/commits/${sha}/pulls`, {}); - - return response.data[0]; - }, - async getReviewsForPullRequest({ - repoFullname, pullNumber, - }) { - const response = await this._client().request(`GET /repos/${repoFullname}/pulls/${pullNumber}/reviews`, {}); - - return response.data; - }, - async getCommits({ - repoFullname, ...data - }) { - const { data: commits } = await this._client().request(`GET /repos/${repoFullname}/commits`, { - ...data, - }); - return commits; - }, - async getBranches({ - repoFullname, ...data - }) { - const { data: branches } = await this._client().request(`GET /repos/${repoFullname}/branches`, { - ...data, - }); - return branches; - }, - async getProjectCards({ - columnId, ...data - }) { - const { data: cards } = await this._client().request(`GET /projects/columns/${columnId}/cards`, { - ...data, - }); - return cards; - }, - async createOrUpdateFileContent({ - repoFullname, - path, - fileContent, - commitMessage, - branch = null, - }) { - const data = { - message: commitMessage, - content: Buffer.from(fileContent).toString("base64"), - }; - const fileExists = await this._makeRequest({ - path: `/repos/${repoFullname}/contents/${path}`, - validateStatus: () => true, - }); - if (fileExists.sha) { - console.log("File exists, overwriting."); - data.sha = fileExists.sha; - } - if (branch) { - data.branch = branch; - } - return this._makeRequest({ - path: `/repos/${repoFullname}/contents/${path}`, - method: "put", - data: data, - }); - }, - }, -}; +import { Octokit } from "@octokit/core"; +import { paginateRest } from "@octokit/plugin-paginate-rest"; +import queries from "./common/queries.mjs"; +import { axios } from "@pipedream/platform"; +import { ConfigurationError } from "@pipedream/platform"; + +const CustomOctokit = Octokit.plugin(paginateRest); + +export default { + type: "app", + app: "github", + propDefinitions: { + orgName: { + label: "Organization", + description: "The name of the Github organization. The name is not case sensitive.", + type: "string", + async options() { + const organizations = await this.getOrganizations(); + + return organizations.map((organization) => organization.login); + }, + }, + repoFullname: { + label: "Repository", + description: "The name of the repository. The name is not case sensitive", + type: "string", + async options({ org }) { + const repositories = await this.getRepos({ + org, + }); + + return repositories.map((repository) => repository.full_name); + }, + }, + repoOrg: { + label: "Organization Repository", + description: "The repository in a organization", + type: "string", + async options({ org }) { + if (!org) { + throw new ConfigurationError("Must specify `org` to display repository options."); + } + const repositories = await this.getOrgRepos({ + org, + }); + return repositories.map((repository) => repository.full_name.split("/")[1]); + }, + }, + project: { + label: "Project", + description: "The project in a repository", + type: "integer", + async options({ repoFullname }) { + const projects = await this.getRepositoryProjects({ + repoFullname, + }); + + return projects.map((project) => ({ + label: project.name, + value: project.id, + })); + }, + }, + projectV2: { + label: "Project V2", + description: "The project (V2) in a repository", + type: "integer", + async options({ + prevContext, org, repo, + }) { + const cursor = prevContext?.cursor ?? null; + + const { + projects, nextCursor, + } = await this.getProjectsV2({ + repoOwner: org, + repoName: repo, + cursor, + }); + + if (cursor && projects.length === 0) { + return []; + } + + return { + options: projects.map((project) => ({ + label: project.title, + value: project.number, + })), + context: { + cursor: nextCursor, + }, + }; + }, + }, + status: { + label: "Item Status", + description: "The status for a project item", + type: "string", + async options({ + org, repo, project, + }) { + const { statuses } = await this.getProjectV2Statuses({ + repoOwner: org, + repoName: repo, + project, + }); + + return statuses.map((status) => ({ + label: status.name, + value: status.id, + })); + }, + }, + labels: { + label: "Labels", + description: "The labels", + type: "string[]", + async options({ repoFullname }) { + const labels = await this.getRepositoryLabels({ + repoFullname, + }); + + return labels.map((label) => label.name); + }, + }, + collaborators: { + label: "Collaborators", + description: "The collaborators", + type: "string[]", + async options({ repoFullname }) { + const collaborators = await this.getRepositoryCollaborators({ + repoFullname, + }); + + return collaborators.map((collaborator) => collaborator.login); + }, + }, + issueNumber: { + label: "Issue Number", + description: "The issue number", + type: "integer", + async options({ repoFullname }) { + const issues = await this.getRepositoryIssues({ + repoFullname, + }); + + return issues.map((issue) => ({ + label: issue.title, + value: +issue.number, + })); + }, + }, + branch: { + label: "Branch", + description: "Branch to monitor for new commits", + type: "string", + async options({ + page, repoFullname, + }) { + const branches = await this.getBranches({ + repoFullname, + params: { + page: page + 1, + }, + }); + + return branches.map((branch) => ({ + label: branch.name, + value: `${branch.commit.sha}/${branch.name}`, + })); + }, + }, + pullNumber: { + type: "integer", + label: "PR Number", + description: "A pull request number", + async options({ repoFullname }) { + const prs = await this.getRepositoryPullRequests({ + repoFullname, + }); + + return prs.map((pr) => ({ + label: pr.title, + value: +pr.number, + })); + }, + }, + column: { + label: "Column", + description: "The column in a project board", + type: "integer", + async options({ project }) { + const columns = await this.getProjectColumns({ + project, + }); + + return columns.map((column) => ({ + label: column.name, + value: column.id, + })); + }, + }, + gistId: { + label: "Gist Id", + description: "The Gist Id to perform your action", + type: "string", + async options({ page }) { + const PER_PAGE = 100; + const gists = await this.getGists({ + per_page: PER_PAGE, + page: page + 1, + }); + + return gists.map((gist) => ({ + label: gist.description ?? gist.id, + value: gist.id, + })); + }, + }, + teamId: { + label: "Team Id", + description: "The id of the team that will be granted access to this repository. This is only valid when creating a repository in an organization.", + type: "integer", + async options() { + const teams = await this.getTeams(); + return teams.map((team) => ({ + label: team.name, + value: team.id, + })); + }, + }, + }, + methods: { + _baseApiUrl() { + return "https://api.github.com"; + }, + _accessToken() { + return this.$auth.oauth_access_token; + }, + _client() { + const client = new CustomOctokit({ + auth: this._accessToken(), + }); + client.hook.error("request", this.handleRequestException); + return client; + }, + async _makeRequest({ + $ = this, + path, + headers = {}, + ...args + } = {}) { + return axios($, { + url: `${this._baseApiUrl()}${path}`, + headers: { + Authorization: `Bearer ${this._accessToken()}`, + Accept: "application/vnd.github+json", + ...headers, + }, + ...args, + }); + }, + async graphql(query, opts = {}) { + return this._client().graphql(query, opts); + }, + handleRequestException(exception) { + console.error(exception); + const status = exception?.status; + if (status && (status === 404 || status === 403)) { + throw new ConfigurationError(`The request failed with status "${status}". It is likely that your token doesn't have sufficient permissions to execute that request. [see mor information here](https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api?apiVersion=2022-11-28#about-authentication).`); + } + throw exception; + }, + async createWebhook({ + repoFullname, data, + }) { + const response = await this._client().request(`POST /repos/${repoFullname}/hooks`, data); + + return response.data; + }, + async createOrgWebhook({ + org, data, + }) { + const response = await this._client().request(`POST /orgs/${org}/hooks`, data); + + return response.data; + }, + async removeWebhook({ + repoFullname, webhookId, + }) { + return this._client().request(`DELETE /repos/${repoFullname}/hooks/${webhookId}`, {}); + }, + async removeOrgWebhook({ + org, webhookId, + }) { + return this._client().request(`DELETE /orgs/${org}/hooks/${webhookId}`, {}); + }, + async getOrganizations() { + const response = await this._client().request("GET /user/orgs", {}); + + return response.data; + }, + async getRepos() { + return this._client().paginate("GET /user/repos", {}); + }, + async getOrgRepos({ org }) { + return this._client().paginate(`GET /orgs/${org}/repos`, {}); + }, + async getRepo({ repoFullname }) { + const response = await this._client().request(`GET /repos/${repoFullname}`, {}); + + return response.data; + }, + async getRepoContent({ + repoFullname, + path, + mediaType, + }) { + return this._makeRequest({ + path: `/repos/${repoFullname}/contents/${path}`, + ...(mediaType && { + headers: { + Accept: mediaType, + }, + }), + }); + }, + async getRepositoryLabels({ repoFullname }) { + return this._client().paginate(`GET /repos/${repoFullname}/labels`, {}); + }, + async getRepositoryCollaborators({ repoFullname }) { + return this._client().paginate(`GET /repos/${repoFullname}/collaborators`, {}); + }, + async getRepositoryIssues({ repoFullname }) { + return this._client().paginate(`GET /repos/${repoFullname}/issues`, { + state: "all", + }); + }, + async getRepositoryProjects({ repoFullname }) { + return this._client().paginate(`GET /repos/${repoFullname}/projects`, {}); + }, + async getProjectsV2({ + repoOwner, repoName, cursor, + }) { + const response = await this.graphql(repoName + ? queries.projectsQuery + : queries.organizationProjectsQuery, { + repoOwner, + repoName, + cursor, + }); + return { + projects: response?.repository?.projectsV2?.nodes ?? + response?.organization?.projectsV2?.nodes, + nextCursor: response?.repository?.projectsV2?.pageInfo?.endCursor ?? + response?.organization?.projectsV2?.pageInfo?.endCursor, + }; + }, + async getProjectV2Statuses({ + repoOwner, repoName, project, + }) { + const response = await this.graphql(repoName ? + queries.statusFieldsQuery : + queries.organizationStatusFieldsQuery, { + repoOwner, + repoName, + project, + }); + + return { + statuses: response?.repository?.projectV2?.field?.options ?? + response?.organization?.projectV2?.field?.options, + }; + }, + async getProjectColumns({ project }) { + return this._client().paginate(`GET /projects/${project}/columns`, {}); + }, + async getGists() { + return this._client().paginate("GET /gists", {}); + }, + async getTeams() { + return this._client().paginate("GET /user/teams", {}); + }, + async getFilteredNotifications({ + reason, data, + }) { + const notifications = await this._client().paginate("GET /notifications", data); + + if (reason) { + return notifications.filter((notification) => notification.reason === reason); + } + + return notifications; + }, + async getAuthenticatedUser() { + const response = await this._client().request("GET /user", {}); + + return response.data; + }, + async getFromUrl({ url }) { + const response = await this._client().request(`GET ${url.replace(this._baseApiUrl(), "")}`, {}); + + return response.data; + }, + async createIssue({ + repoFullname, data, + }) { + const response = await this._client().request(`POST /repos/${repoFullname}/issues`, data); + + return response.data; + }, + async createBranch({ + repoFullname, data, + }) { + const response = await this._client().request(`POST /repos/${repoFullname}/git/refs`, data); + + return response.data; + }, + async createRepository({ data }) { + const response = await this._client().request("POST /user/repos", data); + + return response.data; + }, + async createPullRequest({ + repoFullname, data, + }) { + const response = await this._client().request(`POST /repos/${repoFullname}/pulls`, data); + + return response.data; + }, + async getIssue({ + repoFullname, issueNumber, + }) { + const response = await this._client().request(`GET /repos/${repoFullname}/issues/${issueNumber}`, {}); + + return response.data; + }, + async updateIssue({ + repoFullname, issueNumber, data, + }) { + const response = await this._client().request(`PATCH /repos/${repoFullname}/issues/${issueNumber}`, data); + + return response.data; + }, + async createIssueComment({ + repoFullname, issueNumber, data, + }) { + const response = await this._client().request(`POST /repos/${repoFullname}/issues/${issueNumber}/comments`, data); + + return response.data; + }, + async getIssueFromProjectCard({ + repoFullname, cardId, + }) { + const { data: card } = await this._client().request(`GET /projects/columns/cards/${cardId}`, {}); + if (!card?.content_url) { + console.log("No issue associated with this card"); + return; + } + const issueId = card.content_url.split("/").pop(); + const { data: issue } = await this._client().request(`GET /repos/${repoFullname}/issues/${issueId}`, {}); + return issue; + }, + async searchIssueAndPullRequests({ + query, maxResults, + }) { + let issues = []; + + for await (const response of this._client().paginate.iterator( + "GET /search/issues", + { + q: query, + per_page: 100, + }, + )) { + issues = issues.concat(response.data); + + if (issues.length >= maxResults) { + break; + } + } + + return issues; + }, + async getRepositoryPullRequests({ repoFullname }) { + return this._client().paginate(`GET /repos/${repoFullname}/pulls`, {}); + }, + async getPullRequestForCommit({ + repoFullname, sha, + }) { + const response = await this._client().request(`GET /repos/${repoFullname}/commits/${sha}/pulls`, {}); + + return response.data[0]; + }, + async getReviewsForPullRequest({ + repoFullname, pullNumber, + }) { + const response = await this._client().request(`GET /repos/${repoFullname}/pulls/${pullNumber}/reviews`, {}); + + return response.data; + }, + async getCommits({ + repoFullname, ...data + }) { + const { data: commits } = await this._client().request(`GET /repos/${repoFullname}/commits`, { + ...data, + }); + return commits; + }, + async getBranches({ + repoFullname, ...data + }) { + const { data: branches } = await this._client().request(`GET /repos/${repoFullname}/branches`, { + ...data, + }); + return branches; + }, + async getProjectCards({ + columnId, ...data + }) { + const { data: cards } = await this._client().request(`GET /projects/columns/${columnId}/cards`, { + ...data, + }); + return cards; + }, + async createOrUpdateFileContent({ + repoFullname, + path, + fileContent, + commitMessage, + branch = null, + }) { + const data = { + message: commitMessage, + content: Buffer.from(fileContent).toString("base64"), + }; + const fileExists = await this._makeRequest({ + path: `/repos/${repoFullname}/contents/${path}`, + validateStatus: () => true, + }); + if (fileExists.sha) { + console.log("File exists, overwriting."); + data.sha = fileExists.sha; + } + if (branch) { + data.branch = branch; + } + return this._makeRequest({ + path: `/repos/${repoFullname}/contents/${path}`, + method: "put", + data: data, + }); + }, + async createGist(data) { + return this._makeRequest({ + path: "/gists", + method: "post", + data, + }); + }, + async listGistsFromUser(username, params = {}) { + return this._makeRequest({ + path: `/users/${username}/gists`, + params, + }); + }, + async updateGist(gistId, data) { + return this._makeRequest({ + path: `/gists/${gistId}`, + method: "patch", + data, + }); + }, + }, +}; diff --git a/components/github/package.json b/components/github/package.json index dab10cf104232..ecf2a346d22e9 100644 --- a/components/github/package.json +++ b/components/github/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/github", - "version": "0.3.16", + "version": "0.4.0", "description": "Pipedream Github Components", "main": "github.app.mjs", "keywords": [ diff --git a/components/github/sources/new-card-in-column/new-card-in-column.mjs b/components/github/sources/new-card-in-column/new-card-in-column.mjs index 2ee6582a224f9..5034eb8d8168d 100644 --- a/components/github/sources/new-card-in-column/new-card-in-column.mjs +++ b/components/github/sources/new-card-in-column/new-card-in-column.mjs @@ -6,7 +6,7 @@ export default { key: "github-new-card-in-column", name: "New Card in Column (Classic Projects)", description: "Emit new event when a (classic) project card is created or moved to a specific column. For Projects V2 use `New Issue with Status` trigger. [More information here](https://docs.github.com/en/issues/organizing-your-work-with-project-boards/tracking-work-with-project-boards/adding-issues-and-pull-requests-to-a-project-board)", - version: "0.1.11", + version: "0.1.13", type: "source", props: { ...common.props, diff --git a/components/github/sources/new-commit/new-commit.mjs b/components/github/sources/new-commit/new-commit.mjs index facc97780c11e..693143f116cb8 100644 --- a/components/github/sources/new-commit/new-commit.mjs +++ b/components/github/sources/new-commit/new-commit.mjs @@ -6,7 +6,7 @@ export default { key: "github-new-commit", name: "New Commit (Instant)", description: "Emit new events on new commits to a repo or branch", - version: "0.1.10", + version: "0.1.12", type: "source", dedupe: "unique", props: { diff --git a/components/github/sources/new-discussion/new-discussion.mjs b/components/github/sources/new-discussion/new-discussion.mjs index 2878fa6914749..bbc4acebc2758 100644 --- a/components/github/sources/new-discussion/new-discussion.mjs +++ b/components/github/sources/new-discussion/new-discussion.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-discussion", name: "New Discussion (Instant)", description: "Emit new events on new discussion to a repository", - version: "0.0.3", + version: "0.0.5", type: "source", dedupe: "unique", methods: { diff --git a/components/github/sources/new-gist/new-gist.mjs b/components/github/sources/new-gist/new-gist.mjs index c651e55da5c14..e7c7af072584c 100644 --- a/components/github/sources/new-gist/new-gist.mjs +++ b/components/github/sources/new-gist/new-gist.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-gist", name: "New Gist", description: "Emit new events when new gists are created by the authenticated user", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-issue-with-status/new-issue-with-status.mjs b/components/github/sources/new-issue-with-status/new-issue-with-status.mjs index 16d5a14ecdf20..33fc67c5796cf 100644 --- a/components/github/sources/new-issue-with-status/new-issue-with-status.mjs +++ b/components/github/sources/new-issue-with-status/new-issue-with-status.mjs @@ -7,7 +7,7 @@ export default { key: "github-new-issue-with-status", name: "New Issue with Status (Projects V2)", description: "Emit new event when a project issue is tagged with a specific status. Currently supports Organization Projects only. [More information here](https://docs.github.com/en/issues/planning-and-tracking-with-projects/managing-items-in-your-project/adding-items-to-your-project)", - version: "0.0.10", + version: "0.0.12", type: "source", dedupe: "unique", props: { diff --git a/components/github/sources/new-mention/new-mention.mjs b/components/github/sources/new-mention/new-mention.mjs index 7759173f622a7..1c467de79e2f6 100644 --- a/components/github/sources/new-mention/new-mention.mjs +++ b/components/github/sources/new-mention/new-mention.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-mention", name: "New Mention", description: "Emit new events when you are @mentioned in a new commit, comment, issue or pull request", - version: "0.1.9", + version: "0.1.12", type: "source", hooks: { async activate() { diff --git a/components/github/sources/new-notification/new-notification.mjs b/components/github/sources/new-notification/new-notification.mjs index 7c53304d5d314..c2808cbb5b060 100644 --- a/components/github/sources/new-notification/new-notification.mjs +++ b/components/github/sources/new-notification/new-notification.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-notification", name: "New Notification", description: "Emit new events when you received a new notification", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-organization/new-organization.mjs b/components/github/sources/new-organization/new-organization.mjs index 2c49e9e79f06e..1e1f9d6260b41 100644 --- a/components/github/sources/new-organization/new-organization.mjs +++ b/components/github/sources/new-organization/new-organization.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-organization", name: "New Organization", description: "Emit new events when the authenticated user is added to a new organization", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-repository/new-repository.mjs b/components/github/sources/new-repository/new-repository.mjs index 09826adae044e..3823b671807f1 100644 --- a/components/github/sources/new-repository/new-repository.mjs +++ b/components/github/sources/new-repository/new-repository.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-repository", name: "New Repository", description: "Emit new events when new repositories are created", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-review-request/new-review-request.mjs b/components/github/sources/new-review-request/new-review-request.mjs index 0ca5495b012d7..c3fc62a4814d0 100644 --- a/components/github/sources/new-review-request/new-review-request.mjs +++ b/components/github/sources/new-review-request/new-review-request.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-review-request", name: "New Review Request", description: "Emit new events when you or a team you're a member of are requested to review a pull request", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-security-alert/new-security-alert.mjs b/components/github/sources/new-security-alert/new-security-alert.mjs index a57d010eaf81d..e04cdbac2ee45 100644 --- a/components/github/sources/new-security-alert/new-security-alert.mjs +++ b/components/github/sources/new-security-alert/new-security-alert.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-security-alert", name: "New Security Alert", description: "Emit new events when GitHub discovers a security vulnerability in one of your repositories", - version: "0.1.11", + version: "0.1.13", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/new-team/new-team.mjs b/components/github/sources/new-team/new-team.mjs index f0842742de1d7..b80b389c11f38 100644 --- a/components/github/sources/new-team/new-team.mjs +++ b/components/github/sources/new-team/new-team.mjs @@ -5,7 +5,7 @@ export default { key: "github-new-team", name: "New Team", description: "Emit new events when the user is added to a new team", - version: "0.1.9", + version: "0.1.11", type: "source", dedupe: "unique", async run() { diff --git a/components/github/sources/webhook-events/webhook-events.mjs b/components/github/sources/webhook-events/webhook-events.mjs index e1be24cc03892..2286f0b24866a 100644 --- a/components/github/sources/webhook-events/webhook-events.mjs +++ b/components/github/sources/webhook-events/webhook-events.mjs @@ -7,7 +7,7 @@ export default { name: "New Webhook Event (Instant)", description: "Emit new event for each selected event type", type: "source", - version: "0.0.12", + version: "0.0.14", props: { ...common.props, events: { diff --git a/components/gladia/gladia.app.mjs b/components/gladia/gladia.app.mjs new file mode 100644 index 0000000000000..35ef3c38ab43d --- /dev/null +++ b/components/gladia/gladia.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "gladia", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/gladia/package.json b/components/gladia/package.json new file mode 100644 index 0000000000000..6bc1fd9fc2ed0 --- /dev/null +++ b/components/gladia/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/gladia", + "version": "0.0.1", + "description": "Pipedream Gladia Components", + "main": "gladia.app.mjs", + "keywords": [ + "pipedream", + "gladia" + ], + "homepage": "https://pipedream.com/apps/gladia", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/gmail_custom_oauth/README.md b/components/gmail_custom_oauth/README.md index 1727ed1679576..37330411bb40e 100644 --- a/components/gmail_custom_oauth/README.md +++ b/components/gmail_custom_oauth/README.md @@ -84,3 +84,19 @@ At this point, you should have a Gmail App under your Google Project, and a set ![Check all scopes to include grant your integration permission](https://res.cloudinary.com/pipedreamin/image/upload/v1663269729/docs/components/CleanShot_2022-09-15_at_15.20.26_jlnyw4.gif) 7. Click the final **Connect** and your custom Gmail app should be integrated into Pipedream! + +## Publish your custom Gmail app +Google has a [7 day expiration window](https://developers.google.com/identity/protocols/oauth2#:~:text=A%20Google%20Cloud,Connect%20equivalents) on refresh tokens for applications with a publishing status of "Testing", so you will need to **Publish** your application in order to maintain your account connection. + +1. Navigate to your application, and click **OAuth Consent Screen** on the lefthand sidebar. +2. Under **Publishing status**, click **Publish App**. If you included any sensitive or restricted scopes in your app, there will be a disclosure stating that you will need to go through the process of verification. Click **Confirm**. +3. Your application will not be available externally unless you share your **client_id** with others, and you will not have to go through the verification process unless you intend to onboard over 100 users. +4. The publishing status should be set to **In production**, and your account should maintain its connection without an expiration window. + +![Publish your application](https://res.cloudinary.com/dpenc2lit/image/upload/v1698166716/Screenshot_2023-10-24_at_9.50.06_AM_lve7wq.png) + +![Confirmation of changes](https://res.cloudinary.com/dpenc2lit/image/upload/v1698166716/Screenshot_2023-10-24_at_9.50.18_AM_mndtyc.png) + +# Troubleshooting +**Application disconnects after 7 days**
+If your developer application disconnects after 7 days, you need to follow the steps above to Publish your custom Gmail app in order to keep your account connected. \ No newline at end of file diff --git a/components/google_calendar/actions/create-event/create-event.mjs b/components/google_calendar/actions/create-event/create-event.mjs index 5933bb929408a..c47e639d8b457 100644 --- a/components/google_calendar/actions/create-event/create-event.mjs +++ b/components/google_calendar/actions/create-event/create-event.mjs @@ -1,11 +1,12 @@ import googleCalendar from "../../google_calendar.app.mjs"; import createEventCommon from "../common/create-event-common.mjs"; +import { v4 as uuidv4 } from "uuid"; export default { key: "google_calendar-create-event", name: "Create Event", description: "Create an event to the Google Calendar. [See the documentation](https://googleapis.dev/nodejs/googleapis/latest/calendar/classes/Resource$Events.html#insert)", - version: "0.1.6", + version: "0.1.7", type: "action", props: { googleCalendar, @@ -15,6 +16,12 @@ export default { "calendarId", ], }, + createMeetRoom: { + label: "Create Meet Room", + description: "Create a Google Meet room for this event.", + type: "boolean", + optional: true, + }, ...createEventCommon.props({ isUpdate: false, }), @@ -26,7 +33,7 @@ export default { const timeZone = this.getTimeZone(this.timeZone); const attendees = this.formatAttendees(this.attendees); - const response = await this.googleCalendar.createEvent({ + const data = { calendarId: this.calendarId, sendUpdates: this.sendUpdates, sendNotifications: this.sendNotifications, @@ -44,7 +51,21 @@ export default { }), attendees, }, - }); + }; + + if (this.createMeetRoom) { + data.conferenceDataVersion = 1; + data.resource.conferenceData = { + createRequest: { + requestId: uuidv4(), + conferenceSolutionKey: { + type: "hangoutsMeet", + }, + }, + }; + } + + const response = await this.googleCalendar.createEvent(data); $.export("$summary", `Successfully created event: "${response.id}"`); diff --git a/components/google_calendar/package.json b/components/google_calendar/package.json index 1b00f94ad8f5f..5747c92713505 100644 --- a/components/google_calendar/package.json +++ b/components/google_calendar/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_calendar", - "version": "0.3.15", + "version": "0.3.16", "description": "Pipedream Google_calendar Components", "main": "google_calendar.app.mjs", "keywords": [ diff --git a/components/google_drive/actions/upload-file/upload-file.mjs b/components/google_drive/actions/upload-file/upload-file.mjs index 0b73e78def468..f66e670ef2462 100644 --- a/components/google_drive/actions/upload-file/upload-file.mjs +++ b/components/google_drive/actions/upload-file/upload-file.mjs @@ -10,7 +10,7 @@ export default { key: "google_drive-upload-file", name: "Upload File", description: "Copy an existing file to Google Drive. [See the docs](https://developers.google.com/drive/api/v3/manage-uploads) for more information", - version: "0.1.1", + version: "0.1.2", type: "action", props: { googleDrive, @@ -69,6 +69,27 @@ export default { default: GOOGLE_DRIVE_UPLOAD_TYPE_MULTIPART, optional: true, }, + replaceFile: { + type: "boolean", + label: "Replace File", + description: "Whether should replace file case it exists, default: `false`", + optional: true, + default: false, + }, + }, + methods: { + async getFileIdForReplace(filename, parentId) { + if (this.replaceFile) { + const { files } = await this.googleDrive.listFilesInPage(null, { + q: `name = '${filename}' and '${parentId || "root"}' in parents and trashed = false`, + fields: "files/id,files/name,files/parents", + }); + if (files.length) { + return files[0].id; + } + } + return null; + }, }, async run({ $ }) { const { @@ -83,21 +104,40 @@ export default { throw new Error("One of File URL and File Path is required."); } const driveId = this.googleDrive.getDriveId(this.drive); + + const filename = name || path.basename(fileUrl || filePath); + const fileId = await this.getFileIdForReplace(filename, parentId); + const file = await getFileStream({ $, fileUrl, filePath, }); console.log(`Upload type: ${uploadType}.`); - const resp = await this.googleDrive.createFile(omitEmptyStringValues({ - file, - mimeType, - name: name || path.basename(fileUrl || filePath), - parentId, - driveId, - uploadType, - })); - $.export("$summary", `Successfully uploaded a new file, "${resp.name}"`); - return resp; + + let result = null; + if (fileId) { + await this.googleDrive.updateFileMedia(fileId, file, omitEmptyStringValues({ + mimeType, + uploadType, + })); + result = await this.googleDrive.updateFile(fileId, omitEmptyStringValues({ + name: filename, + mimeType, + uploadType, + })); + $.export("$summary", `Successfully updated file, "${result.name}"`); + } else { + result = await this.googleDrive.createFile(omitEmptyStringValues({ + file, + mimeType, + name: filename, + parentId, + driveId, + uploadType, + })); + $.export("$summary", `Successfully uploaded a new file, "${result.name}"`); + } + return result; }, }; diff --git a/components/google_drive/package.json b/components/google_drive/package.json index 0fb992cd553c0..b18d25428ef76 100644 --- a/components/google_drive/package.json +++ b/components/google_drive/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_drive", - "version": "0.6.10", + "version": "0.6.11", "description": "Pipedream Google_drive Components", "main": "google_drive.app.mjs", "keywords": [ diff --git a/components/google_fit_developer_app/README.md b/components/google_fit_developer_app/README.md index 39eae12c0f75a..c833533dc6d7a 100644 --- a/components/google_fit_developer_app/README.md +++ b/components/google_fit_developer_app/README.md @@ -83,3 +83,19 @@ At this point, you should have a Google Fit App under your Google Project, and a ![Check all scopes to include grant your integration permission](https://res.cloudinary.com/dpenc2lit/image/upload/v1692293421/Screenshot_2023-08-17_at_10.30.15_AM_ymeont.png) 7. Click the final **Connect** and your custom Google Fit app should be integrated into Pipedream! + +## Publish your Google Fit app +Google has a [7 day expiration window](https://developers.google.com/identity/protocols/oauth2#:~:text=A%20Google%20Cloud,Connect%20equivalents) on refresh tokens for applications with a publishing status of "Testing", so you will need to **Publish** your application in order to maintain your account connection. + +1. Navigate to your application, and click **OAuth Consent Screen** on the lefthand sidebar. +2. Under **Publishing status**, click **Publish App**. If you included any sensitive or restricted scopes in your app, there will be a disclosure stating that you will need to go through the process of verification. Click **Confirm**. +3. Your application will not be available externally unless you share your **client_id** with others, and you will not have to go through the verification process unless you intend to onboard over 100 users. +4. The publishing status should be set to **In production**, and your account should maintain its connection without an expiration window. + +![Publish your application](https://res.cloudinary.com/dpenc2lit/image/upload/v1698166716/Screenshot_2023-10-24_at_9.50.06_AM_lve7wq.png) + +![Confirmation of changes](https://res.cloudinary.com/dpenc2lit/image/upload/v1698166716/Screenshot_2023-10-24_at_9.50.18_AM_mndtyc.png) + +# Troubleshooting +**Application disconnects after 7 days**
+If your developer application disconnects after 7 days, you need to follow the steps above to Publish your Google Fit app in order to keep your account connected. \ No newline at end of file diff --git a/components/google_sheets/package.json b/components/google_sheets/package.json index d88851bab33e5..c781932f33b0e 100644 --- a/components/google_sheets/package.json +++ b/components/google_sheets/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_sheets", - "version": "0.5.6", + "version": "0.5.7", "description": "Pipedream Google_sheets Components", "main": "google_sheets.app.mjs", "keywords": [ diff --git a/components/google_sheets/sources/common/new-updates.mjs b/components/google_sheets/sources/common/new-updates.mjs index d8a977c924cff..e1060a2f7d8d4 100644 --- a/components/google_sheets/sources/common/new-updates.mjs +++ b/components/google_sheets/sources/common/new-updates.mjs @@ -67,6 +67,14 @@ export default { _setSheetValues(id, sheetValues) { this.db.set(id, sheetValues); }, + indexToColumnLabel(index) { + let columnLabel = ""; + while (index >= 0) { + columnLabel = String.fromCharCode((index % 26) + 65) + columnLabel; + index = Math.floor(index / 26) - 1; + } + return columnLabel; + }, getContentChanges(colCount, newValues, oldValues, changes, i) { // loop through comparing the values of each cell for (let j = 0; j < colCount; j++) { @@ -82,7 +90,7 @@ export default { : ""; if (newValue !== oldValue) { changes.push({ - cell: `${String.fromCharCode(j + 65)}:${i + 1}`, + cell: `${this.indexToColumnLabel(j)}:${i + 1}`, previous_value: oldValue, new_value: newValue, }); diff --git a/components/google_sheets/sources/new-updates-shared-drive/new-updates-shared-drive.mjs b/components/google_sheets/sources/new-updates-shared-drive/new-updates-shared-drive.mjs index d644fa1a183ec..a91e53c2f8d66 100644 --- a/components/google_sheets/sources/new-updates-shared-drive/new-updates-shared-drive.mjs +++ b/components/google_sheets/sources/new-updates-shared-drive/new-updates-shared-drive.mjs @@ -9,7 +9,7 @@ export default { type: "source", name: "New Updates (Shared Drive, Instant)", description: "Emit new event each time a row or cell is updated in a spreadsheet in a shared drive", - version: "0.1.3", + version: "0.1.4", dedupe: "unique", props: { ...httpBase.props, diff --git a/components/google_sheets/sources/new-updates/new-updates.mjs b/components/google_sheets/sources/new-updates/new-updates.mjs index 51c092e3ce379..caab681170f51 100644 --- a/components/google_sheets/sources/new-updates/new-updates.mjs +++ b/components/google_sheets/sources/new-updates/new-updates.mjs @@ -9,7 +9,7 @@ export default { type: "source", name: "New Updates (Instant)", description: "Emit new event each time a row or cell is updated in a spreadsheet. To use this source with a spreadsheet in a [Shared Drive](https://support.google.com/a/users/answer/9310351), use the **New Updates (Shared Drive, Instant)** source instead.", - version: "0.1.3", + version: "0.1.4", dedupe: "unique", props: { ...httpBase.props, diff --git a/components/google_slides/actions/merge-data/merge-data.mjs b/components/google_slides/actions/merge-data/merge-data.mjs new file mode 100644 index 0000000000000..d585f769ef2a0 --- /dev/null +++ b/components/google_slides/actions/merge-data/merge-data.mjs @@ -0,0 +1,110 @@ +import app from "../../google_slides.app.mjs"; + +export default { + key: "google_slides-merge-data", + name: "Merge Data", + description: "Merge data into a presentation. [See the docs here](https://developers.google.com/slides/api/guides/merge)", + version: "0.0.1", + type: "action", + props: { + app, + drive: { + propDefinition: [ + app, + "watchedDrive", + ], + description: "The drive to select a presentation from. If you are connected with any [Google Shared Drives](https://support.google.com/a/users/answer/9310351), you can select it here.", + }, + presentationId: { + propDefinition: [ + app, + "presentationId", + (c) => ({ + driveId: app.methods.getDriveId(c.drive), + }), + ], + description: "Select the presentation to make a copy of.", + }, + title: { + type: "string", + label: "Title", + description: "The title of the new presentation.", + }, + placeholdersAndTexts: { + type: "object", + label: "Placeholders And Texts", + description: "The placeholders and texts to be merged into the presentation. So the key should be the placeholder ID and the value should be the text to be merged. As a placeholder example you can use `{{name}}` and the value can be `John Doe`.", + }, + placeholdersAndImageUrls: { + type: "object", + label: "Placeholders And Image URLs", + description: "The placeholders and image URLs to be merged into the presentation. So the key should be the placeholder ID and the value should be the image URL to be merged. As a placeholder example you can use `{{image}}` and the value can be `https://example.com/image.jpg`.", + optional: true, + }, + }, + methods: { + getTextRequests(placeholdersAndTexts = {}) { + return Object.entries(placeholdersAndTexts) + .map(([ + key, + value, + ]) => ({ + replaceAllText: { + containsText: { + text: key, + matchCase: true, + }, + replaceText: value, + }, + })); + }, + getImageRequests(placeholdersAndImageUrls = {}) { + return Object.entries(placeholdersAndImageUrls) + .map(([ + key, + value, + ]) => ({ + replaceAllShapesWithImage: { + imageUrl: value, + replaceMethod: "CENTER_INSIDE", + containsText: { + text: key, + matchCase: true, + }, + }, + })); + }, + batchUpdate(args = {}) { + const slides = this.app.slides(); + return slides.presentations.batchUpdate(args); + }, + }, + async run({ $ }) { + const { + app, + batchUpdate, + presentationId, + title, + placeholdersAndTexts, + placeholdersAndImageUrls, + getTextRequests, + getImageRequests, + } = this; + + const presentation = await app.copyPresentation(presentationId, title); + + const { data: response } = await batchUpdate({ + presentationId: presentation.id, + resource: { + requests: [ + ...getTextRequests(placeholdersAndTexts), + ...getImageRequests(placeholdersAndImageUrls), + ], + }, + }); + + $.export("$summary", `Successfully merged data into presentation with ID: ${response.presentationId}`); + + return response; + }, +}; diff --git a/components/google_slides/package.json b/components/google_slides/package.json index 0fd2842538043..0b2020347857f 100644 --- a/components/google_slides/package.json +++ b/components/google_slides/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_slides", - "version": "0.0.4", + "version": "0.1.0", "description": "Pipedream Google Slides Components", "main": "google_slides.app.mjs", "keywords": [ diff --git a/components/gorillastack/.gitignore b/components/gorillastack/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/gorillastack/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/gorillastack/actions/deploy-template/deploy-template.mjs b/components/gorillastack/actions/deploy-template/deploy-template.mjs new file mode 100644 index 0000000000000..6a10078c3df08 --- /dev/null +++ b/components/gorillastack/actions/deploy-template/deploy-template.mjs @@ -0,0 +1,54 @@ +import app from "../../gorillastack.app.mjs"; + +export default { + key: "gorillastack-deploy-template", + name: "Deploy Template", + description: "Deploy a template definition. [See the documentation](https://docs.gorillastack.com/swagger/v2#/templates/get_teams__teamId__templates_deployments)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Deployment Name", + description: "Name for this deployment.", + }, + templateDefinitionId: { + type: "string", + label: "Template Definition Id", + description: "Id of the template definition to deploy.", + async options() { + const { templateDeployments } = await this.app.getDeploymentTemplates({ + $: this, + params: { + "filter.status": "CREATED", + }, + }); + return templateDeployments.map((template) => ({ + label: template.name, + value: template._id, + })); + }, + }, + parameters: { + type: "object", + label: "Parameters", + description: "Parameters for this deployment.", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.app.deployTemplate({ + $, + data: { + name: this.name, + parameters: this.parameters || {}, + templateDefinition: { + uniqueId: this.templateDefinitionId, + }, + }, + }); + $.export("$summary", `Successfully deployed template "${this.templateDefinitionId}"`); + return response; + }, +}; diff --git a/components/gorillastack/actions/invite-user/invite-user.mjs b/components/gorillastack/actions/invite-user/invite-user.mjs new file mode 100644 index 0000000000000..74a9687a70069 --- /dev/null +++ b/components/gorillastack/actions/invite-user/invite-user.mjs @@ -0,0 +1,38 @@ +import app from "../../gorillastack.app.mjs"; + +export default { + key: "gorillastack-invite-user", + name: "Invite User", + description: "Invite new user to GorillaStack. [See the documentation](https://docs.gorillastack.com/swagger/v2#/users/post_teams__teamId__users_invite)", + version: "0.0.1", + type: "action", + props: { + app, + email: { + type: "string", + label: "Email", + description: "Email of the user to invite", + }, + role: { + type: "string", + label: "Role", + description: "Role of the user to invite", + options: [ + "guest", + "member", + "admin", + ], + }, + }, + async run({ $ }) { + const response = await this.app.inviteUser({ + $, + data: { + email: this.email, + role: this.role, + }, + }); + $.export("$summary", `Successfully invited "${this.email}" to GorillaStack`); + return response; + }, +}; diff --git a/components/gorillastack/app/gorillastack.app.ts b/components/gorillastack/app/gorillastack.app.ts deleted file mode 100644 index 6076cb68633aa..0000000000000 --- a/components/gorillastack/app/gorillastack.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "gorillastack", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/gorillastack/gorillastack.app.mjs b/components/gorillastack/gorillastack.app.mjs new file mode 100644 index 0000000000000..774269aa6c1a2 --- /dev/null +++ b/components/gorillastack/gorillastack.app.mjs @@ -0,0 +1,63 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "gorillastack", + propDefinitions: {}, + methods: { + _apiKey() { + return `Bearer ${this.$auth.api_key}`; + }, + _teamId() { + return this.$auth.team_id; + }, + _apiUrl() { + return `https://api.gorillastack.com/v2/teams/${this._teamId()}`; + }, + _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + ...args, + headers: { + "Authorization": this._apiKey(), + "Accept": "application/json", + "Content-Type": "application/json", + }, + }); + }, + inviteUser({ + $ = this, + data, + }) { + return this._makeRequest({ + $, + path: "/users/invite", + method: "POST", + data, + }); + }, + getDeploymentTemplates({ + $ = this, + params, + }) { + return this._makeRequest({ + $, + path: "/templates/deployments", + params, + }); + }, + deployTemplate({ + $ = this, + data, + }) { + return this._makeRequest({ + $, + path: "/templates/deployments", + method: "POST", + data, + }); + }, + }, +}; diff --git a/components/gorillastack/package.json b/components/gorillastack/package.json index 6ecaf9d314d03..eaa06e9b1ec25 100644 --- a/components/gorillastack/package.json +++ b/components/gorillastack/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/gorillastack", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream GorillaStack Components", - "main": "dist/app/gorillastack.app.mjs", + "main": "gorillastack.app.mjs", "keywords": [ "pipedream", "gorillastack" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/gorillastack", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/gumroad/package.json b/components/gumroad/package.json index b18b0b1c306ff..59821236d84ec 100644 --- a/components/gumroad/package.json +++ b/components/gumroad/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/gumroad", - "version": "0.0.4", + "version": "0.0.5", "description": "Pipedream Gumroad Components", "main": "gumroad.app.mjs", "keywords": [ diff --git a/components/gumroad/sources/common/common.mjs b/components/gumroad/sources/common/common.mjs index 78cc66f6cd5f6..8c264be1c4528 100644 --- a/components/gumroad/sources/common/common.mjs +++ b/components/gumroad/sources/common/common.mjs @@ -7,7 +7,7 @@ export default { db: "$.service.db", timer: { type: "$.interface.timer", - static: { + default: { intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, }, }, diff --git a/components/gumroad/sources/new-cancellation/new-cancellation.mjs b/components/gumroad/sources/new-cancellation/new-cancellation.mjs index 3ca03b557d114..bbe3fb949f0c7 100644 --- a/components/gumroad/sources/new-cancellation/new-cancellation.mjs +++ b/components/gumroad/sources/new-cancellation/new-cancellation.mjs @@ -3,7 +3,7 @@ import common from "../common/common.mjs"; export default { ...common, name: "New Cancellation", - version: "0.0.3", + version: "0.0.4", key: "gumroad-new-cancellation", description: "Emit new event on a sale is cancelled.", type: "source", diff --git a/components/gumroad/sources/new-product/new-product.mjs b/components/gumroad/sources/new-product/new-product.mjs index f41ca1f5ec47a..5ee1e9895b735 100644 --- a/components/gumroad/sources/new-product/new-product.mjs +++ b/components/gumroad/sources/new-product/new-product.mjs @@ -3,7 +3,7 @@ import common from "../common/common.mjs"; export default { ...common, name: "New Product", - version: "0.0.3", + version: "0.0.4", key: "gumroad-new-product", description: "Emit new event on each new product.", type: "source", diff --git a/components/gumroad/sources/new-refund/new-refund.mjs b/components/gumroad/sources/new-refund/new-refund.mjs index 000a80ca59234..4568a7304da83 100644 --- a/components/gumroad/sources/new-refund/new-refund.mjs +++ b/components/gumroad/sources/new-refund/new-refund.mjs @@ -3,7 +3,7 @@ import common from "../common/common.mjs"; export default { ...common, name: "New Refund", - version: "0.0.3", + version: "0.0.4", key: "gumroad-new-refund", description: "Emit new event on a sale is refunded.", type: "source", diff --git a/components/gumroad/sources/new-sale/new-sale.mjs b/components/gumroad/sources/new-sale/new-sale.mjs index 8528972967ab9..ec25632414523 100644 --- a/components/gumroad/sources/new-sale/new-sale.mjs +++ b/components/gumroad/sources/new-sale/new-sale.mjs @@ -3,7 +3,7 @@ import common from "../common/common.mjs"; export default { ...common, name: "New Sale", - version: "0.0.3", + version: "0.0.4", key: "gumroad-new-sale", description: "Emit new event on each new sale.", type: "source", diff --git a/components/helloleads/helloleads.app.mjs b/components/helloleads/helloleads.app.mjs new file mode 100644 index 0000000000000..b9efd8ae01160 --- /dev/null +++ b/components/helloleads/helloleads.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "helloleads", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/helloleads/package.json b/components/helloleads/package.json new file mode 100644 index 0000000000000..c2e0134c6dda0 --- /dev/null +++ b/components/helloleads/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/helloleads", + "version": "0.0.1", + "description": "Pipedream HelloLeads Components", + "main": "helloleads.app.mjs", + "keywords": [ + "pipedream", + "helloleads" + ], + "homepage": "https://pipedream.com/apps/helloleads", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/hellosign/actions/send-signature-request-from-template/send-signature-request-from-template.mjs b/components/hellosign/actions/send-signature-request-from-template/send-signature-request-from-template.mjs new file mode 100644 index 0000000000000..fb87ab27359f4 --- /dev/null +++ b/components/hellosign/actions/send-signature-request-from-template/send-signature-request-from-template.mjs @@ -0,0 +1,105 @@ +import hellosign from "../../hellosign.app.mjs"; + +export default { + key: "hellosign-send-signature-request-from-template", + name: "Send Signature Request From Template", + description: "Send a signature request from a template with Dropbox Sign. [See the documentation](https://developers.hellosign.com/api/reference/operation/signatureRequestSendWithTemplate/)", + version: "0.0.1", + type: "action", + props: { + hellosign, + templateId: { + propDefinition: [ + hellosign, + "templateId", + ], + }, + signers: { + propDefinition: [ + hellosign, + "signers", + ], + }, + role: { + propDefinition: [ + hellosign, + "role", + (c) => ({ + templateId: c.templateId, + }), + ], + }, + fileUrls: { + propDefinition: [ + hellosign, + "fileUrls", + ], + optional: true, + }, + allowDecline: { + propDefinition: [ + hellosign, + "allowDecline", + ], + }, + message: { + propDefinition: [ + hellosign, + "message", + ], + }, + subject: { + propDefinition: [ + hellosign, + "subject", + ], + }, + title: { + propDefinition: [ + hellosign, + "title", + ], + }, + testMode: { + propDefinition: [ + hellosign, + "testMode", + ], + }, + }, + async run({ $ }) { + const signers = []; + for (const [ + name, + email, + ] of Object.entries(this.signers)) { + signers.push({ + role: this.role, + name, + email_address: email, + }); + } + + const response = await this.hellosign.sendSignatureRequestWithTemplate({ + data: { + template_ids: [ + this.templateId, + ], + file_urls: this.fileUrls, + signers, + cc_email_addresses: this.ccEmailAddresses, + allow_decline: this.allowDecline, + message: this.message, + subject: this.subject, + title: this.title, + test_mode: this.testMode, + }, + }); + + if (response) { + $.export("$summary", "Successfully sent signature request"); + } + + return response; + }, +}; diff --git a/components/hellosign/actions/send-signature-request/send-signature-request.mjs b/components/hellosign/actions/send-signature-request/send-signature-request.mjs new file mode 100644 index 0000000000000..b76a800963a40 --- /dev/null +++ b/components/hellosign/actions/send-signature-request/send-signature-request.mjs @@ -0,0 +1,91 @@ +import hellosign from "../../hellosign.app.mjs"; + +export default { + key: "hellosign-send-signature-request", + name: "Send Signature Request", + description: "Send a signature request with Dropbox Sign. [See the documentation](https://developers.hellosign.com/api/reference/operation/signatureRequestSend/)", + version: "0.0.1", + type: "action", + props: { + hellosign, + fileUrls: { + propDefinition: [ + hellosign, + "fileUrls", + ], + }, + signers: { + propDefinition: [ + hellosign, + "signers", + ], + }, + ccEmailAddresses: { + propDefinition: [ + hellosign, + "ccEmailAddresses", + ], + }, + allowDecline: { + propDefinition: [ + hellosign, + "allowDecline", + ], + }, + message: { + propDefinition: [ + hellosign, + "message", + ], + }, + subject: { + propDefinition: [ + hellosign, + "subject", + ], + }, + title: { + propDefinition: [ + hellosign, + "title", + ], + }, + testMode: { + propDefinition: [ + hellosign, + "testMode", + ], + }, + }, + async run({ $ }) { + const signers = []; + for (const [ + name, + email, + ] of Object.entries(this.signers)) { + signers.push({ + name, + email_address: email, + }); + } + + const response = await this.hellosign.sendSignatureRequest({ + data: { + file_urls: this.fileUrls, + signers, + cc_email_addresses: this.ccEmailAddresses, + allow_decline: this.allowDecline, + message: this.message, + subject: this.subject, + title: this.title, + test_mode: this.testMode, + }, + }); + + if (response) { + $.export("$summary", "Successfully sent signature request"); + } + + return response; + }, +}; diff --git a/components/hellosign/hellosign.app.mjs b/components/hellosign/hellosign.app.mjs index 6b2a9ce6909d2..db4d13634311e 100644 --- a/components/hellosign/hellosign.app.mjs +++ b/components/hellosign/hellosign.app.mjs @@ -1,11 +1,138 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "hellosign", - propDefinitions: {}, + propDefinitions: { + templateId: { + type: "string", + label: "Template", + description: "Identifier of a template", + async options({ page }) { + const { templates } = await this.listTemplates({ + params: { + account_id: this.accountId(), + page: page + 1, + }, + }); + return templates?.map(({ + template_id: value, title: label, + }) => ({ + value, + label, + })) || []; + }, + }, + role: { + type: "string", + label: "Role", + description: "The role of the signer(s)", + async options({ templateId }) { + const { template: { signer_roles: roles } } = await this.getTemplate({ + templateId, + }); + return roles?.map(({ name }) => name ) || []; + }, + }, + signers: { + type: "object", + label: "Signers", + description: "Enter names & email addresses as key/value pairs with name as the key and email as the value.", + }, + fileUrls: { + type: "string[]", + label: "File URLs", + description: "An array of file URLs to send for signature", + }, + ccEmailAddresses: { + type: "string[]", + label: "CC Email Addresses", + description: "The email addresses that should be CCed.", + optional: true, + }, + allowDecline: { + type: "boolean", + label: "Allow Decline", + description: "Allows signers to decline to sign a document", + default: false, + optional: true, + }, + message: { + type: "string", + label: "Message", + description: "The custom message in the email that will be sent to the signers.", + optional: true, + }, + subject: { + type: "string", + label: "Subject", + description: "The subject in the email that will be sent to the signers.", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The title you want to assign to the SignatureRequest.", + optional: true, + }, + testMode: { + type: "boolean", + label: "Test Mode", + description: "Whether this is a test, the signature request will not be legally binding if set to `true`.", + default: false, + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.hellosign.com/v3"; + }, + _headers() { + return { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + accountId() { + return this.$auth.oauth_uid; + }, + getTemplate({ + templateId, ...args + }) { + return this._makeRequest({ + path: `/template/${templateId}`, + ...args, + }); + }, + listTemplates(args = {}) { + return this._makeRequest({ + path: "/template/list", + ...args, + }); + }, + sendSignatureRequest(args = {}) { + return this._makeRequest({ + path: "/signature_request/send", + method: "POST", + ...args, + }); + }, + sendSignatureRequestWithTemplate(args = {}) { + return this._makeRequest({ + path: "/signature_request/send_with_template", + method: "POST", + ...args, + }); }, }, }; diff --git a/components/hellosign/package.json b/components/hellosign/package.json new file mode 100644 index 0000000000000..f88436baa7a06 --- /dev/null +++ b/components/hellosign/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/hellosign", + "version": "0.0.1", + "description": "Pipedream Hellosign Components", + "main": "hellosign.app.mjs", + "keywords": [ + "pipedream", + "hellosign" + ], + "homepage": "https://pipedream.com/apps/hellosign", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" + } +} diff --git a/components/hubspot/hubspot.app.mjs b/components/hubspot/hubspot.app.mjs index b19aa977ba4b9..b393a1e42afe4 100644 --- a/components/hubspot/hubspot.app.mjs +++ b/components/hubspot/hubspot.app.mjs @@ -89,6 +89,20 @@ export default { description: "Watch for new events concerning the object type specified.", options: OBJECT_TYPES, }, + objectSchema: { + type: "string", + label: "Object Schema", + description: "Watch for new events of objects with the specified custom schema.", + async options() { + const response = await this.listSchemas(); + return response?.results?.map(({ + objectTypeId, name, + }) => ({ + label: name, + value: objectTypeId, + })); + }, + }, objectId: { type: "string", label: "Object ID", @@ -567,6 +581,11 @@ export default { $, }); }, + async listSchemas($) { + return this.makeRequest(API_PATH.CRMV3, "/schemas", { + $, + }); + }, async getSchema(objectType, $) { return this.makeRequest(API_PATH.CRMV3, `/schemas/${objectType}`, { $, diff --git a/components/hubspot/package.json b/components/hubspot/package.json index a040c241bf182..f75135ba966f3 100644 --- a/components/hubspot/package.json +++ b/components/hubspot/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/hubspot", - "version": "0.6.0", + "version": "0.7.0", "description": "Pipedream Hubspot Components", "main": "hubspot.app.mjs", "keywords": [ diff --git a/components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs b/components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs new file mode 100644 index 0000000000000..a8b42f1a4e7e3 --- /dev/null +++ b/components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs @@ -0,0 +1,58 @@ +import common from "../common/common.mjs"; + +export default { + ...common, + key: "hubspot-new-or-updated-custom-object", + name: "New or Updated Custom Object", + description: "Emit new event each time a Custom Object of the specified schema is updated.", + version: "0.0.1", + dedupe: "unique", + type: "source", + props: { + ...common.props, + objectSchema: { + propDefinition: [ + common.props.hubspot, + "objectSchema", + ], + }, + }, + hooks: {}, + methods: { + ...common.methods, + getTs(object) { + return Date.parse(object.updatedAt); + }, + generateMeta(object) { + const { id } = object; + const ts = this.getTs(object); + return { + id: `${id}${ts}`, + summary: `Record ID: ${id}`, + ts, + }; + }, + isRelevant(object, updatedAfter) { + return this.getTs(object) > updatedAfter; + }, + getParams() { + return null; + }, + getObjectParams(object) { + return { + limit: 100, + sorts: [ + { + propertyName: "hs_lastmodifieddate", + direction: "DESCENDING", + }, + ], + object, + }; + }, + async processResults(after) { + const params = this.getObjectParams(this.objectSchema); + await this.searchCRM(params, after); + }, + }, +}; diff --git a/components/imejis_io/imejis_io.app.mjs b/components/imejis_io/imejis_io.app.mjs new file mode 100644 index 0000000000000..ff8fa34b06b2e --- /dev/null +++ b/components/imejis_io/imejis_io.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "imejis_io", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/imejis_io/package.json b/components/imejis_io/package.json new file mode 100644 index 0000000000000..ec298d3482f24 --- /dev/null +++ b/components/imejis_io/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/imejis_io", + "version": "0.0.1", + "description": "Pipedream Imejis.io Components", + "main": "imejis_io.app.mjs", + "keywords": [ + "pipedream", + "imejis_io" + ], + "homepage": "https://pipedream.com/apps/imejis_io", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/insites/insites.app.mjs b/components/insites/insites.app.mjs new file mode 100644 index 0000000000000..d5ef9076ed1ee --- /dev/null +++ b/components/insites/insites.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "insites", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/insites/package.json b/components/insites/package.json new file mode 100644 index 0000000000000..c947e328c85c6 --- /dev/null +++ b/components/insites/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/insites", + "version": "0.0.1", + "description": "Pipedream Insites Components", + "main": "insites.app.mjs", + "keywords": [ + "pipedream", + "insites" + ], + "homepage": "https://pipedream.com/apps/insites", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/jira/actions/add-attachment-to-issue/add-attachment-to-issue.mjs b/components/jira/actions/add-attachment-to-issue/add-attachment-to-issue.mjs index 952082a62eced..b53c9e1b9ca57 100644 --- a/components/jira/actions/add-attachment-to-issue/add-attachment-to-issue.mjs +++ b/components/jira/actions/add-attachment-to-issue/add-attachment-to-issue.mjs @@ -8,7 +8,7 @@ export default { key: "jira-add-attachment-to-issue", name: "Add Attachment To Issue", description: "Adds an attachment to an issue, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-issue-issueidorkey-attachments-post)", - version: "0.2.9", + version: "0.2.10", type: "action", props: { jira, diff --git a/components/jira/actions/add-comment-to-issue/add-comment-to-issue.mjs b/components/jira/actions/add-comment-to-issue/add-comment-to-issue.mjs index a28809b3f8461..8b329e3fadec7 100644 --- a/components/jira/actions/add-comment-to-issue/add-comment-to-issue.mjs +++ b/components/jira/actions/add-comment-to-issue/add-comment-to-issue.mjs @@ -5,7 +5,7 @@ export default { key: "jira-add-comment-to-issue", name: "Add Comment To Issue", description: "Adds a new comment to an issue, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-post)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/add-multiple-attachments-to-issue/add-multiple-attachments-to-issue.mjs b/components/jira/actions/add-multiple-attachments-to-issue/add-multiple-attachments-to-issue.mjs index baeba66cf1aed..84fe52d74ea64 100644 --- a/components/jira/actions/add-multiple-attachments-to-issue/add-multiple-attachments-to-issue.mjs +++ b/components/jira/actions/add-multiple-attachments-to-issue/add-multiple-attachments-to-issue.mjs @@ -8,7 +8,7 @@ export default { key: "jira-add-multiple-attachments-to-issue", name: "Add Multiple Attachments To Issue", description: "Adds multiple attachments to an issue, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-issue-issueidorkey-attachments-post)", - version: "0.0.7", + version: "0.0.8", type: "action", props: { jira, diff --git a/components/jira/actions/add-watcher-to-issue/add-watcher-to-issue.mjs b/components/jira/actions/add-watcher-to-issue/add-watcher-to-issue.mjs index cf98864dbcda2..0bffe8eeac9e4 100644 --- a/components/jira/actions/add-watcher-to-issue/add-watcher-to-issue.mjs +++ b/components/jira/actions/add-watcher-to-issue/add-watcher-to-issue.mjs @@ -3,7 +3,7 @@ import jira from "../../jira.app.mjs"; export default { key: "jira-add-watcher-to-issue", name: "Add Watcher To Issue", - version: "0.0.6", + version: "0.0.7", description: "Adds a user as a watcher of an issue by passing the account ID of the user, For example, `5b10ac8d82e05b22cc7d4ef5`, If no user is specified the calling user is added. [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-watchers/#api-rest-api-3-issue-issueidorkey-watchers-post)", type: "action", props: { diff --git a/components/jira/actions/assign-issue/assign-issue.mjs b/components/jira/actions/assign-issue/assign-issue.mjs index ebda56901a3ea..65a5964e6b6d3 100644 --- a/components/jira/actions/assign-issue/assign-issue.mjs +++ b/components/jira/actions/assign-issue/assign-issue.mjs @@ -3,7 +3,7 @@ import jira from "../../jira.app.mjs"; export default { key: "jira-assign-issue", name: "Assign Issue", - version: "0.0.6", + version: "0.0.7", description: "Assigns an issue to a user. [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-assignee-put)", type: "action", props: { diff --git a/components/jira/actions/create-issue/create-issue.mjs b/components/jira/actions/create-issue/create-issue.mjs index e735be96657d6..2a4620410a1f3 100644 --- a/components/jira/actions/create-issue/create-issue.mjs +++ b/components/jira/actions/create-issue/create-issue.mjs @@ -7,7 +7,7 @@ export default { key: "jira-create-issue", name: "Create Issue", description: "Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-issue-post)", - version: "0.1.13", + version: "0.1.14", type: "action", props: { ...common.props, diff --git a/components/jira/actions/create-version/create-version.mjs b/components/jira/actions/create-version/create-version.mjs index 4534eca1f8a4c..90e5dcc135a90 100644 --- a/components/jira/actions/create-version/create-version.mjs +++ b/components/jira/actions/create-version/create-version.mjs @@ -4,7 +4,7 @@ export default { key: "jira-create-version", name: "Create Jira Version in project", description: "Creates a project version., [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-versions/#api-rest-api-3-version-post)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/delete-project/delete-project.mjs b/components/jira/actions/delete-project/delete-project.mjs index d7bfcd026bd26..87cc46808bbd0 100644 --- a/components/jira/actions/delete-project/delete-project.mjs +++ b/components/jira/actions/delete-project/delete-project.mjs @@ -4,7 +4,7 @@ export default { key: "jira-delete-project", name: "Delete Project", description: "Deletes a project, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-projectidorkey-delete)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/get-all-projects/get-all-projects.mjs b/components/jira/actions/get-all-projects/get-all-projects.mjs index dac073bf0980e..966bfd1d1c5a1 100644 --- a/components/jira/actions/get-all-projects/get-all-projects.mjs +++ b/components/jira/actions/get-all-projects/get-all-projects.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-all-projects", name: "Get All Projects", description: "Gets metadata on all projects, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-project-get)", - version: "0.1.8", + version: "0.1.9", type: "action", props: { jira, diff --git a/components/jira/actions/get-issue/get-issue.mjs b/components/jira/actions/get-issue/get-issue.mjs index 2d180bf499996..7ff8e32388ee1 100644 --- a/components/jira/actions/get-issue/get-issue.mjs +++ b/components/jira/actions/get-issue/get-issue.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-issue", name: "Get Issue", description: "Gets the details for an issue. [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-get)", - version: "0.1.9", + version: "0.1.10", type: "action", props: { jira, diff --git a/components/jira/actions/get-task/get-task.mjs b/components/jira/actions/get-task/get-task.mjs index 2ae80a3549fc7..831a1836c4717 100644 --- a/components/jira/actions/get-task/get-task.mjs +++ b/components/jira/actions/get-task/get-task.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-task", name: "Get Task", description: "Gets the status of a long-running asynchronous task, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-tasks/#api-rest-api-3-task-taskid-get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/get-transitions/get-transitions.mjs b/components/jira/actions/get-transitions/get-transitions.mjs index b6616fe4d6e39..fbd06aa6ab6a7 100644 --- a/components/jira/actions/get-transitions/get-transitions.mjs +++ b/components/jira/actions/get-transitions/get-transitions.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-transitions", name: "Get Transitions", description: "Gets either all transitions or a transition that can be performed by the user on an issue, based on the issue's status, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/get-user/get-user.mjs b/components/jira/actions/get-user/get-user.mjs index ffb3ff7bc2674..f6debc37b3699 100644 --- a/components/jira/actions/get-user/get-user.mjs +++ b/components/jira/actions/get-user/get-user.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-user", name: "Get User", description: "Gets details of user, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-users/#api-rest-api-3-user-get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/get-users/get-users.mjs b/components/jira/actions/get-users/get-users.mjs index d16b116ab1856..3a826f638d923 100644 --- a/components/jira/actions/get-users/get-users.mjs +++ b/components/jira/actions/get-users/get-users.mjs @@ -4,7 +4,7 @@ export default { key: "jira-get-users", name: "Get Users", description: "Gets details of a list of users. [See docs here](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-users/#api-rest-api-3-users-search-get)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { jira, diff --git a/components/jira/actions/list-issue-comments/list-issue-comments.mjs b/components/jira/actions/list-issue-comments/list-issue-comments.mjs index efa7ae8e154f3..919e05440a99f 100644 --- a/components/jira/actions/list-issue-comments/list-issue-comments.mjs +++ b/components/jira/actions/list-issue-comments/list-issue-comments.mjs @@ -4,7 +4,7 @@ export default { key: "jira-list-issue-comments", name: "List Issue Comments", description: "Lists all comments for an issue, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/transition-issue/transition-issue.mjs b/components/jira/actions/transition-issue/transition-issue.mjs index 6fcc327ba1511..255bce88f3904 100644 --- a/components/jira/actions/transition-issue/transition-issue.mjs +++ b/components/jira/actions/transition-issue/transition-issue.mjs @@ -5,7 +5,7 @@ export default { key: "jira-transition-issue", name: "Transition Issue", description: "Performs an issue transition and, if the transition has a screen, updates the fields from the transition screen, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-post)", - version: "0.1.9", + version: "0.1.10", type: "action", props: { jira, diff --git a/components/jira/actions/update-comment/update-comment.mjs b/components/jira/actions/update-comment/update-comment.mjs index aa8a05a6e1df6..ec782e8e28601 100644 --- a/components/jira/actions/update-comment/update-comment.mjs +++ b/components/jira/actions/update-comment/update-comment.mjs @@ -5,7 +5,7 @@ export default { key: "jira-update-comment", name: "Update Comment", description: "Updates a comment, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-id-put)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { jira, diff --git a/components/jira/actions/update-issue/update-issue.mjs b/components/jira/actions/update-issue/update-issue.mjs index df73e8769b39d..12a568138cf21 100644 --- a/components/jira/actions/update-issue/update-issue.mjs +++ b/components/jira/actions/update-issue/update-issue.mjs @@ -1,12 +1,14 @@ import utils from "../../common/utils.mjs"; import common from "../common/issue.mjs"; +import constants from "../../common/constants.mjs"; +import { ConfigurationError } from "@pipedream/platform"; export default { ...common, key: "jira-update-issue", name: "Update Issue", description: "Updates an issue. A transition may be applied and issue properties updated as part of the edit, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-put)", - version: "0.2.7", + version: "0.2.8", type: "action", props: { ...common.props, @@ -20,6 +22,30 @@ export default { }), ], }, + projectId: { + propDefinition: [ + common.props.app, + "projectID", + ({ cloudId }) => ({ + cloudId, + }), + ], + optional: true, + }, + issueTypeId: { + reloadProps: true, + propDefinition: [ + common.props.app, + "issueType", + ({ + cloudId, projectId, + }) => ({ + cloudId, + projectId, + }), + ], + optional: true, + }, notifyUsers: { type: "boolean", label: "Notify Users", @@ -40,7 +66,7 @@ export default { }, transitionId: { label: "Transition ID", - description: "The ID of the issue transition. Required when specifying a transition to undertake.", + description: "The ID of the issue transition. Retrieving options requires a static `issueIdOrKey`. Required when specifying a transition to undertake.", propDefinition: [ common.props.app, "transition", @@ -56,23 +82,61 @@ export default { async additionalProps() { const { cloudId, + projectId, + issueTypeId, issueIdOrKey, } = this; - const { fields } = await this.app.getEditIssueMetadata({ - cloudId, - issueIdOrKey, - }); + try { + const { fields } = await this.app.getEditIssueMetadata({ + cloudId, + issueIdOrKey, + }); - return this.getDynamicFields({ - fields, - }); + return this.getDynamicFields({ + fields, + }); + } catch { + if (!issueTypeId) { + throw new ConfigurationError("Please enter `projectId` and `IssueTypeId` to retrieve additional props."); + } + const { + projects: [ + { + issuetypes: [ + { fields = {} } = {}, + ], + }, + ], + } = await this.app.getCreateIssueMetadata({ + cloudId, + params: { + projectIds: projectId, + issuetypeIds: issueTypeId, + expand: "projects.issuetypes.fields", + }, + }); + + const keys = [ + constants.FIELD_KEY.ISSUETYPE, + constants.FIELD_KEY.PROJECT, + ]; + + return this.getDynamicFields({ + fields, + predicate: ({ key }) => !keys.includes(key), + }); + } }, async run({ $ }) { const { app, cloudId, issueIdOrKey, + // eslint-disable-next-line no-unused-vars + projectId, + // eslint-disable-next-line no-unused-vars + issueTypeId, notifyUsers, overrideScreenSecurity, overrideEditableFlag, diff --git a/components/jira/jira.app.mjs b/components/jira/jira.app.mjs index 95b83b1f6711f..c31a35d25a157 100644 --- a/components/jira/jira.app.mjs +++ b/components/jira/jira.app.mjs @@ -181,26 +181,30 @@ export default { } let { startAt } = prevContext || {}; const pageSize = 50; - const resp = await this.getTransitions({ - cloudId, - issueIdOrKey, - params: { - startAt, - maxResults: pageSize, - }, - }); - startAt = startAt > 0 - ? startAt + pageSize - : pageSize; - return { - options: resp?.transitions?.map((issue) => ({ - value: issue.id, - label: issue.name, - })), - context: { - startAt, - }, - }; + try { + const resp = await this.getTransitions({ + cloudId, + issueIdOrKey, + params: { + startAt, + maxResults: pageSize, + }, + }); + startAt = startAt > 0 + ? startAt + pageSize + : pageSize; + return { + options: resp?.transitions?.map((issue) => ({ + value: issue.id, + label: issue.name, + })), + context: { + startAt, + }, + }; + } catch { + return []; + } }, }, fields: { diff --git a/components/jira/package.json b/components/jira/package.json index bd28510c14742..2db83e5aa289b 100644 --- a/components/jira/package.json +++ b/components/jira/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/jira", - "version": "0.0.13", + "version": "0.0.14", "description": "Pipedream Jira Components", "main": "jira.app.mjs", "keywords": [ diff --git a/components/jira/sources/events/events.mjs b/components/jira/sources/events/events.mjs index 035d786c667fe..ed5807c1a7cc7 100644 --- a/components/jira/sources/events/events.mjs +++ b/components/jira/sources/events/events.mjs @@ -5,7 +5,7 @@ export default { key: "jira-events", name: "New Event", description: "Emit new event when an event with subscribed event source triggered, [See the docs](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-webhooks/#api-rest-api-3-webhook-post)", - version: "0.0.7", + version: "0.0.8", type: "source", dedupe: "unique", ...common, diff --git a/components/jira/sources/issue-created/issue-created.mjs b/components/jira/sources/issue-created/issue-created.mjs index 31d92082a85f1..09c86c2784a0a 100644 --- a/components/jira/sources/issue-created/issue-created.mjs +++ b/components/jira/sources/issue-created/issue-created.mjs @@ -4,7 +4,7 @@ export default { key: "jira-issue-created", name: "New Issue Created Event", description: "Emit new event when an issue is created. Note that Jira supports only one webhook, if more sources are needed please use `New Event` source and select multiple events.", - version: "0.0.7", + version: "0.0.8", type: "source", dedupe: "unique", ...common, diff --git a/components/jira/sources/issue-deleted/issue-deleted.mjs b/components/jira/sources/issue-deleted/issue-deleted.mjs index 4debe3ce5c75d..9dc774691ad5d 100644 --- a/components/jira/sources/issue-deleted/issue-deleted.mjs +++ b/components/jira/sources/issue-deleted/issue-deleted.mjs @@ -4,7 +4,7 @@ export default { key: "jira-issue-deleted", name: "New Issue Deleted Event", description: "Emit new event when an issue is deleted. Note that Jira supports only one webhook, if more sources are needed please use `New Event` source and select multiple events.", - version: "0.0.7", + version: "0.0.8", type: "source", dedupe: "unique", ...common, diff --git a/components/jira/sources/issue-updated/issue-updated.mjs b/components/jira/sources/issue-updated/issue-updated.mjs index ffc9da2c5ac11..10857d60f4359 100644 --- a/components/jira/sources/issue-updated/issue-updated.mjs +++ b/components/jira/sources/issue-updated/issue-updated.mjs @@ -4,7 +4,7 @@ export default { key: "jira-issue-updated", name: "New Issue Updated Event", description: "Emit new event when an issue is updated. Note that Jira supports only one webhook, if more sources are needed please use `New Event` source and select multiple events.", - version: "0.0.7", + version: "0.0.8", type: "source", dedupe: "unique", ...common, diff --git a/components/jumpseller/.gitignore b/components/jumpseller/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/jumpseller/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/jumpseller/actions/create-product-variant/create-product-variant.mjs b/components/jumpseller/actions/create-product-variant/create-product-variant.mjs new file mode 100644 index 0000000000000..7df828613cfd4 --- /dev/null +++ b/components/jumpseller/actions/create-product-variant/create-product-variant.mjs @@ -0,0 +1,79 @@ +import jumpseller from "../../jumpseller.app.mjs"; + +export default { + key: "jumpseller-create-product-variant", + name: "Create Product Variant", + description: "Create a new product variant in Jumpseller. [See the documentation](https://jumpseller.com/support/api/#tag/Product-Variants/paths/~1products~1%7Bid%7D~1variants.json/post)", + version: "0.0.1", + type: "action", + props: { + jumpseller, + productId: { + propDefinition: [ + jumpseller, + "productId", + ], + }, + price: { + propDefinition: [ + jumpseller, + "price", + ], + description: "The price of the product variant.", + optional: true, + }, + sku: { + propDefinition: [ + jumpseller, + "sku", + ], + description: "The sku of the product variant.", + }, + stock: { + propDefinition: [ + jumpseller, + "stock", + ], + description: "Quantity in stock for the product variant.", + }, + options: { + type: "object", + label: "Options", + description: "Enter the name and value of options as key/value pairs.", + optional: true, + }, + }, + async run({ $ }) { + const options = []; + if (this.options) { + for (const [ + key, + value, + ] of Object.entries(this.options)) { + options.push({ + name: key, + value, + }); + } + } + + const { variant } = await this.jumpseller.createProductVariant({ + productId: this.productId, + data: { + variant: { + price: +this.price, + sku: this.sku, + stock: this.stock, + options, + }, + }, + $, + }); + + if (variant?.id) { + $.export("$summary", `Successfully created product variant with ID ${variant.id}.`); + } + + return variant; + }, +}; diff --git a/components/jumpseller/actions/create-product/create-product.mjs b/components/jumpseller/actions/create-product/create-product.mjs new file mode 100644 index 0000000000000..44190bc70bf74 --- /dev/null +++ b/components/jumpseller/actions/create-product/create-product.mjs @@ -0,0 +1,69 @@ +import jumpseller from "../../jumpseller.app.mjs"; + +export default { + key: "jumpseller-create-product", + name: "Create Product", + description: "Create a new product in Jumpseller. [See the documentation](https://jumpseller.com/support/api/#tag/Products/paths/~1products.json/post)", + version: "0.0.1", + type: "action", + props: { + jumpseller, + name: { + propDefinition: [ + jumpseller, + "name", + ], + }, + price: { + propDefinition: [ + jumpseller, + "price", + ], + }, + description: { + propDefinition: [ + jumpseller, + "description", + ], + }, + stock: { + propDefinition: [ + jumpseller, + "stock", + ], + }, + sku: { + propDefinition: [ + jumpseller, + "sku", + ], + }, + status: { + propDefinition: [ + jumpseller, + "status", + ], + }, + }, + async run({ $ }) { + const { product } = await this.jumpseller.createProduct({ + data: { + product: { + name: this.name, + price: +this.price, + description: this.description, + stock: this.stock, + sku: this.sku, + status: this.status, + }, + }, + $, + }); + + if (product?.id) { + $.export("$summary", `Successfully created product with ID ${product.id}.`); + } + + return product; + }, +}; diff --git a/components/jumpseller/actions/update-product/update-product.mjs b/components/jumpseller/actions/update-product/update-product.mjs new file mode 100644 index 0000000000000..fbba94a67fd92 --- /dev/null +++ b/components/jumpseller/actions/update-product/update-product.mjs @@ -0,0 +1,79 @@ +import jumpseller from "../../jumpseller.app.mjs"; +import lodash from "lodash"; + +export default { + key: "jumpseller-update-product", + name: "Update Product", + description: "Update an existing product in Jumpseller. [See the documentation](https://jumpseller.com/support/api/#tag/Products/paths/~1products~1%7Bid%7D.json/put)", + version: "0.0.1", + type: "action", + props: { + jumpseller, + productId: { + propDefinition: [ + jumpseller, + "productId", + ], + }, + name: { + propDefinition: [ + jumpseller, + "name", + ], + optional: true, + }, + price: { + propDefinition: [ + jumpseller, + "price", + ], + optional: true, + }, + description: { + propDefinition: [ + jumpseller, + "description", + ], + }, + stock: { + propDefinition: [ + jumpseller, + "stock", + ], + }, + sku: { + propDefinition: [ + jumpseller, + "sku", + ], + }, + status: { + propDefinition: [ + jumpseller, + "status", + ], + }, + }, + async run({ $ }) { + const { product } = await this.jumpseller.updateProduct({ + productId: this.productId, + data: { + product: lodash.pickBy({ + name: this.name, + price: +this.price, + description: this.description, + stock: this.stock, + sku: this.sku, + status: this.status, + }), + }, + $, + }); + + if (product?.id) { + $.export("$summary", `Successfully updated product with ID ${product.id}.`); + } + + return product; + }, +}; diff --git a/components/jumpseller/app/jumpseller.app.ts b/components/jumpseller/app/jumpseller.app.ts deleted file mode 100644 index acddc34d24698..0000000000000 --- a/components/jumpseller/app/jumpseller.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "jumpseller", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/jumpseller/jumpseller.app.mjs b/components/jumpseller/jumpseller.app.mjs new file mode 100644 index 0000000000000..86cfa6e47b799 --- /dev/null +++ b/components/jumpseller/jumpseller.app.mjs @@ -0,0 +1,132 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "jumpseller", + propDefinitions: { + productId: { + type: "string", + label: "Product", + description: "Identifier of a product", + async options({ page }) { + const products = await this.listProducts({ + params: { + page: page + 1, + }, + }); + return products?.map(({ product }) => ({ + value: product.id, + label: product.name, + })) || []; + }, + }, + name: { + type: "string", + label: "Name", + description: "The name of the product.", + }, + price: { + type: "string", + label: "Price", + description: "The price of the product.", + }, + description: { + type: "string", + label: "Description", + description: "The description of the product.", + optional: true, + }, + stock: { + type: "integer", + label: "Stock", + description: "Quantity in stock for the product.", + optional: true, + }, + sku: { + type: "integer", + label: "SKU", + description: "Stock Keeping Unit of the product.", + optional: true, + }, + status: { + type: "string", + label: "Status", + description: "Status of the product", + options: [ + "available", + "not-available", + "disabled", + ], + optional: true, + }, + }, + methods: { + _baseUrl() { + return "https://api.jumpseller.com/v1"; + }, + _headers() { + return { + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + "Content-Type": "application/json", + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + createWebhook(args = {}) { + return this._makeRequest({ + path: "/hooks.json", + method: "POST", + ...args, + }); + }, + deleteWebhook({ + hookId, ...args + }) { + return this._makeRequest({ + path: `/hooks/${hookId}.json`, + method: "DELETE", + ...args, + }); + }, + listProducts(args = {}) { + return this._makeRequest({ + path: "/products.json", + ...args, + }); + }, + createProduct(args = {}) { + return this._makeRequest({ + path: "/products.json", + method: "POST", + ...args, + }); + }, + createProductVariant({ + productId, ...args + }) { + return this._makeRequest({ + path: `/products/${productId}/variants.json`, + method: "POST", + ...args, + }); + }, + updateProduct({ + productId, ...args + }) { + return this._makeRequest({ + path: `/products/${productId}.json`, + method: "PUT", + ...args, + }); + }, + }, +}; diff --git a/components/jumpseller/package.json b/components/jumpseller/package.json index 8b89280c24cab..c88de7b4d30d4 100644 --- a/components/jumpseller/package.json +++ b/components/jumpseller/package.json @@ -1,16 +1,19 @@ { "name": "@pipedream/jumpseller", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Jumpseller Components", - "main": "dist/app/jumpseller.app.mjs", + "main": "jumpseller.app.mjs", "keywords": [ "pipedream", "jumpseller" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/jumpseller", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "lodash": "^4.17.21" } -} \ No newline at end of file +} diff --git a/components/jumpseller/sources/common/base.mjs b/components/jumpseller/sources/common/base.mjs new file mode 100644 index 0000000000000..a9cba36d28c8e --- /dev/null +++ b/components/jumpseller/sources/common/base.mjs @@ -0,0 +1,52 @@ +import jumpseller from "../../jumpseller.app.mjs"; + +export default { + props: { + jumpseller, + db: "$.service.db", + http: "$.interface.http", + }, + hooks: { + async activate() { + const { hook } = await this.jumpseller.createWebhook({ + data: { + hook: { + url: this.http.endpoint, + event: this.getEvent(), + }, + }, + }); + this._setHookId(hook.id); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.jumpseller.deleteWebhook({ + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + getEvent() { + throw new Error("getEvent is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run(event) { + const { body } = event; + if (!body?.order) { + return; + } + const meta = this.generateMeta(body.order); + this.$emit(body.order, meta); + }, +}; diff --git a/components/jumpseller/sources/order-paid/order-paid.mjs b/components/jumpseller/sources/order-paid/order-paid.mjs new file mode 100644 index 0000000000000..547a6095b8041 --- /dev/null +++ b/components/jumpseller/sources/order-paid/order-paid.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "jumpseller-order-paid", + name: "Order Paid", + description: "Emit new event when an order is paid in Jumpseller. [See the documentation](https://jumpseller.com/support/api/#tag/Hooks/paths/~1hooks.json/post)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvent() { + return "order_paid"; + }, + generateMeta(order) { + return { + id: order.id, + summary: `Order Paid ID ${order.id}`, + ts: Date.now(), + }; + }, + }, +}; diff --git a/components/jumpseller/sources/order-updated/order-updated.mjs b/components/jumpseller/sources/order-updated/order-updated.mjs new file mode 100644 index 0000000000000..8c27166c17e36 --- /dev/null +++ b/components/jumpseller/sources/order-updated/order-updated.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "jumpseller-order-updated", + name: "Order Updated", + description: "Emit new event when an order is updaetd in Jumpseller. [See the documentation](https://jumpseller.com/support/api/#tag/Hooks/paths/~1hooks.json/post)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvent() { + return "order_updated"; + }, + generateMeta(order) { + return { + id: `${order.id}-${Date.now()}`, + summary: `Order Updated ID ${order.id}`, + ts: Date.now(), + }; + }, + }, +}; diff --git a/components/kickbox/kickbox.app.mjs b/components/kickbox/kickbox.app.mjs new file mode 100644 index 0000000000000..752e5f0afc3ff --- /dev/null +++ b/components/kickbox/kickbox.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "kickbox", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/kickbox/package.json b/components/kickbox/package.json new file mode 100644 index 0000000000000..418d30b918130 --- /dev/null +++ b/components/kickbox/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/kickbox", + "version": "0.0.1", + "description": "Pipedream Kickbox Components", + "main": "kickbox.app.mjs", + "keywords": [ + "pipedream", + "kickbox" + ], + "homepage": "https://pipedream.com/apps/kickbox", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/lightspeed_retail_pos/lightspeed_retail_pos.app.mjs b/components/lightspeed_retail_pos/lightspeed_retail_pos.app.mjs new file mode 100644 index 0000000000000..14d4e2049939a --- /dev/null +++ b/components/lightspeed_retail_pos/lightspeed_retail_pos.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "lightspeed_retail_pos", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/lightspeed_retail_pos/package.json b/components/lightspeed_retail_pos/package.json new file mode 100644 index 0000000000000..ba91002fbb9b9 --- /dev/null +++ b/components/lightspeed_retail_pos/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/lightspeed_retail_pos", + "version": "0.0.1", + "description": "Pipedream Lightspeed Retail POS Components", + "main": "lightspeed_retail_pos.app.mjs", + "keywords": [ + "pipedream", + "lightspeed_retail_pos" + ], + "homepage": "https://pipedream.com/apps/lightspeed_retail_pos", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/lightspeed_vt/lightspeed_vt.app.mjs b/components/lightspeed_vt/lightspeed_vt.app.mjs new file mode 100644 index 0000000000000..c3159f0167e60 --- /dev/null +++ b/components/lightspeed_vt/lightspeed_vt.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "lightspeed_vt", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/lightspeed_vt/package.json b/components/lightspeed_vt/package.json new file mode 100644 index 0000000000000..39320145ef85e --- /dev/null +++ b/components/lightspeed_vt/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/lightspeed_vt", + "version": "0.0.1", + "description": "Pipedream LightSpeed VT Components", + "main": "lightspeed_vt.app.mjs", + "keywords": [ + "pipedream", + "lightspeed_vt" + ], + "homepage": "https://pipedream.com/apps/lightspeed_vt", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/listmonk/actions/create-campaign/create-campaign.mjs b/components/listmonk/actions/create-campaign/create-campaign.mjs new file mode 100644 index 0000000000000..1b9f6911fa619 --- /dev/null +++ b/components/listmonk/actions/create-campaign/create-campaign.mjs @@ -0,0 +1,93 @@ +import listmonk from "../../listmonk.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "listmonk-create-campaign", + name: "Create Campaign", + description: "Creates a new campaign in Listmonk. [See the documentation](https://listmonk.app/docs/apis/campaigns/#post-apicampaigns)", + version: "0.0.1", + type: "action", + props: { + listmonk, + name: { + type: "string", + label: "Campaign Name", + description: "The name of the campaign to create.", + }, + subject: { + type: "string", + label: "Subject", + description: "The subject of the campaign.", + }, + listIds: { + propDefinition: [ + listmonk, + "listIds", + ], + }, + type: { + type: "string", + label: "Type", + description: "The type of the campaign.", + options: constants.CAMPAIGN_TYPE_OPTIONS, + }, + contentType: { + type: "string", + label: "Content Type", + description: "The content type of the campaign.", + options: constants.CONTENT_TYPE_OPTIONS, + }, + body: { + type: "string", + label: "Body", + description: "The body of the campaign.", + }, + fromEmail: { + type: "string", + label: "From Email", + description: "From e-mail to show on the campaign e-mails. If left empty, the default value from settings is used.", + optional: true, + }, + tags: { + type: "string[]", + label: "Tags", + description: "Array of string tags to mark the campaign.", + optional: true, + }, + sendAt: { + type: "string", + label: "Send At", + description: "A timestamp to schedule the campaign at. Eg: 2021-12-25T06:00:00 (YYYY-MM-DDTHH:MM:SS)", + optional: true, + }, + templateId: { + propDefinition: [ + listmonk, + "templateId", + ], + }, + }, + async run({ $ }) { + const { data } = await this.listmonk.createCampaign({ + data: { + name: this.name, + subject: this.subject, + lists: this.listIds, + from_email: this.fromEmail, + type: this.type, + content_type: this.contentType, + body: this.body, + tags: this.tags, + send_at: this.sendAt, + template_id: this.templateId, + }, + $, + }); + + if (data?.id) { + $.export("$summary", `Successfully created campaign with ID ${data.id}.`); + } + + return data; + }, +}; diff --git a/components/listmonk/actions/create-list/create-list.mjs b/components/listmonk/actions/create-list/create-list.mjs new file mode 100644 index 0000000000000..52fd34fae1f6e --- /dev/null +++ b/components/listmonk/actions/create-list/create-list.mjs @@ -0,0 +1,53 @@ +import listmonk from "../../listmonk.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "listmonk-create-list", + name: "Create List", + description: "Creates a new list in Listmonk. [See the documentation](https://listmonk.app/docs/apis/lists/#post-apilists)", + version: "0.0.1", + type: "action", + props: { + listmonk, + name: { + type: "string", + label: "List Name", + description: "The name of the list to create.", + }, + type: { + type: "string", + label: "Type", + description: "The type of the list to create.", + options: constants.LIST_TYPE_OPTIONS, + }, + optin: { + type: "string", + label: "Optin", + description: "The type of optin for the list.", + options: constants.OPTIN_OPTIONS, + }, + tags: { + type: "string[]", + label: "Tags", + description: "The tags associated with the list.", + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.listmonk.createList({ + data: { + name: this.name, + type: this.type, + optin: this.optin, + tags: this.tags, + }, + $, + }); + + if (data?.id) { + $.export("$summary", `Successfully created list with ID ${data.id}.`); + } + + return data; + }, +}; diff --git a/components/listmonk/actions/create-subscriber/create-subscriber.mjs b/components/listmonk/actions/create-subscriber/create-subscriber.mjs new file mode 100644 index 0000000000000..0993e7200c6eb --- /dev/null +++ b/components/listmonk/actions/create-subscriber/create-subscriber.mjs @@ -0,0 +1,60 @@ +import listmonk from "../../listmonk.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "listmonk-create-subscriber", + name: "Create Subscriber", + description: "Create a new subscriber in Listmonk. [See the documentation](https://listmonk.app/docs/apis/subscribers/#post-apisubscribers)", + version: "0.0.1", + type: "action", + props: { + listmonk, + email: { + type: "string", + label: "Email", + description: "The email of the subscriber to create.", + }, + name: { + type: "string", + label: "Name", + description: "The name of the subscriber to create.", + }, + status: { + type: "string", + label: "Status", + description: "The status of the new subscriber.", + options: constants.STATUS_OPTIONS, + }, + listIds: { + propDefinition: [ + listmonk, + "listIds", + ], + optional: true, + }, + preconfirmSubscription: { + type: "boolean", + label: "Preconfirm Subscription", + description: "If `true`, marks subscription as `confirmed` and no-optin e-mails are sent for double opt-in lists.", + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.listmonk.createSubscriber({ + data: { + email: this.email, + name: this.name, + status: this.status, + lists: this.listIds, + preconfirm_subscriptions: this.preconfirmSubscription, + }, + $, + }); + + if (data?.id) { + $.export("$summary", `Successfully created subscriber with ID ${data.id}.`); + } + + return data; + }, +}; diff --git a/components/listmonk/common/constants.mjs b/components/listmonk/common/constants.mjs new file mode 100644 index 0000000000000..71c3d3544cbf0 --- /dev/null +++ b/components/listmonk/common/constants.mjs @@ -0,0 +1,35 @@ +const STATUS_OPTIONS = [ + "enabled", + "disabled", + "blocklisted", +]; + +const LIST_TYPE_OPTIONS = [ + "private", + "public", +]; + +const OPTIN_OPTIONS = [ + "single", + "double", +]; + +const CAMPAIGN_TYPE_OPTIONS = [ + "regular", + "optin", +]; + +const CONTENT_TYPE_OPTIONS = [ + "richtext", + "html", + "markdown", + "plain", +]; + +export default { + STATUS_OPTIONS, + LIST_TYPE_OPTIONS, + OPTIN_OPTIONS, + CAMPAIGN_TYPE_OPTIONS, + CONTENT_TYPE_OPTIONS, +}; diff --git a/components/listmonk/listmonk.app.mjs b/components/listmonk/listmonk.app.mjs index 1a78085290330..81f36d619a527 100644 --- a/components/listmonk/listmonk.app.mjs +++ b/components/listmonk/listmonk.app.mjs @@ -1,11 +1,102 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "listmonk", - propDefinitions: {}, + propDefinitions: { + listIds: { + type: "string[]", + label: "Lists", + description: "Array of list IDs to subscribe to (marked as unconfirmed by default).", + async options({ page }) { + const { data: { results } } = await this.listLists({ + params: { + page: page + 1, + }, + }); + return results?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + templateId: { + type: "string", + label: "Template", + description: "ID of the template to use. If left empty, the default template is used.", + optional: true, + async options() { + const { data } = await this.listTemplates(); + return data?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return `https://${this.$auth.url}/api`; + }, + _getAuth() { + return { + username: `${this.$auth.username}`, + password: `${this.$auth.password}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + auth: this._getAuth(), + ...args, + }); + }, + listSubscribers(args = {}) { + return this._makeRequest({ + path: "/subscribers", + ...args, + }); + }, + listLists(args = {}) { + return this._makeRequest({ + path: "/lists", + ...args, + }); + }, + listTemplates(args = {}) { + return this._makeRequest({ + path: "/templates", + ...args, + }); + }, + createSubscriber(args = {}) { + return this._makeRequest({ + path: "/subscribers", + method: "POST", + ...args, + }); + }, + createList(args = {}) { + return this._makeRequest({ + path: "/lists", + method: "POST", + ...args, + }); + }, + createCampaign(args = {}) { + return this._makeRequest({ + path: "/campaigns", + method: "POST", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/listmonk/package.json b/components/listmonk/package.json index bf203ccd8aa05..a0b2e733bc319 100644 --- a/components/listmonk/package.json +++ b/components/listmonk/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/listmonk", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream listmonk Components", "main": "listmonk.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/listmonk/sources/common/base.mjs b/components/listmonk/sources/common/base.mjs new file mode 100644 index 0000000000000..a24f47764f9e8 --- /dev/null +++ b/components/listmonk/sources/common/base.mjs @@ -0,0 +1,72 @@ +import listmonk from "../../listmonk.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + listmonk, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getResourceFn() { + throw new Error("getResourceFn is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + async *paginate({ + resourceFn, + args = {}, + }) { + args = { + ...args, + params: { + ...args.params, + page: 1, + per_page: 100, + }, + }; + let total = 0; + do { + const { data: { results } } = await resourceFn(args); + for (const item of results) { + yield item; + } + args.params.page++; + total = results?.length; + } while (total === args.params.per_page); + }, + }, + async run() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + + const items = this.paginate({ + resourceFn: this.getResourceFn(), + }); + + for await (const item of items) { + const ts = Date.parse(item.created_at); + if (ts > lastTs) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + if (ts > maxTs) { + maxTs = ts; + } + } + } + + this._setLastTs(maxTs); + }, +}; diff --git a/components/listmonk/sources/new-subscriber-added/new-subscriber-added.mjs b/components/listmonk/sources/new-subscriber-added/new-subscriber-added.mjs new file mode 100644 index 0000000000000..1e81aaf5e47c7 --- /dev/null +++ b/components/listmonk/sources/new-subscriber-added/new-subscriber-added.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "listmonk-new-subscriber-added", + name: "New Subscriber Added", + description: "Emit new event when a new subscriber is added. [See the documentation](https://listmonk.app/docs/apis/subscribers/#get-apisubscribers_2)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.listmonk.listSubscribers; + }, + generateMeta(subscriber) { + return { + id: subscriber.id, + summary: `New Subscriber ${subscriber.name}`, + ts: Date.parse(subscriber.created_at), + }; + }, + }, +}; diff --git a/components/mamo_business/actions/create-payment-link/create-payment-link.mjs b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs new file mode 100644 index 0000000000000..2a51d40bfe50e --- /dev/null +++ b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs @@ -0,0 +1,251 @@ +import { parseObject } from "../../common/utils.mjs"; +import app from "../../mamo_business.app.mjs"; + +export default { + key: "mamo_business-create-payment-link", + name: "Create Payment Link", + version: "0.0.1", + description: "Generate a vanilla or subscription payment link. [See the documentation](https://mamopay.readme.io/reference/post_links)", + type: "action", + props: { + app, + title: { + type: "string", + label: "Title", + description: "The title of the payment link.", + }, + description: { + type: "string", + label: "Description", + description: "Payment description. This will appear on the payment checkout page.", + optional: true, + }, + capacity: { + type: "integer", + label: "Capacity", + description: "The capacity will be ignored when the subscription params exist and value will be null.", + optional: true, + }, + active: { + type: "boolean", + label: "Active", + description: "Whether the payment is active or not.", + optional: true, + }, + returnUrl: { + type: "string", + label: "Return URL", + description: "The URL which the customer will be redirected to after a successful payment.", + optional: true, + }, + failureReturnUrl: { + type: "string", + label: "Failure Return URL", + description: "The URL which the customer will be redirected to after a failure payment.", + optional: true, + }, + processingFeePercentage: { + type: "integer", + label: "Processing Fee Percentage", + description: "The percentage of the transaction that is the fee.", + optional: true, + }, + amount: { + type: "string", + label: "Amount", + description: "The value number of the payment.", + optional: true, + }, + amountCurrency: { + type: "string", + label: "Amount Currency", + description: "The currency that the transaction will be charged.", + default: "AED", + options: [ + "AED", + "USD", + "EUR", + "GBP", + "SAR", + ], + }, + isWidget: { + type: "boolean", + label: "Is Widget", + description: "Generate widget payment link.", + optional: true, + }, + enableTabby: { + type: "boolean", + label: "Enable Tabby", + description: "Enables the ability for customers to buy now and pay later.", + optional: true, + }, + enableMessage: { + type: "boolean", + label: "Enable Message", + description: "Enables the ability for customers to add a message during the checkout process.", + optional: true, + }, + enableTips: { + type: "boolean", + label: "Enable Tips", + description: "Enables the tips option. This will be displayed on the first screen.", + optional: true, + }, + enableCustomerDetails: { + type: "boolean", + label: "Enable Customer Details", + description: "Enables adding customer details such as the name, email, and phone number. This screen will be displayed before the payment details screen.", + optional: true, + }, + enableQuantity: { + type: "boolean", + label: "Enable Quantity", + description: "Enable the payment link to be used multiple times.", + optional: true, + }, + enableQrCode: { + type: "boolean", + label: "Enable QR Code", + description: "Adds the ability to verify a payment through a QR code.", + optional: true, + }, + sendCustomerReceipt: { + type: "boolean", + label: "Send Customer Receipt", + description: "Enables the sending of customer receipts.", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of customer which will pre-populate in card info step.", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of customer which will pre-populate in card info step.", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The email of customer which will pre-populate in card info step.", + optional: true, + }, + externalId: { + type: "string", + label: "External Id", + description: "The external ID of your choice to associate with payments captured by this payment link.", + optional: true, + }, + customData: { + type: "object", + label: "Custom Data", + description: "An object with custom data of the payment link.", + optional: true, + }, + isRecurring: { + type: "boolean", + label: "Is Recurring", + description: "Whether this payment link is for a recurring payment.", + reloadProps: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.isRecurring) { + props.frequency = { + type: "string", + label: "Frequency", + description: "Defines the interval that this subscription will be run on.", + options: [ + "weekly", + "monthly", + "annually", + ], + }; + props.frequencyInterval = { + type: "integer", + label: "Frequency Interval", + description: "Defines how often this subscription will run. This will be based on the frequency property defined above.", + }; + props.endDate = { + type: "string", + label: "End Date", + description: "The last date this subscription could run on. Format: YYYY/MM/DD", + }; + props.paymentQuantity = { + type: "string", + label: "Payment Quantity", + description: "Number of times this subscription will occur. If end_date defined, end_date takes precedence.", + }; + } + return props; + }, + async run({ $ }) { + const { + app, + returnUrl, + failureReturnUrl, + processingFeePercentage, + amountCurrency, + isWidget, + enableTabby, + enableMessage, + enableTips, + enableCustomerDetails, + enableQuantity, + enableQrCode, + sendCustomerReceipt, + firstName, + lastName, + externalId, + customData, + isRecurring, + frequency, + frequencyInterval, + endDate, + paymentQuantity, + ...data + } = this; + + const obj = { + return_url: returnUrl, + failure_return_url: failureReturnUrl, + processing_fee_percentage: processingFeePercentage, + amount_currency: amountCurrency, + is_widget: isWidget, + enable_tabby: enableTabby, + enable_message: enableMessage, + enable_tips: enableTips, + enable_customer_details: enableCustomerDetails, + enable_quantity: enableQuantity, + enable_qr_code: enableQrCode, + send_customer_receipt: sendCustomerReceipt, + first_name: firstName, + last_name: lastName, + external_id: externalId, + custom_data: customData && parseObject(customData), + ...data, + }; + if (isRecurring) { + obj.subscription = { + frequency, + frequency_interval: frequencyInterval, + end_ate: endDate, + payment_quantity: paymentQuantity, + }; + } + + const response = await app.createPaymentLink({ + $, + data: obj, + }); + + $.export("$summary", `A new payment link with Id: ${response.id} was successfully created!`); + return response; + }, +}; diff --git a/components/mamo_business/common/utils.mjs b/components/mamo_business/common/utils.mjs new file mode 100644 index 0000000000000..9f49a5c4c8c2f --- /dev/null +++ b/components/mamo_business/common/utils.mjs @@ -0,0 +1,6 @@ +export const parseObject = (obj) => { + if (typeof obj != "object") { + return JSON.parse(obj); + } + return obj; +}; diff --git a/components/mamo_business/mamo_business.app.mjs b/components/mamo_business/mamo_business.app.mjs index fbbfa91e64876..db2295c720698 100644 --- a/components/mamo_business/mamo_business.app.mjs +++ b/components/mamo_business/mamo_business.app.mjs @@ -1,11 +1,47 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "mamo_business", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiUrl() { + return `https://${this.$auth.environment}.mamopay.com/manage_api/v1`; + }, + _getHeaders() { + return { + "Authorization": `Bearer ${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + headers: this._getHeaders(), + ...opts, + }; + + return axios($, config); + }, + createHook(args = {}) { + return this._makeRequest({ + method: "POST", + path: "webhooks", + ...args, + }); + }, + createPaymentLink(args = {}) { + return this._makeRequest({ + method: "POST", + path: "links", + ...args, + }); + }, + deleteHook(hookId) { + return this._makeRequest({ + method: "DELETE", + path: `webhooks/${hookId}`, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/mamo_business/package.json b/components/mamo_business/package.json index 55ce3eeb94015..f3dfd7fd2c978 100644 --- a/components/mamo_business/package.json +++ b/components/mamo_business/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/mamo_business", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Mamo Business Components", "main": "mamo_business.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/mamo_business/sources/common/base.mjs b/components/mamo_business/sources/common/base.mjs new file mode 100644 index 0000000000000..678bfa43679f0 --- /dev/null +++ b/components/mamo_business/sources/common/base.mjs @@ -0,0 +1,56 @@ +import app from "../../mamo_business.app.mjs"; + +export default { + dedupe: "unique", + props: { + app, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + }, + hooks: { + async activate() { + const data = await this.app.createHook({ + data: { + url: this.http.endpoint, + enabled_events: this.getEvent(), + }, + }); + + this._setHookId(data.id); + }, + async deactivate() { + const id = this._getHookId("hookId"); + await this.app.deleteHook(id); + }, + }, + methods: { + emitEvent(body) { + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta(body) { + return { + id: `${body.id}${body.created_date}`, + summary: this.getSummary(body), + ts: new Date(), + }; + }, + }, + async run({ body }) { + if (body.ping) { + return this.http.respond({ + status: 200, + }); + } + this.emitEvent(body); + }, +}; diff --git a/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs b/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs new file mode 100644 index 0000000000000..057811449f02c --- /dev/null +++ b/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs @@ -0,0 +1,25 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "mamo_business-new-failed-payment", + name: "New Failed Payment (Instant)", + description: "Emit new event when a payment is failed.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "charge.failed", + "subscription.failed", + ]; + }, + getSummary(body) { + const { id } = body; + return `A new payment with id ${id} has been failed!`; + }, + }, + sampleEmit, +}; diff --git a/components/mamo_business/sources/new-failed-payment/test-event.mjs b/components/mamo_business/sources/new-failed-payment/test-event.mjs new file mode 100644 index 0000000000000..2590c8406b457 --- /dev/null +++ b/components/mamo_business/sources/new-failed-payment/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "status": "failed", + "id": "MPB-CHRG-E0CE93E071", + "amount": 10, + "refund_amount": 0, + "refund_status": "No refund", + "created_date": "2023-05-31-11-18-57", + "subscription_id": "MPB-SUB-B764EDCBA2", + "settlement_amount": 356.42, + "settlement_currency": "AED", + "settlement_date": "05/06/2023" +}; diff --git a/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs b/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs new file mode 100644 index 0000000000000..394fe989df968 --- /dev/null +++ b/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "mamo_business-new-successful-payment", + name: "New Successful Payment (Instant)", + description: "Emit new event when a payment is charged.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "charge.succeeded", + ]; + }, + getSummary(body) { + const { id } = body; + return `A new payment with id ${id} was successfully charged!`; + }, + }, + sampleEmit, +}; diff --git a/components/mamo_business/sources/new-successful-payment/test-event.mjs b/components/mamo_business/sources/new-successful-payment/test-event.mjs new file mode 100644 index 0000000000000..be9d0e596d558 --- /dev/null +++ b/components/mamo_business/sources/new-successful-payment/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "status": "captured", + "id": "MPB-CHRG-E0CE93E071", + "amount": 10, + "refund_amount": 0, + "refund_status": "No refund", + "created_date": "2023-05-31-11-18-57", + "subscription_id": "MPB-SUB-B764EDCBA2", + "settlement_amount": 356.42, + "settlement_currency": "AED", + "settlement_date": "05/06/2023" +}; diff --git a/components/meetingpulse/meetingpulse.app.mjs b/components/meetingpulse/meetingpulse.app.mjs new file mode 100644 index 0000000000000..288c960888cf8 --- /dev/null +++ b/components/meetingpulse/meetingpulse.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "meetingpulse", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/meetingpulse/package.json b/components/meetingpulse/package.json new file mode 100644 index 0000000000000..37c135bbb6809 --- /dev/null +++ b/components/meetingpulse/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/meetingpulse", + "version": "0.0.1", + "description": "Pipedream MeetingPulse Components", + "main": "meetingpulse.app.mjs", + "keywords": [ + "pipedream", + "meetingpulse" + ], + "homepage": "https://pipedream.com/apps/meetingpulse", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/microsoft_onedrive/actions/create-folder/create-folder.mjs b/components/microsoft_onedrive/actions/create-folder/create-folder.mjs new file mode 100644 index 0000000000000..b79ff6c461b58 --- /dev/null +++ b/components/microsoft_onedrive/actions/create-folder/create-folder.mjs @@ -0,0 +1,91 @@ +import onedrive from "../../microsoft_onedrive.app.mjs"; +import httpRequest from "../../common/httpRequest.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + name: "Create Folder", + description: "Create a new folder in a drive. [See the documentation](https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_post_children?view=odsp-graph-online)", + key: "microsoft_onedrive-create-folder", + version: "0.0.1", + type: "action", + props: { + onedrive, + parentFolderId: { + propDefinition: [ + onedrive, + "folder", + ], + label: "Parent Folder ID", + description: "The ID of the folder which the the new folder should be created.", + optional: true, + }, + sharedFolderReference: { + type: "string", + label: "Shared Folder Reference", + description: "The reference of the shared folder which the the new folder should be created.\n\nE.g. `/drives/{driveId}/items/{folderId}/children`", + optional: true, + async options() { + const { value } = await this.httpRequest({ + url: "/sharedWithMe", + }); + return value.map((shared) => ({ + label: shared.name, + value: `/drives/${shared.remoteItem.parentReference.driveId}/items/${shared.remoteItem.id}/children`, + })); + }, + }, + folderName: { + type: "string", + label: "Folder Name", + description: "The name of the new folder to be created. e.g. `New Folder`", + }, + }, + methods: { + httpRequest, + createFolder({ + folderName, parentFolderId, sharedFolderReference, + }) { + let url = "/root/children"; + if (parentFolderId) { + url = `/items/${parentFolderId}/children`; + } + if (sharedFolderReference) { + url = sharedFolderReference; + } + return this.httpRequest({ + url, + useSharedDrive: !!sharedFolderReference, + headers: { + "Content-Type": "application/json", + }, + data: { + name: folderName, + folder: {}, + ["@microsoft.graph.conflictBehavior"]: "rename", + }, + method: "POST", + }); + }, + }, + async run({ $ }) { + const { + folderName, parentFolderId, sharedFolderReference, + } = this; + + if (sharedFolderReference && parentFolderId) { + throw new ConfigurationError("You have to select either a `Parent Folder` or a `Shared Folder`."); + } + + const response = await this.createFolder({ + folderName, + parentFolderId, + sharedFolderReference, + }); + + if (response?.id) { + $.export("$summary", `Successfully created folder with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/microsoft_onedrive/actions/download-file/download-file.mjs b/components/microsoft_onedrive/actions/download-file/download-file.mjs index 4f867f534e921..473dd246626ef 100644 --- a/components/microsoft_onedrive/actions/download-file/download-file.mjs +++ b/components/microsoft_onedrive/actions/download-file/download-file.mjs @@ -9,7 +9,7 @@ export default { name: "Download File", description: "Download a file stored in OneDrive. [See the documentation](https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_get_content?view=odsp-graph-online)", key: "microsoft_onedrive-download-file", - version: "0.0.3", + version: "0.0.4", type: "action", props: { onedrive, diff --git a/components/microsoft_onedrive/actions/get-excel-table/get-excel-table.mjs b/components/microsoft_onedrive/actions/get-excel-table/get-excel-table.mjs index d4ebeeb2325de..88200dd8c38f3 100644 --- a/components/microsoft_onedrive/actions/get-excel-table/get-excel-table.mjs +++ b/components/microsoft_onedrive/actions/get-excel-table/get-excel-table.mjs @@ -5,7 +5,7 @@ export default { name: "Get Table", description: "Retrieve a table from an Excel spreadsheet stored in OneDrive [See the documentation](https://learn.microsoft.com/en-us/graph/api/table-range?view=graph-rest-1.0&tabs=http)", key: "microsoft_onedrive-get-excel-table", - version: "0.0.2", + version: "0.0.3", type: "action", props: { onedrive, diff --git a/components/microsoft_onedrive/actions/upload-file/upload-file.mjs b/components/microsoft_onedrive/actions/upload-file/upload-file.mjs index 528c2f12a2cbb..35f0bd47d07c9 100644 --- a/components/microsoft_onedrive/actions/upload-file/upload-file.mjs +++ b/components/microsoft_onedrive/actions/upload-file/upload-file.mjs @@ -8,7 +8,7 @@ export default { name: "Upload File", description: "Upload a file to OneDrive. [See the documentation](https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_put_content?view=odsp-graph-online)", key: "microsoft_onedrive-upload-file", - version: "0.0.1", + version: "0.0.3", type: "action", props: { onedrive, @@ -70,7 +70,7 @@ export default { }); if (response?.id) { - $.export("$summary", `Succressfully uploaded file with ID ${response.id}.`); + $.export("$summary", `Successfully uploaded file with ID ${response.id}.`); } return response; diff --git a/components/microsoft_onedrive/common/httpRequest.mjs b/components/microsoft_onedrive/common/httpRequest.mjs index 4ad15ecc942f4..a157fb0647125 100644 --- a/components/microsoft_onedrive/common/httpRequest.mjs +++ b/components/microsoft_onedrive/common/httpRequest.mjs @@ -3,9 +3,13 @@ import { axios } from "@pipedream/platform"; export default function ({ $, ...args }) { + let baseURL = "https://graph.microsoft.com/v1.0/me/drive"; + if (args.useSharedDrive) { + baseURL = "https://graph.microsoft.com/v1.0"; + } return axios($, { ...args, - baseURL: "https://graph.microsoft.com/v1.0/me/drive", + baseURL, headers: { ...args.headers, Authorization: `Bearer ${this.onedrive.$auth.oauth_access_token}`, diff --git a/components/microsoft_onedrive/package.json b/components/microsoft_onedrive/package.json index 6bb3f9132e8e3..44fd1ee997ea3 100644 --- a/components/microsoft_onedrive/package.json +++ b/components/microsoft_onedrive/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/microsoft_onedrive", - "version": "1.2.4", + "version": "1.3.0", "description": "Pipedream Microsoft OneDrive components", "main": "microsoft_onedrive.app.js", "homepage": "https://pipedream.com/apps/microsoft-onedrive", diff --git a/components/miro_custom_app/package.json b/components/miro_custom_app/package.json index 7f8bd4964db0a..a166097e3c177 100644 --- a/components/miro_custom_app/package.json +++ b/components/miro_custom_app/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/miro_custom_app", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream Miro Developer App Components", - "main": "dist/app/miro_custom_app.app.mjs", + "main": "miro_custom_app.app.mjs", "keywords": [ "pipedream", "miro_custom_app" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/miro_custom_app", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } } diff --git a/components/miro_custom_app/sources/common/base.mjs b/components/miro_custom_app/sources/common/base.mjs new file mode 100644 index 0000000000000..dda2ca772ce60 --- /dev/null +++ b/components/miro_custom_app/sources/common/base.mjs @@ -0,0 +1,42 @@ +import miro from "../../miro_custom_app.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + miro, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + async *paginate({ + resourceFn, args = {}, + }) { + args = { + ...args, + params: { + ...args.params, + limit: 50, + }, + }; + let total = 0; + do { + const { + data, cursor, + } = await resourceFn(args); + for (const item of data) { + yield item; + } + total = data?.length; + args.params.cursor = cursor; + } while (total === args.params.limit); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, +}; diff --git a/components/miro_custom_app/sources/item-position-changed/item-position-changed.mjs b/components/miro_custom_app/sources/item-position-changed/item-position-changed.mjs new file mode 100644 index 0000000000000..278f753abc56b --- /dev/null +++ b/components/miro_custom_app/sources/item-position-changed/item-position-changed.mjs @@ -0,0 +1,66 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "miro_custom_app-item-position-changed", + name: "Item Position Changed", + description: "Emit new event when an item's position changes in a Miro Custom App.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + teamId: { + propDefinition: [ + common.props.miro, + "teamId", + ], + }, + boardId: { + propDefinition: [ + common.props.miro, + "boardId", + ({ teamId }) => ({ + teamId, + }), + ], + }, + }, + methods: { + ...common.methods, + _getPositions() { + return this.db.get("positions") || {}; + }, + _setPositions(positions) { + this.db.set("positions", positions); + }, + generateMeta(item) { + const ts = Date.parse(item.modifiedAt); + return { + id: `${item.id}-${ts}`, + summary: `Item ${item.id} position changed`, + ts, + }; + }, + }, + async run() { + const positions = this._getPositions(); + const newPositions = {}; + const items = this.paginate({ + resourceFn: this.miro.listItems, + args: { + boardId: this.boardId, + }, + }); + for await (const item of items) { + const position = JSON.stringify(item.position); + if (positions[item.id] && positions[item.id] !== position) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + } + newPositions[item.id] = position; + } + + this._setPositions(newPositions); + }, +}; diff --git a/components/mocean_api/actions/get-balance/get-balance.mjs b/components/mocean_api/actions/get-balance/get-balance.mjs new file mode 100644 index 0000000000000..138f55433b9a6 --- /dev/null +++ b/components/mocean_api/actions/get-balance/get-balance.mjs @@ -0,0 +1,23 @@ +import mocean from "../../mocean_api.app.mjs"; + +export default { + key: "mocean_api-get-balance", + name: "Get Balance", + description: "Retrieve your current account balance with Mocean API. [See the documentation](https://moceanapi.com/docs/#get-balance)", + version: "0.0.1", + type: "action", + props: { + mocean, + }, + async run({ $ }) { + const response = await this.mocean.getBalance({ + $, + }); + + if (response) { + $.export("$summary", "Successfully retrieved balance."); + } + + return response; + }, +}; diff --git a/components/mocean_api/actions/send-sms/send-sms.mjs b/components/mocean_api/actions/send-sms/send-sms.mjs new file mode 100644 index 0000000000000..e6a92aeade49d --- /dev/null +++ b/components/mocean_api/actions/send-sms/send-sms.mjs @@ -0,0 +1,43 @@ +import mocean from "../../mocean_api.app.mjs"; + +export default { + key: "mocean_api-send-sms", + name: "Send SMS", + description: "Send an outbound SMS from your Mocean account. [See the documentation](https://moceanapi.com/docs/#send-sms)", + version: "0.0.1", + type: "action", + props: { + mocean, + from: { + type: "string", + label: "From", + description: "The information that is displayed to the recipient as the sender of the SMS when a message is received at a mobile device.", + }, + to: { + type: "string", + label: "To", + description: "Phone number of the receiver. To send to multiple receivers, separate each entry with white space (‘ ’) or comma (,). Phone number must include country code, for example, a Malaysian phone number will be like 60123456789.", + }, + text: { + type: "string", + label: "Text", + description: "Contents of the message, URL encoded as necessary (e.g. Text+message+test%21). If you are sending binary content, this will be a hex string.", + }, + }, + async run({ $ }) { + const response = await this.mocean.sendSMS({ + data: { + "mocean-from": this.from, + "mocean-to": this.to, + "mocean-text": this.text, + }, + $, + }); + + if (response) { + $.export("$summary", "Successfully sent SMS message."); + } + + return response; + }, +}; diff --git a/components/mocean_api/mocean_api.app.mjs b/components/mocean_api/mocean_api.app.mjs index 07de90a7f8166..9702337011b73 100644 --- a/components/mocean_api/mocean_api.app.mjs +++ b/components/mocean_api/mocean_api.app.mjs @@ -1,11 +1,55 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "mocean_api", propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://rest.moceanapi.com/rest/2"; + }, + _authParams(params) { + return { + ...params, + "mocean-api-key": `${this.$auth.api_key}`, + "mocean-api-secret": `${this.$auth.api_secret}`, + }; + }, + _makeRequest({ + $ = this, + path, + params = {}, + data, + ...args + }) { + if (data) { + args = { + ...args, + data: this._authParams(data), + }; + } else { + args = { + ...args, + params: this._authParams(params), + }; + } + return axios($, { + url: `${this._baseUrl()}${path}`, + ...args, + }); + }, + getBalance(args = {}) { + return this._makeRequest({ + path: "/account/balance", + ...args, + }); + }, + sendSMS(args = {}) { + return this._makeRequest({ + path: "/sms", + method: "POST", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/mocean_api/package.json b/components/mocean_api/package.json index efb1011b54e68..cf6c0f506ed5c 100644 --- a/components/mocean_api/package.json +++ b/components/mocean_api/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/mocean_api", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Mocean API Components", "main": "mocean_api.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/mocean_api/sources/balance-updated/balance-updated.mjs b/components/mocean_api/sources/balance-updated/balance-updated.mjs new file mode 100644 index 0000000000000..922c25104710d --- /dev/null +++ b/components/mocean_api/sources/balance-updated/balance-updated.mjs @@ -0,0 +1,35 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "mocean_api-balance-updated", + name: "Balance Updated", + description: "Emit new event when the account balance has been updated. [See the documentation](https://moceanapi.com/docs/#get-balance)", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + _getPreviousBalance() { + return this.db.get("previousBalance"); + }, + _setPreviousBalance(previousBalance) { + this.db.set("previousBalance", previousBalance); + }, + generateMeta(balance) { + return { + id: Date.now(), + summary: `New Balance ${balance.value}`, + ts: Date.now(), + }; + }, + }, + async run() { + const previousBalance = this._getPreviousBalance(); + const balance = await this.mocean.getBalance(); + if (balance.value !== previousBalance) { + const meta = this.generateMeta(balance); + this.$emit(balance, meta); + } + this._setPreviousBalance(balance.value); + }, +}; diff --git a/components/mocean_api/sources/common/base.mjs b/components/mocean_api/sources/common/base.mjs new file mode 100644 index 0000000000000..23f335c3786be --- /dev/null +++ b/components/mocean_api/sources/common/base.mjs @@ -0,0 +1,20 @@ +import mocean from "../../mocean_api.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + mocean, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, +}; diff --git a/components/modeck/.gitignore b/components/modeck/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/modeck/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/modeck/actions/create-render/create-render.mjs b/components/modeck/actions/create-render/create-render.mjs new file mode 100644 index 0000000000000..bec0a6a31bbf4 --- /dev/null +++ b/components/modeck/actions/create-render/create-render.mjs @@ -0,0 +1,63 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseArray } from "../../common/utils.mjs"; +import modeck from "../../modeck.app.mjs"; + +export default { + key: "modeck-create-render", + name: "Create Render", + version: "0.0.1", + description: "Create a new edit with the data supplied. [See the documentation](https://modeck.io/docs#modeckapi)", + type: "action", + props: { + modeck, + deck: { + propDefinition: [ + modeck, + "deck", + ], + }, + name: { + type: "string", + label: "Name", + description: "The name of this edit.", + }, + editId: { + type: "string", + label: "Edit Id", + description: "The identifier of this edit.", + optional: true, + }, + notificationEmail: { + type: "string", + label: "Notification Email", + description: "The email that will be notified on render finishing.", + optional: true, + }, + mogrtSeq: { + type: "string[]", + label: "MogrtSeq", + description: "An array of mogrtSeq objects.", + }, + }, + async run({ $ }) { + const { + modeck, + mogrtSeq, + ...data + } = this; + + const response = await modeck.createRender({ + $, + data: { + ...data, + mogrtSeq: parseArray(mogrtSeq), + }, + }); + + if (!response.success) throw new ConfigurationError(response.info); + + $.export("$summary", `A new edit with Id: ${response.editId} was successfully created!`); + return response; + }, +}; + diff --git a/components/modeck/common/utils.mjs b/components/modeck/common/utils.mjs new file mode 100644 index 0000000000000..dd2b52ebc43b2 --- /dev/null +++ b/components/modeck/common/utils.mjs @@ -0,0 +1,11 @@ +export const parseArray = (array) => { + if (Array.isArray(array)) { + return array.map((item) => { + if (typeof item != "object") { + item = JSON.parse(item); + } + return item; + }); + } + return JSON.parse(array); +}; diff --git a/components/modeck/modeck.app.mjs b/components/modeck/modeck.app.mjs new file mode 100644 index 0000000000000..f8aa7ee5ff76f --- /dev/null +++ b/components/modeck/modeck.app.mjs @@ -0,0 +1,54 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "modeck", + propDefinitions: { + deck: { + type: "string", + label: "Deck Name", + description: "The deck name you want to edit.", + async options() { + const response = await this.listDecks(); + return response.decks?.map(({ name }) => name) || []; + }, + }, + }, + methods: { + _apiUrl() { + return "https://api.modeck.io"; + }, + _makeRequest({ + $ = this, data = {}, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + data: { + apiKey: this.$auth.api_key, + ...data, + }, + ...opts, + }; + return axios($, config); + }, + createRender(args = {}) { + return this._makeRequest({ + method: "POST", + path: "render", + ...args, + }); + }, + listDecks(args = {}) { + return this._makeRequest({ + path: "listdecks", + ...args, + }); + }, + listRenders(args = {}) { + return this._makeRequest({ + path: "listrenders", + ...args, + }); + }, + }, +}; diff --git a/components/modeck/package.json b/components/modeck/package.json index 47c3ac636475a..6b0aea43661c3 100644 --- a/components/modeck/package.json +++ b/components/modeck/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/modeck", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream MoDeck Components", - "main": "dist/app/modeck.app.mjs", + "main": "modeck.app.mjs", "keywords": [ "pipedream", "modeck" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/modeck", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/modeck/sources/render-complete/render-complete.mjs b/components/modeck/sources/render-complete/render-complete.mjs new file mode 100644 index 0000000000000..1594c79067b0b --- /dev/null +++ b/components/modeck/sources/render-complete/render-complete.mjs @@ -0,0 +1,82 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import modeck from "../../modeck.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "modeck-render-complete", + name: "New Render Completed", + version: "0.0.1", + description: "Emit new event when a render is completed.", + type: "source", + dedupe: "unique", + props: { + modeck, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the MoDeck on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + deck: { + propDefinition: [ + modeck, + "deck", + ], + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async startEvent(maxResults = 0) { + const { + modeck, + deck, + } = this; + + const lastDate = this._getLastDate(); + + const { renderList } = await modeck.listRenders({ + data: { + deck, + }, + }); + + let responseArray = []; + let count = 0; + + for (const render of renderList) { + if (new Date(render.data) <= new Date(lastDate)) break; + responseArray.push(render); + if (maxResults && (++count === maxResults)) break; + } + if (responseArray.length) this._setLastDate(responseArray[0].date); + + for (const render of responseArray.reverse()) { + this.$emit( + render, + { + id: render.id, + summary: `The render with id: "${render.id}" has been completed!`, + ts: render.date, + }, + ); + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/modeck/sources/render-complete/test-event.mjs b/components/modeck/sources/render-complete/test-event.mjs new file mode 100644 index 0000000000000..fc83b9cdfa68c --- /dev/null +++ b/components/modeck/sources/render-complete/test-event.mjs @@ -0,0 +1,8 @@ +export default { + "name": "renderName", + "editName": "editName", + "format": "mp4", + "url": "https://modeck-media.s3.us-east-1.amazonaws.com/884c1234567a123456789/editIdTest/render/render.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASDSDFGDFGFGHFGH%2F20231005%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231005T202702Z&X-Amz-Expires=7200&X-Amz-Signature=12345678ac4d411ab6f63dfc5e4e972fb78ee7db2266eecf90387481a82e&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%20%3D%22TEST01.mp4%22&versionId=sdfSFsdsdfgSfWsPpu_GwbhbhHrM86hntl&x-id=GetObject", + "date": "2023-10-05T20:26:15.000Z", + "id": 123456789000 +} \ No newline at end of file diff --git a/components/neetoinvoice/neetoinvoice.app.mjs b/components/neetoinvoice/neetoinvoice.app.mjs new file mode 100644 index 0000000000000..b91e57418caa2 --- /dev/null +++ b/components/neetoinvoice/neetoinvoice.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "neetoinvoice", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/neetoinvoice/package.json b/components/neetoinvoice/package.json new file mode 100644 index 0000000000000..931809c761eb1 --- /dev/null +++ b/components/neetoinvoice/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/neetoinvoice", + "version": "0.0.1", + "description": "Pipedream neetoinvoice Components", + "main": "neetoinvoice.app.mjs", + "keywords": [ + "pipedream", + "neetoinvoice" + ], + "homepage": "https://pipedream.com/apps/neetoinvoice", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/neetokb/neetokb.app.mjs b/components/neetokb/neetokb.app.mjs new file mode 100644 index 0000000000000..f4c9e4e01c647 --- /dev/null +++ b/components/neetokb/neetokb.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "neetokb", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/neetokb/package.json b/components/neetokb/package.json new file mode 100644 index 0000000000000..4f32b697bd870 --- /dev/null +++ b/components/neetokb/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/neetokb", + "version": "0.0.1", + "description": "Pipedream neetoKB Components", + "main": "neetokb.app.mjs", + "keywords": [ + "pipedream", + "neetokb" + ], + "homepage": "https://pipedream.com/apps/neetokb", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/niftykit/actions/create-mint-link/create-mint-link.mjs b/components/niftykit/actions/create-mint-link/create-mint-link.mjs new file mode 100644 index 0000000000000..58ea8c0525027 --- /dev/null +++ b/components/niftykit/actions/create-mint-link/create-mint-link.mjs @@ -0,0 +1,83 @@ +import niftykit from "../../niftykit.app.mjs"; + +export default { + key: "niftykit-create-mint-link", + name: "Create Mint Link", + description: "Creates a mint link in NiftyKit. [See the documentation](https://api.niftykit.com/docs?_gl=1*d8mlfi*_ga*MTY5MTM2MjIwNi4xNjk0MDMzOTk3*_ga_B0DCGWCR37*MTY5NzE0MTUzNy40LjAuMTY5NzE0MTUzNy42MC4wLjA.#/onboarding/OnboardingController_createMintLink)", + version: "0.0.1", + type: "action", + props: { + niftykit, + name: { + type: "string", + label: "Name", + description: "Name of the new mint link", + }, + description: { + type: "string", + label: "Description", + description: "Description of the new mint link", + }, + image: { + type: "string", + label: "Image", + description: "URL of the new mint link image", + }, + maxAmount: { + type: "string", + label: "Max Amount", + description: "Max amount", + }, + maxPerWallet: { + type: "string", + label: "Max Per Wallet", + description: "Max per wallet", + }, + maxPerTx: { + type: "string", + label: "Max Per Tx", + description: "Max per transaction", + }, + price: { + type: "string", + label: "Price", + description: "Whether the NFT is `Free` or `Paid`", + options: [ + "Free", + "Paid", + ], + optional: true, + }, + pricePerNFT: { + type: "string", + label: "Price per NFT", + description: "Price per NFT", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.niftykit.createMintLink({ + data: { + NFTMetadata: { + name: this.name, + description: this.description, + image: this.image, + }, + maxAmount: +this.maxAmount, + maxPerWallet: +this.maxPerWallet, + maxPerTx: +this.maxPerTx, + price: this.price, + pricePerNFT: this.pricePerNFT + ? +this.pricePerNFT + : undefined, + }, + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully created Mint Link with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/niftykit/niftykit.app.mjs b/components/niftykit/niftykit.app.mjs index f3ae3df34a487..63b519ebc2d59 100644 --- a/components/niftykit/niftykit.app.mjs +++ b/components/niftykit/niftykit.app.mjs @@ -1,11 +1,41 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "niftykit", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.niftykit.com"; + }, + _headers() { + return { + "accept": "application/json", + "x-api-key": `${this.$auth.api_access_key}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + listTokens(args = {}) { + return this._makeRequest({ + path: "/v3/collections/tokens", + ...args, + }); + }, + createMintLink(args = {}) { + return this._makeRequest({ + path: "/onboarding/mintLinks", + method: "POST", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/niftykit/package.json b/components/niftykit/package.json index 2f75c34a3196b..c08dacf552686 100644 --- a/components/niftykit/package.json +++ b/components/niftykit/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/niftykit", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream NiftyKit Components", "main": "niftykit.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/niftykit/sources/common/base.mjs b/components/niftykit/sources/common/base.mjs new file mode 100644 index 0000000000000..58a87afdffc44 --- /dev/null +++ b/components/niftykit/sources/common/base.mjs @@ -0,0 +1,47 @@ +import niftykit from "../../niftykit.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + niftykit, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getItems() { + throw new Error("getItems is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + const items = await this.getItems(); + + for (const item of items) { + const ts = Date.parse(item.createdAt); + if (ts > lastTs) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + if (ts > maxTs) { + maxTs = ts; + } + } + } + + this._setLastTs(maxTs); + }, +}; diff --git a/components/niftykit/sources/new-nft-minted/new-nft-minted.mjs b/components/niftykit/sources/new-nft-minted/new-nft-minted.mjs new file mode 100644 index 0000000000000..8c9f175bc78bf --- /dev/null +++ b/components/niftykit/sources/new-nft-minted/new-nft-minted.mjs @@ -0,0 +1,29 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "niftykit-new-nft-minted", + name: "New NFT Minted", + description: "Emit new event when a new NFT is minted in NiftyKit. [See the documentation](https://api.niftykit.com/docs?_gl=1*d8mlfi*_ga*MTY5MTM2MjIwNi4xNjk0MDMzOTk3*_ga_B0DCGWCR37*MTY5NzE0MTUzNy40LjAuMTY5NzE0MTUzNy42MC4wLjA.#/collections/CollectionsController_getTokens)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + async getItems() { + const { data } = await this.niftykit.listTokens({ + params: { + all: true, + }, + }); + return data; + }, + generateMeta(item) { + return { + id: item.id, + summary: `New NFT Minted ID ${item.id}`, + ts: Date.parse(item.createdAt), + }; + }, + }, +}; diff --git a/components/notion/actions/append-block/append-block.mjs b/components/notion/actions/append-block/append-block.mjs index 3fc525e8154ba..3a597dd0e8f90 100644 --- a/components/notion/actions/append-block/append-block.mjs +++ b/components/notion/actions/append-block/append-block.mjs @@ -5,8 +5,8 @@ export default { ...base, key: "notion-append-block", name: "Append Block to Parent", - description: "Creates and appends blocks to the specified parent. [See the docs](https://developers.notion.com/reference/patch-block-children)", - version: "0.2.10", + description: "Creates and appends blocks to the specified parent. [See the documentation](https://developers.notion.com/reference/patch-block-children)", + version: "0.2.11", type: "action", props: { notion, @@ -40,6 +40,12 @@ export default { description: "Content of new blocks to append. You must use Markdown syntax [See docs](https://www.notion.so/help/writing-and-editing-basics#markdown-&-shortcuts)", optional: true, }, + imageUrls: { + type: "string[]", + label: "Image URLs", + description: "List of URLs to append as image blocks", + optional: true, + }, }, async run({ $ }) { const children = []; @@ -71,6 +77,21 @@ export default { } } + // add image blocks + if (this.imageUrls?.length) { + for (const url of this.imageUrls) { + children.push({ + type: "image", + image: { + type: "external", + external: { + url, + }, + }, + }); + } + } + if (children.length === 0) { $.export("$summary", "Nothing to append"); return; diff --git a/components/notion/actions/update-page/update-page.mjs b/components/notion/actions/update-page/update-page.mjs index 289354bf8e946..7ceb410a0e891 100644 --- a/components/notion/actions/update-page/update-page.mjs +++ b/components/notion/actions/update-page/update-page.mjs @@ -1,13 +1,13 @@ import notion from "../../notion.app.mjs"; import base from "../common/base-page-builder.mjs"; -import { pick } from "lodash-es"; +import pick from "lodash-es/pick.js"; export default { ...base, key: "notion-update-page", name: "Update Page", description: "Updates page property values for the specified page. Properties that are not set will remain unchanged. To append page content, use the *append block* action. [See the docs](https://developers.notion.com/reference/patch-page)", - version: "1.0.0", + version: "1.0.2", type: "action", props: { notion, @@ -78,10 +78,14 @@ export default { }, }, async run({ $ }) { - const currentPage = await this.notion.retrievePage(this.pageId); - const page = this.buildPage(currentPage); - const response = await this.notion.updatePage(this.pageId, page); - $.export("$summary", "Updated page successfully"); - return response; + try { + const currentPage = await this.notion.retrievePage(this.pageId); + const page = this.buildPage(currentPage); + const response = await this.notion.updatePage(this.pageId, page); + $.export("$summary", "Updated page successfully"); + return response; + } catch (error) { + throw new Error(error.body); + } }, }; diff --git a/components/notion/package.json b/components/notion/package.json index fbf2b8ffc4220..7fbe72cd27d7f 100644 --- a/components/notion/package.json +++ b/components/notion/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/notion", - "version": "0.1.2", + "version": "0.1.6", "description": "Pipedream Notion Components", "main": "notion.app.mjs", "keywords": [ diff --git a/components/notion/sources/updated-page/updated-page.mjs b/components/notion/sources/updated-page/updated-page.mjs index fd35a8b330a61..55d5a3e9818a2 100644 --- a/components/notion/sources/updated-page/updated-page.mjs +++ b/components/notion/sources/updated-page/updated-page.mjs @@ -8,7 +8,7 @@ export default { key: "notion-updated-page", name: "Updated Page in Database", /* eslint-disable-line pipedream/source-name */ description: "Emit new event when a page in a database is updated. To select a specific page, use `Updated Page ID` instead", - version: "0.0.7", + version: "0.0.8", type: "source", dedupe: "unique", props: { @@ -23,12 +23,13 @@ export default { async run() { const params = this.lastUpdatedSortParam(); const lastCheckedTimestamp = this.getLastUpdatedTimestamp(); + let newLastUpdatedTimestamp = lastCheckedTimestamp; const pagesStream = this.notion.getPages(this.databaseId, params); for await (const page of pagesStream) { if (!this.isResultNew(page.last_edited_time, lastCheckedTimestamp)) { - break; + continue; } const meta = this.generateMeta( @@ -41,8 +42,13 @@ export default { this.$emit(page, meta); - this.setLastUpdatedTimestamp(Date.parse(page?.last_edited_time)); + newLastUpdatedTimestamp = Math.max( + newLastUpdatedTimestamp, + Date.parse(page?.last_edited_time), + ); } + + this.setLastUpdatedTimestamp(newLastUpdatedTimestamp); }, sampleEmit, }; diff --git a/components/openai/actions/create-transcription/create-transcription.mjs b/components/openai/actions/create-transcription/create-transcription.mjs index 969e5cd0bae7d..4717cb5b7db3c 100644 --- a/components/openai/actions/create-transcription/create-transcription.mjs +++ b/components/openai/actions/create-transcription/create-transcription.mjs @@ -22,7 +22,7 @@ const pipelineAsync = promisify(stream.pipeline); export default { name: "Create Transcription", - version: "0.0.9", + version: "0.1.0", key: "openai-create-transcription", description: "Transcribes audio into the input language. [See docs here](https://platform.openai.com/docs/api-reference/audio/create).", type: "action", @@ -43,7 +43,6 @@ export default { description: "**Optional**. The language of the input audio. Supplying the input language will improve accuracy and latency.", type: "string", optional: true, - default: "en", options: lang.LANGUAGES.map((l) => ({ label: l.label, value: l.value, diff --git a/components/piggy/.gitignore b/components/piggy/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/piggy/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/piggy/actions/common/constants.mjs b/components/piggy/actions/common/constants.mjs new file mode 100644 index 0000000000000..bf0f1c0069c93 --- /dev/null +++ b/components/piggy/actions/common/constants.mjs @@ -0,0 +1,88 @@ +export default { + DATA_TYPES: [ + { + label: "URL", + value: "url", + }, + { + label: "Text", + value: "text", + }, + { + label: "Date", + value: "date", + }, + { + label: "Phone", + value: "phone", + }, + { + label: "Float", + value: "float", + }, + { + label: "Color", + value: "color", + }, + { + label: "Email", + value: "email", + }, + { + label: "Number", + value: "number", + }, + { + label: "Select", + value: "select", + }, + { + label: "Boolean", + value: "boolean", + }, + { + label: "Rich Text", + value: "rich_text", + }, + { + label: "Date & Time", + value: "date_time", + }, + { + label: "Long Text", + value: "long_text", + }, + { + label: "Date Range", + value: "date_range", + }, + { + label: "Time Range", + value: "time_range", + }, + { + label: "Identifier", + value: "identifier", + }, + { + label: "Birth Date", + value: "birth_date", + }, + { + label: "File Upload", + value: "file_upload", + }, + { + label: "Media Upload", + value: "media_upload", + }, + { + label: "Multi-Select", + value: "multi_select", + }, + { + label: "License Plate", + value: "license_plate", + }, + ], +}; diff --git a/components/piggy/actions/create-contact-attribute/create-contact-attribute.mjs b/components/piggy/actions/create-contact-attribute/create-contact-attribute.mjs new file mode 100644 index 0000000000000..4ad8b2ac5b5f6 --- /dev/null +++ b/components/piggy/actions/create-contact-attribute/create-contact-attribute.mjs @@ -0,0 +1,51 @@ +import app from "../../piggy.app.mjs"; +import constants from "../common/constants.mjs"; + +export default { + name: "Create Contact Attribute", + version: "0.0.1", + key: "piggy-create-contact-attribute", + description: "Creates a contact attribute. [See the documentation](https://docs.piggy.eu/v3/oauth/contact-attributes#:~:text=Create%20Contact%20Attribute)", + type: "action", + props: { + app, + name: { + type: "string", + label: "Name", + description: "Name of the attribute", + }, + dataType: { + type: "string", + label: "Data type", + description: "Type of the attribute", + options: constants.DATA_TYPES, + }, + label: { + type: "string", + label: "Label", + description: "Label of the attribute", + }, + descripion: { + type: "string", + label: "Descripion", + description: "Description of the attribute", + }, + }, + async run({ $ }) { + const response = await this.app.createContactAttribute({ + $, + data: { + name: this.name, + data_type: this.dataType, + label: this.label, + description: this.description, + }, + }); + + if (response) { + $.export("$summary", `Successfully created contact attribute with name ${response.data.name}`); + } + + return response; + }, +}; diff --git a/components/piggy/actions/find-or-create-contact/find-or-create-contact.mjs b/components/piggy/actions/find-or-create-contact/find-or-create-contact.mjs new file mode 100644 index 0000000000000..16dfd568f2d80 --- /dev/null +++ b/components/piggy/actions/find-or-create-contact/find-or-create-contact.mjs @@ -0,0 +1,31 @@ +import app from "../../piggy.app.mjs"; + +export default { + name: "Find Or Create Contact", + version: "0.0.1", + key: "piggy-find-or-create-contact", + description: "Find or create a contact. [See the documentation](https://docs.piggy.eu/v3/oauth/contacts#:~:text=Possible%20errors-,Find%20or%20Create%20Contact,-Find%20Contact%20by)", + type: "action", + props: { + app, + email: { + type: "string", + label: "Email", + description: "Email of the contact", + }, + }, + async run({ $ }) { + const response = await this.app.findOrCreateContact({ + $, + data: { + email: this.email, + }, + }); + + if (response) { + $.export("$summary", "Successfully retrieved or created contact"); + } + + return response; + }, +}; diff --git a/components/piggy/package.json b/components/piggy/package.json index 257c339e70032..b0636b1fabe7c 100644 --- a/components/piggy/package.json +++ b/components/piggy/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/piggy", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Piggy Components", - "main": "dist/app/piggy.app.mjs", + "main": "piggy.app.mjs", "keywords": [ "pipedream", "piggy" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/piggy", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/piggy/piggy.app.mjs b/components/piggy/piggy.app.mjs new file mode 100644 index 0000000000000..71a3746382fd4 --- /dev/null +++ b/components/piggy/piggy.app.mjs @@ -0,0 +1,72 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "piggy", + propDefinitions: { + contactId: { + label: "Contact ID", + description: "The contact ID", + type: "string", + async options({ page }) { + const { data: contacts } = await this.getContacts({ + params: { + page: page + 1, + }, + }); + + return contacts.map((contact) => ({ + label: contact.email, + value: contact.uuid, + })); + }, + }, + }, + methods: { + _apiKey() { + return this.$auth.api_key; + }, + _apiUrl() { + return "https://api.piggy.eu/api/v3/oauth"; + }, + async _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + headers: { + Authorization: `Bearer ${this._apiKey()}`, + }, + ...args, + }); + }, + async findOrCreateContact(args = {}) { + return this._makeRequest({ + path: "/clients/contacts/find-or-create", + ...args, + }); + }, + async createContactAttribute(args = {}) { + return this._makeRequest({ + path: "/clients/contact-attributes", + method: "post", + ...args, + }); + }, + async getContacts(args = {}) { + return this._makeRequest({ + path: "/clients/contacts", + ...args, + }); + }, + async updateContact({ + contactId, ...args + }) { + return this._makeRequest({ + path: `/clients/contacts/${contactId}`, + method: "put", + ...args, + }); + }, + }, +}; diff --git a/components/piggy/sources/new-contact-created/new-contact-created.mjs b/components/piggy/sources/new-contact-created/new-contact-created.mjs new file mode 100644 index 0000000000000..deaf9d3aec599 --- /dev/null +++ b/components/piggy/sources/new-contact-created/new-contact-created.mjs @@ -0,0 +1,76 @@ +import app from "../../piggy.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + name: "New Contact Created", + version: "0.0.1", + key: "piggy-new-contact-created", + description: "Emit new event on each new contact.", + type: "source", + dedupe: "unique", + props: { + app, + db: "$.service.db", + timer: { + type: "$.interface.timer", + static: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + emitEvent(data) { + this.$emit(data, { + id: data.uuid, + summary: `New contact created with ID ${data.uuid}`, + ts: new Date(), + }); + }, + _setLastResourceId(id) { + this.db.set("lastResourceId", id); + }, + _getLastResourceId() { + return this.db.get("lastResourceId"); + }, + }, + hooks: { + async deploy() { + const { data: contacts } = await this.app.getContacts({ + params: { + limit: 20, + }, + }); + + contacts.forEach(this.emitEvent); + }, + }, + async run() { + const lastResourceId = this._getLastResourceId(); + + let page = 1; + + while (page >= 0) { + const { data: contacts } = await this.app.getContacts({ + params: { + page, + limit: 100, + }, + }); + + if (contacts.length) { + this._setLastResourceId(contacts[0].uuid); + } + + contacts.forEach(this.emitEvent); + + if ( + contacts.length < 100 || + contacts.filter((contact) => contact.uuid === lastResourceId) + ) { + return; + } + + page++; + } + }, +}; diff --git a/components/placid/package.json b/components/placid/package.json new file mode 100644 index 0000000000000..0118814f153bb --- /dev/null +++ b/components/placid/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/placid", + "version": "0.0.1", + "description": "Pipedream Placid Components", + "main": "placid.app.mjs", + "keywords": [ + "pipedream", + "placid" + ], + "homepage": "https://pipedream.com/apps/placid", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/placid/placid.app.mjs b/components/placid/placid.app.mjs new file mode 100644 index 0000000000000..48f93a4188d73 --- /dev/null +++ b/components/placid/placid.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "placid", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/podio/actions/common/common-item.mjs b/components/podio/actions/common/common-item.mjs index 10a11de702e11..3542d68e755c8 100644 --- a/components/podio/actions/common/common-item.mjs +++ b/components/podio/actions/common/common-item.mjs @@ -53,11 +53,15 @@ export default { }, }, methods: { - getFields() { + async getFields() { const fields = {}; for (let key of Object.keys(this)) { if (key.startsWith("field_")) { - fields[key.split("_")[1]] = utils.extractPropValues(this[key]); + const fieldName = key.split("_")[1]; + const fieldId = await this.getFieldId(this.appId, fieldName); + if (fieldId) { + fields[fieldId] = utils.extractPropValues(this[key]); + } } } return fields; @@ -77,7 +81,25 @@ export default { })) || []; spaceFields.push(...fields); } - return spaceFields; + return this.eliminateDuplicateFields(spaceFields); + }, + eliminateDuplicateFields(fields) { + const seen = new Set(); + return fields.filter((field) => { + const isDuplicate = seen.has(field.label); + seen.add(field.label); + return !isDuplicate; + }); + }, + async getFieldId(appId, fieldName) { + const { fields } = await this.app.getApp({ + appId, + }); + const field = fields.find(({ label }) => label === fieldName); + if (!field) { + console.log(`Field "${fieldName}" not found in app with ID ${appId}.`); + } + return field?.field_id; }, getIfUpdate() { return false; @@ -94,29 +116,22 @@ export default { })).fields : await this.getSpaceFields(); - props.fieldIds = { - type: "integer[]", + props.fieldNames = { + type: "string[]", label: "Fields", description: `Fields to ${this.getIfUpdate() ? "update" : "create"}`, - options: fields.map(({ - field_id: value, label, app_label, - }) => ({ - value, - label: `${label}${app_label - ? " - for use with App \"" + app_label + "\"" - : ""}`, - })), + options: fields.map(({ label }) => label), reloadProps: true, }; - if (!this.fieldIds?.length) { + if (!this.fieldNames?.length) { return props; } - for (const id of this.fieldIds) { - const field = fields.find(({ field_id }) => field_id === id ); + for (const fieldName of this.fieldNames) { + const field = fields.find(({ label }) => fieldName === label ); if (field.type != "calculation") { const newProp = { type: utils.getType(field), @@ -134,7 +149,7 @@ export default { }; }); } - props[`field_${field.field_id}`] = newProp; + props[`field_${field.label}`] = newProp; } } return props; diff --git a/components/podio/actions/create-item/create-item.mjs b/components/podio/actions/create-item/create-item.mjs index c1b5612a2bd2e..cea4812290f27 100644 --- a/components/podio/actions/create-item/create-item.mjs +++ b/components/podio/actions/create-item/create-item.mjs @@ -6,13 +6,13 @@ delete props.itemId; export default { type: "action", key: "podio-create-item", - version: "0.0.2", + version: "0.0.3", name: "Create an Item", description: "Adds a new item to the given app. [See the documentation](https://developers.podio.com/doc/items/add-new-item-22362)", ...common, props, async run ({ $ }) { - const fields = this.getFields(); + const fields = await this.getFields(); const reminder = this.reminder ? { remind_delta: this.reminder, diff --git a/components/podio/actions/update-item/update-item.mjs b/components/podio/actions/update-item/update-item.mjs index 2ab3e4e7e0cbf..5b0f4185effcf 100644 --- a/components/podio/actions/update-item/update-item.mjs +++ b/components/podio/actions/update-item/update-item.mjs @@ -3,7 +3,7 @@ import common from "../common/common-item.mjs"; export default { type: "action", key: "podio-update-item", - version: "0.0.2", + version: "0.0.3", name: "Update an Item", description: "Updates an item. [See the documentation](https://developers.podio.com/doc/items/update-item-22363)", ...common, @@ -14,7 +14,7 @@ export default { }, }, async run ({ $ }) { - const fields = this.getFields(); + const fields = await this.getFields(); const reminder = this.reminder ? { remind_delta: this.reminder, diff --git a/components/podio/package.json b/components/podio/package.json index 9b3b01c926130..86c3484381910 100644 --- a/components/podio/package.json +++ b/components/podio/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/podio", - "version": "0.0.3", + "version": "0.0.4", "description": "Pipedream Podio Components", "main": "podio.app.mjs", "keywords": [ diff --git a/components/polygonscan/polygonscan.app.mjs b/components/polygonscan/polygonscan.app.mjs index f5dd509ff4a29..f3e4a9fa9a5d4 100644 --- a/components/polygonscan/polygonscan.app.mjs +++ b/components/polygonscan/polygonscan.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/poof/actions/create-deposit-address/create-deposit-address.mjs b/components/poof/actions/create-deposit-address/create-deposit-address.mjs new file mode 100644 index 0000000000000..8c958f31a2fb7 --- /dev/null +++ b/components/poof/actions/create-deposit-address/create-deposit-address.mjs @@ -0,0 +1,37 @@ +import poof from "../../poof.app.mjs"; + +export default { + key: "poof-create-deposit-address", + name: "Create Deposit Address", + description: "Creates a new deposit address in Poof. [See the documentation](https://docs.poof.io/reference/create_address)", + version: "0.0.1", + type: "action", + props: { + poof, + amount: { + type: "string", + label: "Amount", + description: "The transaction amount", + }, + crypto: { + type: "string", + label: "Crypto", + description: "The type of cryptocurrency. Example: `bitcoin`", + }, + }, + async run({ $ }) { + const response = await this.poof.createDepositAddress({ + data: { + amount: +this.amount, + crypto: this.crypto, + }, + $, + }); + + if (response?.address) { + $.export("$summary", `Successfully created deposit address ${response.address}.`); + } + + return response; + }, +}; diff --git a/components/poof/actions/list-transactions/list-transactions.mjs b/components/poof/actions/list-transactions/list-transactions.mjs new file mode 100644 index 0000000000000..3115a29c0e55a --- /dev/null +++ b/components/poof/actions/list-transactions/list-transactions.mjs @@ -0,0 +1,21 @@ +import poof from "../../poof.app.mjs"; + +export default { + key: "poof-list-transactions", + name: "List Transactions", + description: "Retrieve a list of transactions in Poof. [See the documentation](https://docs.poof.io/reference/fetch-transaction-list)", + version: "0.0.1", + type: "action", + props: { + poof, + }, + async run({ $ }) { + const response = await this.poof.listTransactions({ + $, + }); + + $.export("$summary", "Successfully retrieved transactions."); + + return response; + }, +}; diff --git a/components/poof/actions/send-transaction/send-transaction.mjs b/components/poof/actions/send-transaction/send-transaction.mjs new file mode 100644 index 0000000000000..53ae2a0627a15 --- /dev/null +++ b/components/poof/actions/send-transaction/send-transaction.mjs @@ -0,0 +1,43 @@ +import poof from "../../poof.app.mjs"; + +export default { + key: "poof-send-transaction", + name: "Send Transaction", + description: "Sends a transaction in Poof. [See the documentation](https://docs.poof.io/reference/sendtransaction)", + version: "0.0.1", + type: "action", + props: { + poof, + amount: { + type: "string", + label: "Amount", + description: "The transaction amount", + }, + crypto: { + type: "string", + label: "Crypto", + description: "The type of cryptocurrency. Example: `bitcoin`", + }, + address: { + type: "string", + label: "Address", + description: "The address to send the transaction to", + }, + }, + async run({ $ }) { + const response = await this.poof.sendTransaction({ + data: { + amount: +this.amount, + crypto: this.crypto, + address: this.address, + }, + $, + }); + + if (response?.message !== "invalid") { + $.export("$summary", "Successfully sent transaction."); + } + + return response; + }, +}; diff --git a/components/poof/package.json b/components/poof/package.json index c95d406448c55..70dad0c610542 100644 --- a/components/poof/package.json +++ b/components/poof/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/poof", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Poof Components", "main": "poof.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/poof/poof.app.mjs b/components/poof/poof.app.mjs index 6a300d036b7de..5380ef63e38e1 100644 --- a/components/poof/poof.app.mjs +++ b/components/poof/poof.app.mjs @@ -1,11 +1,53 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "poof", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://www.poof.io/api"; + }, + _headers() { + return { + "Authorization": `${this.$auth.api_key}`, + "Content-Type": "application/json", + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + method: "POST", + ...args, + }); + }, + createWebhook(args = {}) { + return this._makeRequest({ + path: "/v1/create_webhook", + ...args, + }); + }, + listTransactions(args = {}) { + return this._makeRequest({ + path: "/v1/fetch_transactions", + ...args, + }); + }, + createDepositAddress(args = {}) { + return this._makeRequest({ + path: "/v2/create_charge", + ...args, + }); + }, + sendTransaction(args = {}) { + return this._makeRequest({ + path: "/v2/payouts", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/poof/sources/common/base.mjs b/components/poof/sources/common/base.mjs new file mode 100644 index 0000000000000..23f97af5f211d --- /dev/null +++ b/components/poof/sources/common/base.mjs @@ -0,0 +1,32 @@ +import poof from "../../poof.app.mjs"; + +export default { + props: { + poof, + http: "$.interface.http", + }, + hooks: { + async deploy() { + await this.poof.createWebhook({ + data: { + url: this.http.endpoint, + }, + }); + }, + }, + methods: { + isRelevant() { + return true; + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run(event) { + const { body } = event; + if (this.isRelevant(body)) { + const meta = this.generateMeta(body); + this.$emit(body, meta); + } + }, +}; diff --git a/components/poof/sources/common/constants.mjs b/components/poof/sources/common/constants.mjs new file mode 100644 index 0000000000000..14db412e4bfed --- /dev/null +++ b/components/poof/sources/common/constants.mjs @@ -0,0 +1,13 @@ +const PAYMENT_TYPES = { + "yes": "Payment Complete", + "payout": "Crypto Payout Sent", + "paid_partial": "Crypto Payment Partially Paid", + "transfer_out": "Bank Transfer Sent", + "pending": "Payment Processing", + "transfer_in": "Bank Transfer/ Poof Balance Received", + "hold": "Third party payment method pending", +}; + +export default { + PAYMENT_TYPES, +}; diff --git a/components/poof/sources/new-payment-made/new-payment-made.mjs b/components/poof/sources/new-payment-made/new-payment-made.mjs new file mode 100644 index 0000000000000..77bac723feda0 --- /dev/null +++ b/components/poof/sources/new-payment-made/new-payment-made.mjs @@ -0,0 +1,46 @@ +import common from "../common/base.mjs"; +import constants from "../common/constants.mjs"; + +export default { + ...common, + key: "poof-new-payment-made", + name: "New Payment Made", + description: "Emit new events when a payment is made in Poof. [See the documentation](https://docs.poof.io/reference/notifications-api)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + paymentTypes: { + type: "string[]", + label: "Payment Type(s)", + description: "Filter incoming events by payment type", + options() { + const paymentTypes = []; + for (const [ + key, + value, + ] of Object.entries(constants.PAYMENT_TYPES)) { + paymentTypes.push({ + value: key, + label: value, + }); + } + return paymentTypes; + }, + }, + }, + methods: { + ...common.methods, + isRelevant(payment) { + return this.paymentTypes.includes(payment.paid); + }, + generateMeta(payment) { + return { + id: payment.payment_id, + summary: `New ${constants.PAYMENT_TYPES[payment.paid]}`, + ts: Date.now(), + }; + }, + }, +}; diff --git a/components/productlane/actions/create-contact/create-contact.mjs b/components/productlane/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..674228ec9fafd --- /dev/null +++ b/components/productlane/actions/create-contact/create-contact.mjs @@ -0,0 +1,42 @@ +import productlane from "../../productlane.app.mjs"; + +export default { + key: "productlane-create-contact", + name: "Create Contact", + description: "Creates a new contact with email, name, and an array of segments in Productlane. [See the documentation](https://productlane.com/docs/api-reference/contacts/create-contact)", + version: "0.0.1", + type: "action", + props: { + productlane, + email: { + propDefinition: [ + productlane, + "email", + ], + }, + name: { + type: "string", + label: "Name", + description: "The name of the contact", + optional: true, + }, + segments: { + type: "string[]", + label: "Segments", + description: "Array of segments", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.productlane.createContact({ + $, + data: { + email: this.email, + name: this.name, + segments: this.segments, + }, + }); + $.export("$summary", `Successfully created contact ${this.email}`); + return response; + }, +}; diff --git a/components/productlane/actions/create-feedback/create-feedback.mjs b/components/productlane/actions/create-feedback/create-feedback.mjs new file mode 100644 index 0000000000000..d39b4f4cb0e9f --- /dev/null +++ b/components/productlane/actions/create-feedback/create-feedback.mjs @@ -0,0 +1,100 @@ +import { + ORIGIN_OPTIONS, PAIN_LEVEL_OPTIONS, +} from "../../common/constants.mjs"; +import productlane from "../../productlane.app.mjs"; + +export default { + key: "productlane-create-feedback", + name: "Create Feedback", + description: + "Create new feedback in Productlane. [See the documentation](https://productlane.com/docs/api-reference/portal/create-feedback)", + version: "0.0.1", + type: "action", + props: { + productlane, + projectId: { + propDefinition: [ + productlane, + "projectId", + ], + }, + email: { + propDefinition: [ + productlane, + "email", + ], + description: "The email for the feedback", + }, + text: { + type: "string", + label: "Text", + description: "The text of the feedback", + }, + notifyByEmail: { + propDefinition: [ + productlane, + "notify", + ], + label: "Notify by Email", + description: "Whether to notify by email", + }, + notifyBySlack: { + propDefinition: [ + productlane, + "notify", + ], + label: "Notify by Slack", + description: "Whether to notify by slack", + }, + origin: { + type: "string", + label: "Origin", + description: "The origin of the feedback", + optional: true, + options: ORIGIN_OPTIONS, + }, + painLevel: { + type: "string", + label: "Pain Level", + description: "The pain level of the feedback", + options: PAIN_LEVEL_OPTIONS, + }, + }, + async run({ $ }) { + const { + email, + notifyByEmail, + notifyBySlack, + origin, + painLevel, + text, + projectId, + } = this; + + const data = { + email: email, + notify: ((notifyByEmail ?? notifyBySlack) !== undefined) + ? { + email: notifyByEmail, + slack: notifyBySlack, + } + : undefined, + origin: origin, + painLevel: painLevel, + text: text, + projectId: projectId, + }; + + const response = await this.productlane.createFeedback({ + $, + data, + }); + + $.export( + "$summary", + `Successfully created feedback with ID: ${response.id}`, + ); + + return response; + }, +}; diff --git a/components/productlane/actions/upvote-project/upvote-project.mjs b/components/productlane/actions/upvote-project/upvote-project.mjs new file mode 100644 index 0000000000000..ecc627a435d15 --- /dev/null +++ b/components/productlane/actions/upvote-project/upvote-project.mjs @@ -0,0 +1,36 @@ +import productlane from "../../productlane.app.mjs"; + +export default { + key: "productlane-upvote-project", + name: "Upvote Project", + description: "Upvotes a project by ID. [See the documentation](https://productlane.com/docs/api-reference/portal/upvote-project)", + version: "0.0.1", + type: "action", + props: { + productlane, + projectId: { + propDefinition: [ + productlane, + "projectId", + ], + }, + email: { + propDefinition: [ + productlane, + "email", + ], + description: "The email associated with the upvote", + }, + }, + async run({ $ }) { + const response = await this.productlane.upvoteProject({ + $, + projectId: this.projectId, + data: { + email: this.email, + }, + }); + $.export("$summary", "Successfully upvoted project"); + return response; + }, +}; diff --git a/components/productlane/common/constants.mjs b/components/productlane/common/constants.mjs new file mode 100644 index 0000000000000..26af38588bf8b --- /dev/null +++ b/components/productlane/common/constants.mjs @@ -0,0 +1,20 @@ +export const ORIGIN_OPTIONS = [ + "INAPP", + "PORTAL", + "API", + "SLACK", + "INTERCOM", + "INTERCOM_ATTACHMENT", + "ZENDESK_ATTACHMENT", + "FRONT_ATTACHMENT", + "EMAIL", + "ZAPIER", + "HUBSPOT", +]; + +export const PAIN_LEVEL_OPTIONS = [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", +]; diff --git a/components/productlane/package.json b/components/productlane/package.json index e27c262ef84c1..772b5a97f77b2 100644 --- a/components/productlane/package.json +++ b/components/productlane/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/productlane", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Productlane Components", "main": "productlane.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/productlane/productlane.app.mjs b/components/productlane/productlane.app.mjs index d48169256f3e6..dea5eeb14a8eb 100644 --- a/components/productlane/productlane.app.mjs +++ b/components/productlane/productlane.app.mjs @@ -1,11 +1,83 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "productlane", - propDefinitions: {}, + propDefinitions: { + projectId: { + type: "string", + label: "Project ID", + description: "The ID of the project. [See the documentation](https://productlane.com/docs/api-reference/portal/list-projects) for more information", + async options() { + const projects = await this.listProjects(); + return projects.map((p) => ({ + label: p.name, + value: p.id, + })); + }, + }, + email: { + type: "string", + label: "Email", + description: "The email of the contact", + }, + notify: { + type: "boolean", + label: "Notify", + description: "Whether to notify", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://productlane.com/api/v1"; + }, + _workspaceId() { + return this.$auth.workspace_id; + }, + async _makeRequest({ + $ = this, + path, + headers, + ...otherOpts + }) { + return axios($, { + ...otherOpts, + url: this._baseUrl() + path, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.api_key}`, + }, + }); + }, + async createContact(opts) { + return this._makeRequest({ + ...opts, + path: "/contacts", + method: "POST", + }); + }, + async upvoteProject({ + projectId, ...opts + }) { + return this._makeRequest({ + ...opts, + path: `/projects/${projectId}/upvotes`, + method: "POST", + }); + }, + async createFeedback(opts) { + return this._makeRequest({ + ...opts, + path: "/feedback", + method: "POST", + }); + }, + async listProjects() { + const { projects } = await this._makeRequest({ + path: `/projects/${this._workspaceId()}`, + }); + return projects; }, }, -}; \ No newline at end of file +}; diff --git a/components/puppeteer/actions/get-html/get-html.mjs b/components/puppeteer/actions/get-html/get-html.mjs index 0e01438780156..70c72e3b10c56 100644 --- a/components/puppeteer/actions/get-html/get-html.mjs +++ b/components/puppeteer/actions/get-html/get-html.mjs @@ -4,7 +4,7 @@ export default { key: "puppeteer-get-html", name: "Get HTML", description: "Get the HTML of a webpage using Puppeteer. [See the documentation](https://pptr.dev/api/puppeteer.page.content)", - version: "0.0.6", + version: "0.0.7", type: "action", props: { puppeteer, diff --git a/components/puppeteer/actions/get-page-title/get-page-title.mjs b/components/puppeteer/actions/get-page-title/get-page-title.mjs index 27e3c035aa416..23f555cf63bb8 100644 --- a/components/puppeteer/actions/get-page-title/get-page-title.mjs +++ b/components/puppeteer/actions/get-page-title/get-page-title.mjs @@ -4,7 +4,7 @@ export default { key: "puppeteer-get-page-title", name: "Get Page Title", description: "Get the title of a webpage using Puppeteer. [See the documentation](https://pptr.dev/api/puppeteer.page.title)", - version: "0.0.6", + version: "0.0.7", type: "action", props: { puppeteer, diff --git a/components/puppeteer/actions/get-pdf/get-pdf.mjs b/components/puppeteer/actions/get-pdf/get-pdf.mjs index ece72a710be4f..64cbdebb3ad66 100644 --- a/components/puppeteer/actions/get-pdf/get-pdf.mjs +++ b/components/puppeteer/actions/get-pdf/get-pdf.mjs @@ -6,7 +6,7 @@ export default { key: "puppeteer-get-pdf", name: "Get PDF", description: "Generate a PDF of a page using Puppeteer. [See the documentation](https://pptr.dev/api/puppeteer.page.pdf)", - version: "0.0.6", + version: "0.0.7", type: "action", props: { puppeteer, diff --git a/components/puppeteer/actions/screenshot-page/screenshot-page.mjs b/components/puppeteer/actions/screenshot-page/screenshot-page.mjs index 06f021289b006..443fdc7124ef7 100644 --- a/components/puppeteer/actions/screenshot-page/screenshot-page.mjs +++ b/components/puppeteer/actions/screenshot-page/screenshot-page.mjs @@ -7,7 +7,7 @@ export default { key: "puppeteer-screenshot-page", name: "Screenshot a Page", description: "Captures a screenshot of a page using Puppeteer. [See the documentation](https://pptr.dev/api/puppeteer.page.screenshot)", - version: "0.0.7", + version: "0.0.8", type: "action", props: { puppeteer, diff --git a/components/puppeteer/package.json b/components/puppeteer/package.json index 709fb3e5a9fde..7a94b3067c22c 100644 --- a/components/puppeteer/package.json +++ b/components/puppeteer/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/puppeteer", - "version": "0.7.0", + "version": "0.8.0", "description": "Pipedream Puppeteer Components", "main": "puppeteer.app.mjs", "keywords": [ diff --git a/components/puppeteer/puppeteer.app.mjs b/components/puppeteer/puppeteer.app.mjs index 3ec238c903a4c..c2d67254e57c4 100644 --- a/components/puppeteer/puppeteer.app.mjs +++ b/components/puppeteer/puppeteer.app.mjs @@ -21,12 +21,14 @@ export default { const browser = await puppeteer.launch({ executablePath: await chromium.executablePath(), headless: chromium.headless, + cacheDirectory: "/tmp", ignoreHTTPSErrors: true, defaultViewport: chromium.defaultViewport, args: [ ...chromium.args, "--hide-scrollbars", "--disable-web-security", + "--font-render-hinting=none", ], ...opts, }); diff --git a/components/push_by_techulus/package.json b/components/push_by_techulus/package.json new file mode 100644 index 0000000000000..6074099306b54 --- /dev/null +++ b/components/push_by_techulus/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/push_by_techulus", + "version": "0.0.1", + "description": "Pipedream Push by Techulus Components", + "main": "push_by_techulus.app.mjs", + "keywords": [ + "pipedream", + "push_by_techulus" + ], + "homepage": "https://pipedream.com/apps/push_by_techulus", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/push_by_techulus/push_by_techulus.app.mjs b/components/push_by_techulus/push_by_techulus.app.mjs new file mode 100644 index 0000000000000..c1fc46dc3fde5 --- /dev/null +++ b/components/push_by_techulus/push_by_techulus.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "push_by_techulus", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/recruitee/package.json b/components/recruitee/package.json new file mode 100644 index 0000000000000..28add3a30e6fd --- /dev/null +++ b/components/recruitee/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/recruitee", + "version": "0.0.1", + "description": "Pipedream Recruitee Components", + "main": "recruitee.app.mjs", + "keywords": [ + "pipedream", + "recruitee" + ], + "homepage": "https://pipedream.com/apps/recruitee", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/recruitee/recruitee.app.mjs b/components/recruitee/recruitee.app.mjs new file mode 100644 index 0000000000000..6e73d0107ac0f --- /dev/null +++ b/components/recruitee/recruitee.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "recruitee", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/redmine/actions/create-issue/create-issue.mjs b/components/redmine/actions/create-issue/create-issue.mjs new file mode 100644 index 0000000000000..91ed70602d2a5 --- /dev/null +++ b/components/redmine/actions/create-issue/create-issue.mjs @@ -0,0 +1,69 @@ +import app from "../../redmine.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "redmine-create-issue", + name: "Create Issue", + description: "Creates a new issue in Redmine. [See the documentation](https://www.redmine.org/projects/redmine/wiki/rest_issues#creating-an-issue)", + version: "0.0.1", + type: "action", + props: { + app, + projectId: { + propDefinition: [ + app, + "projectId", + ], + }, + trackerId: { + propDefinition: [ + app, + "trackerId", + ], + }, + subject: { + type: "string", + label: "Subject", + description: "The subject of the issue", + }, + description: { + type: "string", + label: "Description", + description: "The description of the issue", + }, + statusId: { + propDefinition: [ + app, + "statusId", + ], + }, + priorityId: { + propDefinition: [ + app, + "priorityId", + ], + }, + }, + methods: { + createIssue(args = {}) { + return this.app.post({ + path: "/issues.json", + ...args, + }); + }, + }, + run({ $: step }) { + const { + createIssue, + ...issue + } = this; + + return createIssue({ + step, + data: { + issue: utils.transformProps(issue), + }, + summary: (response) => `Successfully created issue with ID: \`${response.issue?.id}\``, + }); + }, +}; diff --git a/components/redmine/actions/delete-user/delete-user.mjs b/components/redmine/actions/delete-user/delete-user.mjs new file mode 100644 index 0000000000000..177f0853833ff --- /dev/null +++ b/components/redmine/actions/delete-user/delete-user.mjs @@ -0,0 +1,40 @@ +import app from "../../redmine.app.mjs"; + +export default { + key: "redmine-delete-user", + name: "Delete User", + description: "Deletes a user from the Redmine platform. [See the documentation](https://www.redmine.org/projects/redmine/wiki/rest_users#delete)", + version: "0.0.1", + type: "action", + props: { + app, + userId: { + propDefinition: [ + app, + "userId", + ], + }, + }, + methods: { + deleteUser({ + userId, ...args + } = {}) { + return this.app.delete({ + path: `/users/${userId}.json`, + ...args, + }); + }, + }, + run({ $: step }) { + const { + deleteUser, + userId, + } = this; + + return deleteUser({ + step, + userId, + summary: () => "Successfully deleted user", + }); + }, +}; diff --git a/components/redmine/actions/update-project/update-project.mjs b/components/redmine/actions/update-project/update-project.mjs new file mode 100644 index 0000000000000..73eb6c95ba9c5 --- /dev/null +++ b/components/redmine/actions/update-project/update-project.mjs @@ -0,0 +1,75 @@ +import app from "../../redmine.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "redmine-update-project", + name: "Update Project", + description: "Updates an existing project in Redmine. [See the documentation](https://www.redmine.org/projects/redmine/wiki/rest_projects#updating-a-project)", + version: "0.0.1", + type: "action", + props: { + app, + projectId: { + propDefinition: [ + app, + "projectId", + ], + }, + name: { + type: "string", + label: "Name", + description: "The name of the project", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "The description of the project", + optional: true, + }, + homepage: { + type: "string", + label: "Homepage", + description: "The homepage of the project", + optional: true, + }, + isPublic: { + type: "boolean", + label: "Is Public", + description: "Whether the project is public", + optional: true, + }, + inheritMembers: { + type: "boolean", + label: "Inherit Members", + description: "Whether the project should inherit members from its parent", + optional: true, + }, + }, + methods: { + updateProject({ + projectId, ...args + } = {}) { + return this.app.put({ + path: `/projects/${projectId}.json`, + ...args, + }); + }, + }, + run({ $: step }) { + const { + updateProject, + projectId, + ...project + } = this; + + return updateProject({ + step, + projectId, + data: { + project: utils.transformProps(project), + }, + summary: () => "Successfully updated project", + }); + }, +}; diff --git a/components/redmine/common/constants.mjs b/components/redmine/common/constants.mjs new file mode 100644 index 0000000000000..4f26a9a20e03f --- /dev/null +++ b/components/redmine/common/constants.mjs @@ -0,0 +1,15 @@ +const SUMMARY_LABEL = "$summary"; +const DOMAIN_PLACEHOLDER = "{domain}"; +const BASE_URL = `https://${DOMAIN_PLACEHOLDER}`; +const LAST_CREATED_AT = "lastCreatedAt"; +const DEFAULT_MAX = 600; +const DEFAULT_LIMIT = 60; + +export default { + SUMMARY_LABEL, + DOMAIN_PLACEHOLDER, + BASE_URL, + DEFAULT_MAX, + DEFAULT_LIMIT, + LAST_CREATED_AT, +}; diff --git a/components/redmine/common/utils.mjs b/components/redmine/common/utils.mjs new file mode 100644 index 0000000000000..2bcc3ad0a1e86 --- /dev/null +++ b/components/redmine/common/utils.mjs @@ -0,0 +1,43 @@ +function toSnakeCase(str) { + return str?.replace(/([A-Z])/g, "_$1").toLowerCase(); +} + +function keysToSnakeCase(data = {}) { + return Object.entries(data) + .reduce((acc, [ + key, + value, + ]) => ({ + ...acc, + [toSnakeCase(key)]: value, + }), {}); +} + +function transformProps(props) { + if (!props) { + return; + } + + return keysToSnakeCase( + Object.fromEntries( + Object.entries(props) + .filter(([ + key, + value, + ]) => typeof(value) !== "function" && key !== "app"), + ), + ); +} + +async function iterate(iterations) { + const items = []; + for await (const item of iterations) { + items.push(item); + } + return items; +} + +export default { + transformProps, + iterate, +}; diff --git a/components/redmine/package.json b/components/redmine/package.json index 56a5906166039..17144e2e3db94 100644 --- a/components/redmine/package.json +++ b/components/redmine/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/redmine", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Redmine Components", "main": "redmine.app.mjs", "keywords": [ @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/redmine/redmine.app.mjs b/components/redmine/redmine.app.mjs index 830f6ecc68ca3..0bc99f353ddbe 100644 --- a/components/redmine/redmine.app.mjs +++ b/components/redmine/redmine.app.mjs @@ -1,11 +1,226 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import constants from "./common/constants.mjs"; +import utils from "./common/utils.mjs"; + export default { type: "app", app: "redmine", - propDefinitions: {}, + propDefinitions: { + projectId: { + type: "integer", + label: "Project ID", + description: "The ID of the project", + async options() { + const response = await this.listProjects(); + console.log(response); + const { projects } = response; + return projects.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + trackerId: { + type: "integer", + label: "Tracker ID", + description: "The ID of the tracker", + async options() { + const { trackers } = await this.listTrackers(); + return trackers.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + statusId: { + type: "integer", + label: "Status ID", + description: "The ID of the status", + async options() { + const { issue_statuses: statuses } = await this.listIssueStatuses(); + return statuses.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + priorityId: { + type: "integer", + label: "Priority ID", + description: "The ID of the priority", + async options() { + const { issue_priorities: priorities } = await this.listIssuePriorities(); + return priorities.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + userId: { + type: "integer", + label: "User ID", + description: "The ID of the user", + async options() { + const { users } = await this.listUsers(); + return users.map(({ + id: value, login: label, + }) => ({ + label, + value, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + exportSummary(step) { + if (!step?.export) { + throw new ConfigurationError("The summary method should be bind to the step object aka `$`"); + } + return (msg = "") => step.export(constants.SUMMARY_LABEL, msg); + }, + getUrl(path) { + const baseUrl = constants.BASE_URL + .replace(constants.DOMAIN_PLACEHOLDER, this.$auth.hostname); + return `${baseUrl}${path}`; + }, + async makeRequest({ + step = this, path, headers, summary, ...args + } = {}) { + const { + getUrl, + exportSummary, + $auth: { api_key: apiKey }, + } = this; + + const config = { + ...args, + url: getUrl(path), + headers: { + ...headers, + "X-Redmine-API-Key": apiKey, + }, + }; + + const response = await axios(step, config); + + if (typeof(summary) === "function") { + exportSummary(step)(summary(response)); + } + + return response || { + success: true, + }; + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + put(args = {}) { + return this.makeRequest({ + method: "put", + ...args, + }); + }, + delete(args = {}) { + return this.makeRequest({ + method: "delete", + ...args, + }); + }, + listIssues(args = {}) { + return this.makeRequest({ + path: "/issues.json", + ...args, + }); + }, + listProjects(args = {}) { + return this.makeRequest({ + path: "/projects.json", + ...args, + }); + }, + listTrackers(args = {}) { + return this.makeRequest({ + path: "/trackers.json", + ...args, + }); + }, + listIssueStatuses(args = {}) { + return this.makeRequest({ + path: "/issue_statuses.json", + ...args, + }); + }, + listIssuePriorities(args = {}) { + return this.makeRequest({ + path: "/enumerations/issue_priorities.json", + ...args, + }); + }, + listUsers(args = {}) { + return this.makeRequest({ + path: "/users.json", + ...args, + }); + }, + async *getIterations({ + resourceFn, + resourceFnArgs, + resourceName, + max = constants.DEFAULT_MAX, + }) { + let offset = 0; + let resourcesCount = 0; + + while (true) { + const response = + await resourceFn({ + ...resourceFnArgs, + params: { + offset, + limit: constants.DEFAULT_LIMIT, + ...resourceFnArgs?.params, + }, + }); + + const nextResources = resourceName && response[resourceName] || response; + + if (!nextResources?.length) { + console.log("No more resources found"); + return; + } + + for (const resource of nextResources) { + yield resource; + resourcesCount += 1; + + if (resourcesCount >= max) { + return; + } + } + + if (nextResources.length < constants.DEFAULT_LIMIT) { + console.log("Less resources than the limit found, no more resources to fetch"); + return; + } + + offset += constants.DEFAULT_LIMIT; + } + }, + paginate(args = {}) { + return utils.iterate(this.getIterations(args)); }, }, -}; \ No newline at end of file +}; diff --git a/components/redmine/sources/common/base.mjs b/components/redmine/sources/common/base.mjs new file mode 100644 index 0000000000000..59b0f195959dd --- /dev/null +++ b/components/redmine/sources/common/base.mjs @@ -0,0 +1,14 @@ +import { ConfigurationError } from "@pipedream/platform"; +import app from "../../redmine.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + }, + methods: { + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, +}; diff --git a/components/redmine/sources/common/polling.mjs b/components/redmine/sources/common/polling.mjs new file mode 100644 index 0000000000000..418eb4b8534ab --- /dev/null +++ b/components/redmine/sources/common/polling.mjs @@ -0,0 +1,58 @@ +import { + ConfigurationError, + DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import common from "./base.mjs"; + +export default { + ...common, + props: { + ...common.props, + timer: { + type: "$.interface.timer", + label: "Polling schedule", + description: "How often to poll the API", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + ...common.methods, + getResourceName() { + throw new ConfigurationError("getResourceName is not implemented"); + }, + getResourceFn() { + throw new ConfigurationError("getResourceFn is not implemented"); + }, + getResourceFnArgs() { + throw new ConfigurationError("getResourceFnArgs is not implemented"); + }, + processEvent(resource) { + const meta = this.generateMeta(resource); + this.$emit(resource, meta); + }, + async processResources(resources) { + Array.from(resources) + .reverse() + .forEach(this.processEvent); + }, + }, + async run() { + const { + app, + getResourceFn, + getResourceFnArgs, + getResourceName, + processResources, + } = this; + + const resources = await app.paginate({ + resourceFn: getResourceFn(), + resourceFnArgs: getResourceFnArgs(), + resourceName: getResourceName(), + }); + + processResources(resources); + }, +}; diff --git a/components/redmine/sources/issue-created/issue-created.mjs b/components/redmine/sources/issue-created/issue-created.mjs new file mode 100644 index 0000000000000..70ce8122c3b07 --- /dev/null +++ b/components/redmine/sources/issue-created/issue-created.mjs @@ -0,0 +1,34 @@ +import common from "../common/polling.mjs"; + +export default { + ...common, + key: "redmine-issue-created", + name: "New Issue Created", + description: "Emit new event when a new issue is created in Redmine", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceName() { + return "issues"; + }, + getResourceFn() { + return this.app.listIssues; + }, + getResourceFnArgs() { + return { + params: { + sort: "created_on:desc", + }, + }; + }, + generateMeta(resource) { + return { + id: resource.id, + summary: `New Issue: ${resource.subject}`, + ts: Date.parse(resource.created_on), + }; + }, + }, +}; diff --git a/components/redmine/sources/project-updated/project-updated.mjs b/components/redmine/sources/project-updated/project-updated.mjs new file mode 100644 index 0000000000000..89762b3b5f663 --- /dev/null +++ b/components/redmine/sources/project-updated/project-updated.mjs @@ -0,0 +1,35 @@ +import common from "../common/polling.mjs"; + +export default { + ...common, + key: "redmine-project-updated", + name: "Project Updated", + description: "Emits an event whenever a project is updated in Redmine", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceName() { + return "projects"; + }, + getResourceFn() { + return this.app.listProjects; + }, + getResourceFnArgs() { + return { + params: { + sort: "updated_on:desc", + }, + }; + }, + generateMeta(resource) { + const ts = Date.parse(resource.updated_on); + return { + id: `${resource.id}-${ts}`, + summary: `Project Updated: ${resource.name}`, + ts, + }; + }, + }, +}; diff --git a/components/referralhero/package.json b/components/referralhero/package.json new file mode 100644 index 0000000000000..ea5c2c49fb729 --- /dev/null +++ b/components/referralhero/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/referralhero", + "version": "0.0.1", + "description": "Pipedream ReferralHero Components", + "main": "referralhero.app.mjs", + "keywords": [ + "pipedream", + "referralhero" + ], + "homepage": "https://pipedream.com/apps/referralhero", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/referralhero/referralhero.app.mjs b/components/referralhero/referralhero.app.mjs new file mode 100644 index 0000000000000..5dd6827bb507b --- /dev/null +++ b/components/referralhero/referralhero.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "referralhero", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/riskadvisor/actions/create-client/create-client.mjs b/components/riskadvisor/actions/create-client/create-client.mjs new file mode 100644 index 0000000000000..ca8a2dcc01eae --- /dev/null +++ b/components/riskadvisor/actions/create-client/create-client.mjs @@ -0,0 +1,102 @@ +import riskadvisor from "../../riskadvisor.app.mjs"; + +export default { + key: "riskadvisor-create-client", + name: "Create Client", + description: "Create a new client in RiskAdvisor. [See the documentation](https://api.riskadvisor.insure/clients#create-a-client)", + version: "0.0.1", + type: "action", + props: { + riskadvisor, + firstName: { + propDefinition: [ + riskadvisor, + "firstName", + ], + }, + lastName: { + propDefinition: [ + riskadvisor, + "lastName", + ], + }, + phoneNumber: { + propDefinition: [ + riskadvisor, + "phoneNumber", + ], + }, + email: { + propDefinition: [ + riskadvisor, + "email", + ], + }, + middleName: { + propDefinition: [ + riskadvisor, + "middleName", + ], + }, + suffix: { + propDefinition: [ + riskadvisor, + "suffix", + ], + }, + dateOfBirth: { + propDefinition: [ + riskadvisor, + "dateOfBirth", + ], + }, + contactPreference: { + propDefinition: [ + riskadvisor, + "contactPreference", + ], + }, + gender: { + propDefinition: [ + riskadvisor, + "gender", + ], + }, + education: { + propDefinition: [ + riskadvisor, + "education", + ], + }, + occupation: { + propDefinition: [ + riskadvisor, + "occupation", + ], + }, + }, + async run({ $ }) { + const response = await this.riskadvisor.createClient({ + data: { + first_name: this.firstName, + last_name: this.lastName, + phone_number: this.phoneNumber, + email: this.email, + middle_name: this.middleName, + suffix: this.suffix, + date_of_birth: this.dateOfBirth, + contact_preference: this.contactPreference, + gender: this.gender, + education: this.education, + occupation: this.occupation, + }, + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully created client with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/riskadvisor/actions/create-risk-profile/create-risk-profile.mjs b/components/riskadvisor/actions/create-risk-profile/create-risk-profile.mjs new file mode 100644 index 0000000000000..a863a4e80955c --- /dev/null +++ b/components/riskadvisor/actions/create-risk-profile/create-risk-profile.mjs @@ -0,0 +1,51 @@ +import riskadvisor from "../../riskadvisor.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "riskadvisor-create-risk-profile", + name: "Create Risk Profile", + description: "Creates a risk profile in RiskAdvisor. [See the documentation](https://api.riskadvisor.insure/risk-profiles#create-a-risk-profile)", + version: "0.0.1", + type: "action", + props: { + riskadvisor, + insuranceType: { + type: "string", + label: "Insurance Type", + description: "The type of insurance", + options: constants.INSURANCE_TYPES, + }, + clientId: { + propDefinition: [ + riskadvisor, + "clientId", + ], + description: "Unique identifier for the client in the risk profile.", + }, + coClientId: { + propDefinition: [ + riskadvisor, + "clientId", + ], + label: "Co-Client", + description: "Unique identifier for the other client in the risk profile.", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.riskadvisor.createRiskProfile({ + data: { + insurance_type: this.insuranceType, + client_id: this.clientId, + co_client_id: this.coClientId, + }, + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully created risk profile with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/riskadvisor/actions/update-client/update-client.mjs b/components/riskadvisor/actions/update-client/update-client.mjs new file mode 100644 index 0000000000000..345775989ce4b --- /dev/null +++ b/components/riskadvisor/actions/update-client/update-client.mjs @@ -0,0 +1,131 @@ +import riskadvisor from "../../riskadvisor.app.mjs"; +import lodash from "lodash"; + +export default { + key: "riskadvisor-update-client", + name: "Update Client", + description: "Updates an existing in RiskAdvisor. [See the documentation](https://api.riskadvisor.insure/clients#update-a-client)", + version: "0.0.1", + type: "action", + props: { + riskadvisor, + clientId: { + propDefinition: [ + riskadvisor, + "clientId", + ], + }, + firstName: { + propDefinition: [ + riskadvisor, + "firstName", + ], + optional: true, + }, + lastName: { + propDefinition: [ + riskadvisor, + "lastName", + ], + optional: true, + }, + phoneNumber: { + propDefinition: [ + riskadvisor, + "phoneNumber", + ], + optional: true, + }, + email: { + propDefinition: [ + riskadvisor, + "email", + ], + optional: true, + }, + middleName: { + propDefinition: [ + riskadvisor, + "middleName", + ], + }, + suffix: { + propDefinition: [ + riskadvisor, + "suffix", + ], + }, + dateOfBirth: { + propDefinition: [ + riskadvisor, + "dateOfBirth", + ], + }, + contactPreference: { + propDefinition: [ + riskadvisor, + "contactPreference", + ], + }, + gender: { + propDefinition: [ + riskadvisor, + "gender", + ], + }, + education: { + propDefinition: [ + riskadvisor, + "education", + ], + }, + occupation: { + propDefinition: [ + riskadvisor, + "occupation", + ], + }, + }, + methods: { + // getClient endpoint "/api/clients/:id" not yet implemented in the RiskAdvisor API + // todo: update to use getClient endpoint when implemented + async getClient(clientId) { + const clients = this.riskadvisor.paginate({ + resourceFn: this.riskadvisor.listClients, + }); + + for await (const client of clients) { + if (client.id === clientId) { + return client; + } + } + }, + }, + async run({ $ }) { + const client = await this.getClient(this.clientId); + + const response = await this.riskadvisor.updateClient({ + clientId: this.clientId, + data: lodash.pickBy({ + first_name: this.firstName || client.first_name, + last_name: this.lastName || client.last_name, + phone_number: this.phoneNumber || client.phone_number, + email: this.email || client.email, + middle_name: this.middleName, + suffix: this.suffix, + date_of_birth: this.dateOfBirth, + contact_preference: this.contactPreference, + gender: this.gender, + education: this.education, + occupation: this.occupation, + }), + $, + }); + + if (response?.id) { + $.export("$summary", `Successfully updated client with ID ${response.id}.`); + } + + return response; + }, +}; diff --git a/components/riskadvisor/common/constants.mjs b/components/riskadvisor/common/constants.mjs new file mode 100644 index 0000000000000..426f08a0a9919 --- /dev/null +++ b/components/riskadvisor/common/constants.mjs @@ -0,0 +1,13 @@ +const DEFAULT_LIMIT = 25; + +const INSURANCE_TYPES = [ + "home", + "auto", + "umbrella", + "home_auto", +]; + +export default { + DEFAULT_LIMIT, + INSURANCE_TYPES, +}; diff --git a/components/riskadvisor/package.json b/components/riskadvisor/package.json new file mode 100644 index 0000000000000..a49007109b25d --- /dev/null +++ b/components/riskadvisor/package.json @@ -0,0 +1,19 @@ +{ + "name": "@pipedream/riskadvisor", + "version": "0.1.0", + "description": "Pipedream RiskAdvisor Components", + "main": "riskadvisor.app.mjs", + "keywords": [ + "pipedream", + "riskadvisor" + ], + "homepage": "https://pipedream.com/apps/riskadvisor", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "lodash": "^4.17.21" + } +} diff --git a/components/riskadvisor/riskadvisor.app.mjs b/components/riskadvisor/riskadvisor.app.mjs new file mode 100644 index 0000000000000..df527bbc55dc2 --- /dev/null +++ b/components/riskadvisor/riskadvisor.app.mjs @@ -0,0 +1,175 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + +export default { + type: "app", + app: "riskadvisor", + propDefinitions: { + clientId: { + type: "integer", + label: "Client", + description: "Identifier of the client to update", + async options({ prevContext }) { + const params = { + limit: constants.DEFAULT_LIMIT, + }; + if (prevContext?.startingAfter) { + params.starting_after = prevContext.startingAfter; + } + const { data } = await this.listClients({ + params, + }); + const options = data?.map(({ + id: value, first_name, last_name, + }) => ({ + value, + label: `${first_name} ${last_name}`, + })) || []; + return { + options, + context: { + startingAfter: data?.length + ? data[data.length - 1].id + : null, + }, + }; + }, + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the client.", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the client.", + }, + phoneNumber: { + type: "string", + label: "Phone Number", + description: "The phone number for the client.", + }, + email: { + type: "string", + label: "Email", + description: "The email address of the client.", + }, + middleName: { + type: "string", + label: "Middle Name", + description: "The middle name of the client.", + optional: true, + }, + suffix: { + type: "string", + label: "Suffix", + description: "The suffix of the client.", + optional: true, + }, + dateOfBirth: { + type: "string", + label: "Date of Birth", + description: "The date of birth of the client.", + optional: true, + }, + contactPreference: { + type: "string", + label: "Contact Preference", + description: "The client's preferred contact method (i.e. phone or email).", + optional: true, + }, + gender: { + type: "string", + label: "Gender", + description: "The client's gender.", + optional: true, + }, + education: { + type: "string", + label: "Education", + description: "The client's education level.", + optional: true, + }, + occupation: { + type: "string", + label: "Occupation", + description: "The client's occupation.", + optional: true, + }, + }, + methods: { + _baseUrl() { + return "https://app.riskadvisor.insure/api"; + }, + _headers() { + return { + Authorization: `Bearer ${this.$auth.api_token}`, + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + listClients(args = {}) { + return this._makeRequest({ + path: "/clients", + ...args, + }); + }, + createClient(args = {}) { + return this._makeRequest({ + path: "/clients", + method: "POST", + ...args, + }); + }, + createRiskProfile(args = {}) { + return this._makeRequest({ + path: "/risk-profiles", + method: "POST", + ...args, + }); + }, + updateClient({ + clientId, ...args + }) { + return this._makeRequest({ + path: `/clients/${clientId}`, + method: "PUT", + ...args, + }); + }, + async *paginate({ + resourceFn, + args = {}, + }) { + args = { + ...args, + params: { + ...args.params, + limit: constants.DEFAULT_LIMIT, + }, + }; + let total = 0; + do { + const { data } = await resourceFn(args); + if (!data?.length) { + return; + } + for (const item of data) { + yield item; + } + total = data.length; + args.params.starting_after = data[total - 1].id; + } while (total === args.params.limit); + }, + }, +}; diff --git a/components/riskadvisor/sources/client-updated/client-updated.mjs b/components/riskadvisor/sources/client-updated/client-updated.mjs new file mode 100644 index 0000000000000..d24a5772bd22d --- /dev/null +++ b/components/riskadvisor/sources/client-updated/client-updated.mjs @@ -0,0 +1,28 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "riskadvisor-client-updated", + name: "Client Updated", + description: "Emit new event each time a client is updated in RiskAdvisor. [See the documentation](https://api.riskadvisor.insure/clients#list-all-clients)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTsField() { + return "updated_at"; + }, + getResourceFn() { + return this.riskadvisor.listClients; + }, + generateMeta(client) { + const ts = Date.parse(client[this.getTsField()]); + return { + id: `${client.id}${ts}`, + summary: `Client "${client.first_name} ${client.last_name}" Updated`, + ts, + }; + }, + }, +}; diff --git a/components/riskadvisor/sources/common/base.mjs b/components/riskadvisor/sources/common/base.mjs new file mode 100644 index 0000000000000..e5a5bc1929a1c --- /dev/null +++ b/components/riskadvisor/sources/common/base.mjs @@ -0,0 +1,66 @@ +import riskadvisor from "../../riskadvisor.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + riskadvisor, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + await this.processEvents(25); + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getTsField() { + throw new Error("getTsField is not implemented"); + }, + gettResourceFn() { + throw new Error("gettResourceFn is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + async processEvents(max) { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + + const items = this.riskadvisor.paginate({ + resourceFn: this.getResourceFn(), + }); + + let count = 0; + for await (const item of items) { + const ts = Date.parse(item[this.getTsField()]); + if (ts > lastTs) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + if (ts > maxTs) { + maxTs = ts; + } + count++; + if (max && count >= max) { + break; + } + } + } + + this._setLastTs(maxTs); + }, + }, + async run() { + await this.processEvents(); + }, +}; diff --git a/components/riskadvisor/sources/new-client-created/new-client-created.mjs b/components/riskadvisor/sources/new-client-created/new-client-created.mjs new file mode 100644 index 0000000000000..e6b60ac6af4da --- /dev/null +++ b/components/riskadvisor/sources/new-client-created/new-client-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "riskadvisor-new-client-created", + name: "New Client Created", + description: "Emit new event each time a new client is created in RiskAdvisor. [See the documentation](https://api.riskadvisor.insure/clients#list-all-clients)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTsField() { + return "created_at"; + }, + getResourceFn() { + return this.riskadvisor.listClients; + }, + generateMeta(client) { + return { + id: client.id, + summary: `Client "${client.first_name} ${client.last_name}" Created`, + ts: Date.parse(client[this.getTsField()]), + }; + }, + }, +}; diff --git a/components/salesmsg/.gitignore b/components/salesmsg/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/salesmsg/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/salesmsg/actions/create-new-contact/create-new-contact.mjs b/components/salesmsg/actions/create-new-contact/create-new-contact.mjs new file mode 100644 index 0000000000000..aab634fdcd7f9 --- /dev/null +++ b/components/salesmsg/actions/create-new-contact/create-new-contact.mjs @@ -0,0 +1,79 @@ +import salesmsg from "../../salesmsg.app.mjs"; + +export default { + key: "salesmsg-create-new-contact", + name: "Create New Contact", + version: "0.0.1", + description: "Create a new contact. [See the documentation](https://documenter.getpostman.com/view/13798866/2s935uHgXp#57f25fd9-2de8-4c9c-97f4-79bf7a6eb255)", + type: "action", + props: { + salesmsg, + number: { + type: "string", + label: "Number", + description: "The phone number of the contact.", + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the contact.", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the contact.", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The email of the contact.", + optional: true, + }, + colorIndex: { + type: "string", + label: "Color Index", + description: "The color index of the contact.", + optional: true, + }, + contactIntegrationId: { + type: "string", + label: "Contact Integration Id", + description: "The integration id of the contact.", + optional: true, + }, + phoneType: { + type: "string", + label: "Phone Type", + description: "The type of the phone number. E.g. **phone, etc**", + optional: true, + }, + }, + async run({ $ }) { + const { + salesmsg, + firstName, + lastName, + colorIndex, + contactIntegrationId, + phoneType, + ...params + } = this; + + const response = await salesmsg.createContact({ + $, + params: { + first_name: firstName, + last_name: lastName, + color_index: colorIndex, + contact_integration_id: contactIntegrationId, + phone_type: phoneType, + ...params, + }, + }); + + $.export("$summary", `A new contact with Id: ${response.id} was successfully created!`); + return response; + }, +}; diff --git a/components/salesmsg/actions/get-user-conversations/get-user-conversations.mjs b/components/salesmsg/actions/get-user-conversations/get-user-conversations.mjs new file mode 100644 index 0000000000000..ccc0c6a22c229 --- /dev/null +++ b/components/salesmsg/actions/get-user-conversations/get-user-conversations.mjs @@ -0,0 +1,27 @@ +import salesmsg from "../../salesmsg.app.mjs"; + +export default { + key: "salesmsg-get-user-conversations", + name: "Get User Conversations", + version: "0.0.1", + description: "Retrieves the list of all the conversations of a specific contact id. [See the documentation](https://documenter.getpostman.com/view/13798866/2s935uHgXp#27602c5c-e171-4009-b11c-3d19904d7dcd)", + type: "action", + props: { + salesmsg, + }, + async run({ $ }) { + const items = this.salesmsg.paginate({ + fn: this.salesmsg.listConversations, + }); + const responseArray = []; + + for await (const item of items) { + responseArray.push(item); + } + + $.export("$summary", `${responseArray.length} conversation${responseArray.length > 1 + ? "s were" + : " was"} successfully fetched!`); + return responseArray; + }, +}; diff --git a/components/salesmsg/actions/search-conversations/search-conversations.mjs b/components/salesmsg/actions/search-conversations/search-conversations.mjs new file mode 100644 index 0000000000000..347004db5eb75 --- /dev/null +++ b/components/salesmsg/actions/search-conversations/search-conversations.mjs @@ -0,0 +1,48 @@ +import salesmsg from "../../salesmsg.app.mjs"; + +export default { + key: "salesmsg-search-conversations", + name: "Search Conversations", + version: "0.0.1", + description: "Search active conversations in conversation history. [See the documentation](https://documenter.getpostman.com/view/13798866/2s935uHgXp#c9f078a9-931a-45c0-81c7-14d2b745ca5b)", + type: "action", + props: { + salesmsg, + term: { + type: "string", + label: "Term", + description: "Search term (first/last name, email, number, formatted_number, tag)", + }, + type: { + type: "string", + label: "Type", + description: "Define search type.", + options: [ + "contacts", + "messages", + ], + optional: true, + }, + }, + async run({ $ }) { + const { + salesmsg, + ...params + } = this; + const items = salesmsg.paginate({ + fn: salesmsg.searchConversations, + perPage: true, + params, + }); + const responseArray = []; + + for await (const item of items) { + responseArray.push(item); + } + + $.export("$summary", `${responseArray.length} conversation${responseArray.length > 1 + ? "s were" + : " was"} successfully fetched!`); + return responseArray; + }, +}; diff --git a/components/salesmsg/app/salesmsg.app.ts b/components/salesmsg/app/salesmsg.app.ts deleted file mode 100644 index 73913c8762f2a..0000000000000 --- a/components/salesmsg/app/salesmsg.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "salesmsg", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/salesmsg/common/contants.mjs b/components/salesmsg/common/contants.mjs new file mode 100644 index 0000000000000..ea830c15a04cb --- /dev/null +++ b/components/salesmsg/common/contants.mjs @@ -0,0 +1 @@ +export const LIMIT = 100; diff --git a/components/salesmsg/package.json b/components/salesmsg/package.json index ec3dfc8eef9e4..145321d086e29 100644 --- a/components/salesmsg/package.json +++ b/components/salesmsg/package.json @@ -1,16 +1,19 @@ { "name": "@pipedream/salesmsg", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream Salesmsg Components", - "main": "dist/app/salesmsg.app.mjs", + "main": "salesmsg.app.mjs", "keywords": [ "pipedream", "salesmsg" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/salesmsg", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } } + diff --git a/components/salesmsg/salesmsg.app.mjs b/components/salesmsg/salesmsg.app.mjs new file mode 100644 index 0000000000000..040a828076c95 --- /dev/null +++ b/components/salesmsg/salesmsg.app.mjs @@ -0,0 +1,142 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/contants.mjs"; + +export default { + type: "app", + app: "salesmsg", + propDefinitions: { + contactId: { + type: "string", + label: "Contact Id", + description: "The id of the contact you want to fetch messages.", + async options({ page }) { + const { data } = await this.listContacts({ + params: { + page: page + 1, + }, + }); + + return data.map(({ + id: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + }, + methods: { + _apiUrl() { + return "https://api.salesmessage.com/pub/v2.1"; + }, + _getHeaders() { + return { + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + headers: this._getHeaders(), + ...opts, + }; + + return axios($, config); + }, + createContact(args = {}) { + return this._makeRequest({ + method: "POST", + path: "contacts", + ...args, + }); + }, + listContacts(args = {}) { + return this._makeRequest({ + path: "contacts", + ...args, + }); + }, + listConversations(args = {}) { + return this._makeRequest({ + path: "conversations", + ...args, + }); + }, + listMessages(args = {}) { + return this._makeRequest({ + path: "messages/contacts", + ...args, + }); + }, + searchConversations(args = {}) { + return this._makeRequest({ + path: "conversations/search", + ...args, + }); + }, + async *paginate({ + fn, params = { + filter: "open", + }, maxResults = null, perPage = false, + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + if (perPage) { + params.page = ++page; + } else { + params.limit = LIMIT; + params.offset = LIMIT * page; + page++; + } + const response = await fn({ + params, + }); + const data = response.data || response.results || response; + + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); + }, + async *paginateMessages({ + maxResults = null, params, + }) { + let hasMore = false; + let count = 0; + let before = false; + + do { + params.limit = LIMIT; + if (before) params.before = before; + + const data = await this.listMessages({ + params, + }); + + for (const d of data) { + yield d; + before = d.id; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); + }, + }, +}; diff --git a/components/salesmsg/sources/contact-created/contact-created.mjs b/components/salesmsg/sources/contact-created/contact-created.mjs new file mode 100644 index 0000000000000..645405991612d --- /dev/null +++ b/components/salesmsg/sources/contact-created/contact-created.mjs @@ -0,0 +1,79 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import salesmsg from "../../salesmsg.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "salesmsg-contact-created", + name: "New Contact Created", + version: "0.0.1", + description: "Emit new event when a new contact is created.", + type: "source", + dedupe: "unique", + props: { + salesmsg, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Salesmsg API on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || "1970-01-01"; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async startEvent(maxResults = 0) { + + const lastDate = this._getLastDate(); + const items = this.salesmsg.paginate({ + fn: this.salesmsg.listContacts, + maxResults, + perPage: true, + params: { + "sortBy": "created_at", + "sortOrder": "desc", + "filtersList[0][filters][0][key]": "created_at", + "filtersList[0][filters][0][operator]": "is_after", + "filtersList[0][filters][0][value]": lastDate, + "filtersList[1][filters][0][key]": "created_at", + "filtersList[1][filters][0][operator]": "is_equal_to", + "filtersList[1][filters][0][value]": lastDate, + }, + }); + + let responseArray = []; + + for await (const item of items) { + responseArray.push(item); + } + + if (responseArray.length) this._setLastDate(responseArray[0].created_at); + + for (const item of responseArray.reverse()) { + this.$emit( + item, + { + id: item.id, + summary: `A new contact with id: "${item.id}" was created!`, + ts: item.created_at, + }, + ); + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/salesmsg/sources/contact-created/test-event.mjs b/components/salesmsg/sources/contact-created/test-event.mjs new file mode 100644 index 0000000000000..3f39806c3b490 --- /dev/null +++ b/components/salesmsg/sources/contact-created/test-event.mjs @@ -0,0 +1,138 @@ +export default { + "id": 8096796, + "owner_id": -47116527, + "color": "nulla in do", + "first_name": "sit reprehenderit magna voluptate ea", + "last_name": "est d", + "full_name": "laborum", + "email": "in elit cupidatat", + "photo_url": "cillum non", + "number": "sunt cillum", + "country": "commodo nostrud anim ex", + "country_calling_code": "officia qui incididunt et", + "national_number": "voluptate ullamco mollit nisi", + "typeted_number": "enim sint culpa a", + "is_blocked": true, + "opt_out": true, + "opt_out_at": "deserunt cillum aliqua elit", + "opt_in_request_at": "officia quis", + "opt_in_requests": [ + { + "number_id": 51439264 + }, + { + "number_id": -84034005 + } + ], + "opt_in_at": "ea sed proident irure fugiat", + "shortcode_opt_outs": [ + { + "id": -7791621, + "number_id": -41795645, + "name": "anim officia sint ullamco", + "disabled": false, + "timestamp": "incididunt ut", + "value": false + }, + { + "id": 59950039, + "number_id": 3565041, + "name": "anim dolore tempor fugiat ipsum", + "disabled": false, + "timestamp": "ex id anim sint sit", + "value": false + } + ], + "created_at": "in enim incididunt ex cupidatat", + "updated_at": "proident do ut", + "integration_vendor_id": 51464717, + "prevent_autolink": false, + "integration": [ + { + "id": 6265397, + "key": "ullamco", + "name": "dolor", + "photo_url": "qui sit enim dolor elit" + }, + { + "id": -99777723, + "key": "ad", + "name": "anim ut reprehenderit", + "photo_url": "dolor in mollit in" + } + ], + "custom_fields": [ + { + "id": -53193119, + "custom_field_id": -34819703, + "contact_id": 33742889, + "field_key": "mollit elit ea", + "organization_id": -35104886, + "text": "sint aliquip", + "number": 89536031, + "date": "nisi laborum sunt", + "created_at": "eiusmod sunt", + "updated_at": "a", + "value": [ + { + "type": "Ut consectetur deserunt", + "value": "ullamco" + }, + { + "type": "et Ut quis", + "value": "non nost" + } + ] + }, + { + "id": -20588006, + "custom_field_id": -78052965, + "contact_id": 20220698, + "field_key": "officia labore", + "organization_id": 15933253, + "text": "Excepteur", + "number": -20577886, + "date": "in sit temp", + "created_at": "in amet exercitation", + "updated_at": "sunt dolor Excepteur in pariatur", + "value": [ + { + "type": "commodo sed", + "value": "mollit eiusmod dolor" + }, + { + "type": "laborum in proident et occaecat", + "value": "Lorem in dolor" + } + ] + } + ], + "tags": [ + { + "id": -30102850, + "name": "irure amet Ut deserunt", + "label": "voluptate id", + "created_at": "dolore consectetur ipsum adipisicing" + }, + { + "id": -54598851, + "name": "occaecat do sed id", + "label": "ex qui tempor", + "created_at": "veniam eu labore irure ex" + } + ], + "notes": [ + { + "id": -95347179, + "text": "irure mollit", + "created_at": "L", + "updated_at": "mollit pariatur ipsum aliqua" + }, + { + "id": -3515187, + "text": "veniam voluptate sed", + "created_at": "quis eiusmod est occaecat", + "updated_at": "ullamco dolor voluptate reprehenderit" + } + ] +} \ No newline at end of file diff --git a/components/salesmsg/sources/message-received/message-received.mjs b/components/salesmsg/sources/message-received/message-received.mjs new file mode 100644 index 0000000000000..09667d2016dbc --- /dev/null +++ b/components/salesmsg/sources/message-received/message-received.mjs @@ -0,0 +1,79 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import salesmsg from "../../salesmsg.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "salesmsg-message-received", + name: "New Message Received", + version: "0.0.1", + description: "Emit new event when a new message is received.", + type: "source", + dedupe: "unique", + props: { + salesmsg, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Salesmsg API on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + contactId: { + propDefinition: [ + salesmsg, + "contactId", + ], + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async startEvent(maxResults = 0) { + + const lastId = this._getLastId(); + const items = this.salesmsg.paginateMessages({ + maxResults, + params: { + contacts: [ + this.contactId, + ], + }, + }); + + let responseArray = []; + + for await (const item of items) { + if (item.id <= lastId) break; + responseArray.push(item); + + if (responseArray.length) this._setLastId(responseArray[0].id); + + for (const item of responseArray.reverse()) { + this.$emit( + item, + { + id: item.id, + summary: `A new message with id: "${item.id}" was received!`, + ts: item.created_at, + }, + ); + } + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/salesmsg/sources/message-received/test-event.mjs b/components/salesmsg/sources/message-received/test-event.mjs new file mode 100644 index 0000000000000..cbd959a2caed7 --- /dev/null +++ b/components/salesmsg/sources/message-received/test-event.mjs @@ -0,0 +1,310 @@ +export default { + "id": 75553107, + "conversation_id": 44589606, + "automated": false, + "status": "consequat ut laboris nostrud", + "type": "laboris", + "mms_status": "esse non qui anim commodo", + "body": "reprehenderit occaecat sed", + "body_raw": "dolore aliquip officia", + "has_tagging": true, + "icon": "sit velit consequat", + "received_at": "Ut aliquip", + "send_at": "ea enim", + "queued_at": "ut tempor in est Lorem", + "sent_at": "mollit nostrud consequat sunt", + "delivered_at": "esse nulla nisi", + "failed_at": "occaecat ut", + "failed_reason": "in nisi fugiat", + "created_at": "ipsum et", + "stop_on_response": true, + "record": { + "id": 19922617, + "message_id": 35431026, + "sid": "nulla eiusmod", + "record_id": "aliqua ad aute", + "duration": -56162112, + "status": "Ut", + "url": "do", + "is_voicemail": false, + "created_at": "laborum ut Lorem amet veniam", + "updated_at": "aliqua ea commodo deserunt" + }, + "user_id": -72894126, + "source_id": -22199754, + "source": "Excepteur non", + "contact": { + "id": 6377298, + "owner_id": -76613515, + "color": "dolor aute", + "first_name": "elit labore nulla", + "last_name": "incididunt ut in est pariatur", + "full_name": "commodo sed dolor", + "email": "labore voluptate Ut", + "photo_url": "dolor minim eu tempor", + "number": "nulla elit temp", + "country": "exercitation velit", + "country_calling_code": "officia proident sit aliqua", + "national_number": "ullamco proident", + "typeted_number": "Excepteur enim nulla culpa", + "is_blocked": true, + "opt_out": true, + "opt_out_at": "quis", + "opt_in_request_at": "dolore anim", + "opt_in_requests": [ + { + "number_id": 18495245 + }, + { + "number_id": -14355354 + } + ], + "opt_in_at": "sunt", + "shortcode_opt_outs": [ + { + "id": -4060950, + "number_id": -98702053, + "name": "Duis dolore", + "disabled": true, + "timestamp": "eiusmod", + "value": false + }, + { + "id": -84142728, + "number_id": -30431253, + "name": "mollit nulla al", + "disabled": false, + "timestamp": "Ut dolor", + "value": false + } + ], + "created_at": "cil", + "updated_at": "sunt esse id dolo", + "integration_vendor_id": -15929749, + "prevent_autolink": false, + "integration": [ + { + "id": -5353513, + "key": "ad sit non", + "name": "tempor", + "photo_url": "nisi in aute in dolore" + }, + { + "id": 37753786, + "key": "elit eiusmod", + "name": "laboris", + "photo_url": "nostrud" + } + ], + "custom_fields": [ + { + "id": -65349264, + "custom_field_id": -99623380, + "contact_id": -52788908, + "field_key": "consectetur magna deserunt ut", + "organization_id": -67386526, + "text": "esse ut id veniam", + "number": -68242981, + "date": "qui enim officia", + "created_at": "incididunt", + "updated_at": "quis qui in c", + "value": [ + { + "type": { + "value": "" + }, + "value": { + "value": "" + } + }, + { + "type": { + "value": "" + }, + "value": { + "value": "" + } + } + ] + }, + { + "id": 75257821, + "custom_field_id": 82354475, + "contact_id": -10177506, + "field_key": "laborum eiusmod fugiat", + "organization_id": 76089518, + "text": "Duis aute ", + "number": -76134365, + "date": "Duis commodo veniam", + "created_at": "Duis ad ut pariatur dolore", + "updated_at": "consectetur Duis proident ex", + "value": [ + { + "type": { + "value": "" + }, + "value": { + "value": "" + } + }, + { + "type": { + "value": "" + }, + "value": { + "value": "" + } + } + ] + } + ], + "tags": [ + { + "id": -47064806, + "name": "s", + "label": "cupidatat proident nisi", + "created_at": "nostrud magna eiusmod" + }, + { + "id": -54029135, + "name": "enim c", + "label": "Excepteur consectetur qui qui", + "created_at": "velit ullamco" + } + ], + "notes": [ + { + "id": -92166232, + "text": "enim aute", + "created_at": "est ea", + "updated_at": "commodo ut reprehenderit dolor" + }, + { + "id": -79968630, + "text": "venia", + "created_at": "sit", + "updated_at": "in" + } + ] + }, + "user": { + "id": 16305270, + "email": "consecte", + "first_name": "nulla commodo", + "last_name": "exercitation enim ut eiusmod", + "full_name": "adipisicing incididunt sint", + "role": "aute reprehende", + "inbox_id": 94923440, + "organization_id": -98424672, + "photo_url": "amet cupidatat laborum", + "unread": 67604163, + "number": "ullamco non minim Excepteur culpa", + "formatted_number": "dolor", + "organizationNumber": [ + { + "id": 27486777, + "country": "est anim", + "number": "consequat laboris dolor", + "formatted_number": "minim pariatur enim", + "mms": "Lorem deserunt Duis sint" + }, + { + "id": -1487747, + "country": "ullamco dolor", + "number": "incididunt", + "formatted_number": "dolor do Duis", + "mms": "eu" + } + ], + "userPermissions": [ + "laboris minim", + "veniam et" + ] + }, + "pending_mentions": [ + { + "id": 25912443, + "email": "ut e", + "first_name": "ullamco et cupidatat", + "last_name": "exercitation dolore velit tempor", + "full_name": "adipisicing ex mollit", + "role": "nulla", + "inbox_id": 86281885, + "organization_id": 35788264, + "photo_url": "cupidatat exercitation", + "unread": -42215402, + "number": "culpa id ea ul", + "formatted_number": "do fugiat dolore aute id", + "organizationNumber": [ + { + "id": 79122038, + "country": "ipsum non aute", + "number": "sint ut pariatur", + "formatted_number": "qui Lorem id sint", + "mms": "nulla deserunt veniam dolore" + }, + { + "id": -46711595, + "country": "non do ad exercitation enim", + "number": "ipsum quis esse qui aliquip", + "formatted_number": "qui labore sit", + "mms": "laborum aliqua consequat" + } + ], + "userPermissions": [ + "non consectetur est cillum", + "amet nisi pariatur" + ] + }, + { + "id": -12713288, + "email": "cillum", + "first_name": "occaecat ex", + "last_name": "dolore anim occaecat Ut ", + "full_name": "laboris deser", + "role": "amet", + "inbox_id": 68589746, + "organization_id": 35521166, + "photo_url": "eiusmod nisi dolor commodo", + "unread": -86629415, + "number": "aliqua consequat", + "formatted_number": "do cupidatat est", + "organizationNumber": [ + { + "id": 15818242, + "country": "nulla irure anim", + "number": "ex irure", + "formatted_number": "d", + "mms": "officia laboris est aliqu" + }, + { + "id": 63349133, + "country": "ipsum adipisicing esse", + "number": "nisi velit", + "formatted_number": "sed fugiat", + "mms": "laboris pariatur amet velit" + } + ], + "userPermissions": [ + "ipsum ut", + "velit proident quis occaecat" + ] + } + ], + "media": [ + { + "id": -72936924, + "content_type": "consectetur reprehenderit ir", + "source": "non nulla incididunt consect", + "source_short": "dolor ea", + "created_at": "elit ad consequat" + }, + { + "id": 11474746, + "content_type": "consectetur tempor esse", + "source": "commodo elit voluptate", + "source_short": "proident ad velit ma", + "created_at": "anim incididunt ea sed et" + } + ] +} \ No newline at end of file diff --git a/components/satismeter/package.json b/components/satismeter/package.json new file mode 100644 index 0000000000000..1f884f18d1f77 --- /dev/null +++ b/components/satismeter/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/satismeter", + "version": "0.0.1", + "description": "Pipedream SatisMeter Components", + "main": "satismeter.app.mjs", + "keywords": [ + "pipedream", + "satismeter" + ], + "homepage": "https://pipedream.com/apps/satismeter", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/satismeter/satismeter.app.mjs b/components/satismeter/satismeter.app.mjs new file mode 100644 index 0000000000000..b90bb1c158cfb --- /dev/null +++ b/components/satismeter/satismeter.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "satismeter", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/signable/package.json b/components/signable/package.json new file mode 100644 index 0000000000000..a1351f149940f --- /dev/null +++ b/components/signable/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/signable", + "version": "0.0.1", + "description": "Pipedream Signable Components", + "main": "signable.app.mjs", + "keywords": [ + "pipedream", + "signable" + ], + "homepage": "https://pipedream.com/apps/signable", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/signable/signable.app.mjs b/components/signable/signable.app.mjs new file mode 100644 index 0000000000000..9d7374e4412c2 --- /dev/null +++ b/components/signable/signable.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "signable", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/signalwire/package.json b/components/signalwire/package.json new file mode 100644 index 0000000000000..f892241cc7190 --- /dev/null +++ b/components/signalwire/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/signalwire", + "version": "0.0.1", + "description": "Pipedream SignalWire Components", + "main": "signalwire.app.mjs", + "keywords": [ + "pipedream", + "signalwire" + ], + "homepage": "https://pipedream.com/apps/signalwire", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/signalwire/signalwire.app.mjs b/components/signalwire/signalwire.app.mjs new file mode 100644 index 0000000000000..54d8da2c11ad9 --- /dev/null +++ b/components/signalwire/signalwire.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "signalwire", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/slottable/package.json b/components/slottable/package.json index d361d883be117..5b7020934afe3 100644 --- a/components/slottable/package.json +++ b/components/slottable/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/slottable", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Slottable Components", "main": "slottable.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/slottable/slottable.app.mjs b/components/slottable/slottable.app.mjs index c9dc9f11a6756..3fe7271c55880 100644 --- a/components/slottable/slottable.app.mjs +++ b/components/slottable/slottable.app.mjs @@ -1,11 +1,54 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "slottable", propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://slottable.app/api/v1"; + }, + _headers() { + return { + "Authorization": `Bearer ${this.$auth.api_token}`, + "Accept": "application/json", + "Content-Type": "application/json", + }; + }, + _makeRequest({ + $ = this, + path, + ...args + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: this._headers(), + ...args, + }); + }, + async getCompanyId() { + const { data: { attributes: { company_id } } } = await this._makeRequest({ + path: "/token", + }); + return company_id; + }, + createWebhook({ + companyId, ...args + }) { + return this._makeRequest({ + path: `/companies/${companyId}/webhooks`, + method: "POST", + ...args, + }); + }, + deleteWebhook({ + companyId, hookId, ...args + }) { + return this._makeRequest({ + path: `/companies/${companyId}/webhooks/${hookId}`, + method: "DELETE", + ...args, + }); }, }, }; diff --git a/components/slottable/sources/booking-contact-updated/booking-contact-updated.mjs b/components/slottable/sources/booking-contact-updated/booking-contact-updated.mjs new file mode 100644 index 0000000000000..c3a8b7cc35593 --- /dev/null +++ b/components/slottable/sources/booking-contact-updated/booking-contact-updated.mjs @@ -0,0 +1,25 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "slottable-booking-contact-updated", + name: "Booking Contact Updated", + version: "0.0.1", + description: "Emit new event when a booking contact is changed (new, updated, or deleted) in Slottable.", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getModel() { + return "BookingContact"; + }, + generateMeta(contact) { + const ts = Date.parse(contact.updated_at); + return { + id: `${contact.id}-${ts}`, + summary: `Booking Contact Updated with ID ${contact.id}`, + ts, + }; + }, + }, +}; diff --git a/components/slottable/sources/common/base.mjs b/components/slottable/sources/common/base.mjs new file mode 100644 index 0000000000000..6ba36e6e1e6ca --- /dev/null +++ b/components/slottable/sources/common/base.mjs @@ -0,0 +1,53 @@ +import slottable from "../../slottable.app.mjs"; + +export default { + props: { + slottable, + db: "$.service.db", + http: "$.interface.http", + }, + hooks: { + async activate() { + const companyId = await this.slottable.getCompanyId(); + const { data: { id } } = await this.slottable.createWebhook({ + companyId, + data: { + url: this.http.endpoint, + method: "post", + model: this.getModel(), + events: "changed", + }, + }); + this._setHookId(id); + }, + async deactivate() { + const companyId = await this.slottable.getCompanyId(); + const hookId = this._getHookId(); + if (hookId) { + await this.slottable.deleteWebhook({ + companyId, + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + getModel() { + throw new Error("getModel is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run(event) { + const { body } = event; + const meta = this.generateMeta(body.data); + this.$emit(body, meta); + }, +}; diff --git a/components/slottable/sources/contact-updated/contact-updated.mjs b/components/slottable/sources/contact-updated/contact-updated.mjs new file mode 100644 index 0000000000000..865b84a1fa2e8 --- /dev/null +++ b/components/slottable/sources/contact-updated/contact-updated.mjs @@ -0,0 +1,25 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "slottable-contact-updated", + name: "Contact Updated", + version: "0.0.1", + description: "Emit new event when a contact is changed (new, updated, or deleted) in Slottable.", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getModel() { + return "Contact"; + }, + generateMeta(contact) { + const ts = Date.parse(contact.updated_at); + return { + id: `${contact.id}-${ts}`, + summary: `Contact Updated with ID ${contact.id}`, + ts, + }; + }, + }, +}; diff --git a/components/sonix/package.json b/components/sonix/package.json new file mode 100644 index 0000000000000..3e571ebe07f30 --- /dev/null +++ b/components/sonix/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/sonix", + "version": "0.0.1", + "description": "Pipedream Sonix Components", + "main": "sonix.app.mjs", + "keywords": [ + "pipedream", + "sonix" + ], + "homepage": "https://pipedream.com/apps/sonix", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/sonix/sonix.app.mjs b/components/sonix/sonix.app.mjs new file mode 100644 index 0000000000000..b6f537b7632e5 --- /dev/null +++ b/components/sonix/sonix.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "sonix", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/spotlightr/actions/create-video/create-video.mjs b/components/spotlightr/actions/create-video/create-video.mjs new file mode 100644 index 0000000000000..a1a0dc5aad8de --- /dev/null +++ b/components/spotlightr/actions/create-video/create-video.mjs @@ -0,0 +1,138 @@ +import { ConfigurationError } from "@pipedream/platform"; +import FormData from "form-data"; +import fs from "fs"; +import { checkTmp } from "../../common/utils.mjs"; +import spotlightr from "../../spotlightr.app.mjs"; + +export default { + key: "spotlightr-create-video", + name: "Create Video", + version: "0.0.1", + description: "Create a video in an application. [See the documentation](https://app.spotlightr.com/docs/api/#create-video)", + type: "action", + props: { + spotlightr, + url: { + type: "string", + label: "URL", + description: "The video URL. `You must provide at least the URL or File.`", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The name of the video.", + }, + customS3: { + type: "string", + label: "Custom S3", + description: "**0** or **ID** of custom integration.", + default: "0", + }, + hls: { + type: "integer", + label: "HLS", + description: "The media streaming protocol.", + options: [ + { + label: "To leave as unsecured.", + value: 0, + }, + { + label: "To encode.", + value: 1, + }, + ], + optional: true, + }, + videoGroup: { + propDefinition: [ + spotlightr, + "videoGroup", + ], + optional: true, + }, + create: { + type: "integer", + label: "Create", + description: "The type of the creation.", + options: [ + { + label: "To debug.", + value: 0, + }, + { + label: "To confirm.", + value: 1, + }, + ], + optional: true, + }, + playerSettings: { + type: "object", + label: "Player Settings", + description: "Video ID for coping playerSettings (decoded base64)", + optional: true, + }, + file: { + type: "string", + label: "File", + description: "Full path to the file in `/tmp/` directory. E.g. `/tmp/video.mp4`. `You must provide at least the URL or File.`", + optional: true, + }, + }, + async run({ $ }) { + const { + spotlightr, + url, + file, + ...data + } = this; + + if (!file && !url) { + throw new ConfigurationError("You must provide at least the URL or File."); + } + + let preparedData = {}; + if (file) { + const formData = new FormData(); + + const path = checkTmp(file); + if (!fs.existsSync(path)) { + throw new ConfigurationError("File does not exist!"); + } + + formData.append("file", fs.createReadStream(path)); + for (const [ + key, + value, + ] of Object.entries(data)) { + formData.append(key, value); + } + + preparedData = { + formDataRequest: true, + headers: formData.getHeaders(), + data: formData, + }; + + } else { + preparedData = { + data: { + URL: url, + ...data, + }, + }; + } + + const response = await spotlightr.createVideo({ + $, + ...preparedData, + }); + + $.export("$summary", `A new video with ${response.data + ? `URL: ${response.data}` + : `Id: ${response}`} was successfully created!`); + return response.data || response; + }, +}; diff --git a/components/spotlightr/actions/delete-videos/delete-videos.mjs b/components/spotlightr/actions/delete-videos/delete-videos.mjs new file mode 100644 index 0000000000000..00406ca58fa98 --- /dev/null +++ b/components/spotlightr/actions/delete-videos/delete-videos.mjs @@ -0,0 +1,35 @@ +import spotlightr from "../../spotlightr.app.mjs"; + +export default { + key: "spotlightr-delete-videos", + name: "Delete Videos", + version: "0.0.1", + description: "Delete videos from your account. [See the documentation](https://app.spotlightr.com/docs/api/#deleteVideo)", + type: "action", + props: { + spotlightr, + videoId: { + propDefinition: [ + spotlightr, + "videoId", + ], + type: "string[]", + }, + }, + async run({ $ }) { + const { + spotlightr, + videoId, + } = this; + + const response = await spotlightr.deleteVideo({ + $, + params: { + IDs: videoId, + }, + }); + + $.export("$summary", `The videos with Ids: ${videoId.toString()} were successfully deleted!`); + return response; + }, +}; diff --git a/components/spotlightr/common/utils.mjs b/components/spotlightr/common/utils.mjs new file mode 100644 index 0000000000000..dc2ed5a5be34d --- /dev/null +++ b/components/spotlightr/common/utils.mjs @@ -0,0 +1,6 @@ +export const checkTmp = (filename) => { + if (filename.indexOf("/tmp") === -1) { + return `/tmp/${filename}`; + } + return filename; +}; diff --git a/components/spotlightr/package.json b/components/spotlightr/package.json new file mode 100644 index 0000000000000..d61745dce68b8 --- /dev/null +++ b/components/spotlightr/package.json @@ -0,0 +1,20 @@ +{ + "name": "@pipedream/spotlightr", + "version": "0.1.0", + "description": "Pipedream Spotlightr Components", + "main": "spotlightr.app.mjs", + "keywords": [ + "pipedream", + "spotlightr" + ], + "homepage": "https://pipedream.com/apps/spotlightr", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "form-data": "^4.0.0", + "fs": "^0.0.1-security" + } +} diff --git a/components/spotlightr/sources/video-viewed/test-event.mjs b/components/spotlightr/sources/video-viewed/test-event.mjs new file mode 100644 index 0000000000000..23d5f7993327f --- /dev/null +++ b/components/spotlightr/sources/video-viewed/test-event.mjs @@ -0,0 +1,28 @@ +export default { + "id": 123123123, + "videoID": 123123123, + "date": "2023-10-24 17:37:20", + "cookie": "n7mqa33xk123123mtpmn", + "userID": 123123123, + "email": "", + "IP": "", + "phone": "", + "data": "", + "watchedSegments": "", + "percentWatched": 0, + "country": "", + "country_name": "", + "region_name": "", + "city": "", + "zip": "", + "latitude": "0.00000000", + "longitude": "0.00000000", + "currency_code": "", + "currency_name": "", + "device": "", + "browser": "", + "viewerID": 123123123, + "contactData": "", + "referer": "oembed5.cdn.spotlightr.com", + "customViewerID": "" +} \ No newline at end of file diff --git a/components/spotlightr/sources/video-viewed/video-viewed.mjs b/components/spotlightr/sources/video-viewed/video-viewed.mjs new file mode 100644 index 0000000000000..2af9cfdc6e0af --- /dev/null +++ b/components/spotlightr/sources/video-viewed/video-viewed.mjs @@ -0,0 +1,76 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import spotlightr from "../../spotlightr.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "spotlightr-video-viewed", + name: "New Video View", + version: "0.0.1", + description: "Emit new event when a specific video is viewed by a user.", + type: "source", + dedupe: "unique", + props: { + spotlightr, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Spotlightr on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + videoId: { + propDefinition: [ + spotlightr, + "videoId", + ], + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async startEvent(maxResults = 0) { + const { + spotlightr, + videoId, + } = this; + + const lastDate = this._getLastDate(); + const items = spotlightr.paginate({ + fn: spotlightr.getViews, + maxResults, + params: { + videoID: videoId, + }, + }); + + let responseArray = []; + + for await (const item of items) { + if (new Date(item.date) <= new Date(lastDate)) break; + responseArray.push(item); + } + if (responseArray.length) this._setLastDate(responseArray[0].created_time); + + for (const item of responseArray.reverse()) { + this.$emit( + item, + { + id: item.id, + summary: `New view with id: "${item.id}" was created!`, + ts: Date.parse(item.date), + }, + ); + } + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/spotlightr/spotlightr.app.mjs b/components/spotlightr/spotlightr.app.mjs new file mode 100644 index 0000000000000..86e0b3e4dd7b9 --- /dev/null +++ b/components/spotlightr/spotlightr.app.mjs @@ -0,0 +1,126 @@ +import axiosPipedream from "@pipedream/platform"; +import axios from "axios"; + +export default { + type: "app", + app: "spotlightr", + propDefinitions: { + videoGroup: { + type: "string", + label: "Video Group", + description: "The ID of the project.", + async options() { + const { data } = await this.listGroups(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + videoId: { + type: "string", + label: "Video Id", + description: "The ID of the video.", + async options() { + const { videos: { data } } = await this.listVideos(); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + }, + methods: { + _apiUrl() { + return "https://api.spotlightr.com/api"; + }, + _getApiKey() { + return this.$auth.api_key; + }, + _getParams(params = {}) { + return { + ...params, + "vooKey": this._getApiKey(), + }; + }, + _makeRequest({ + $ = this, params, path, formDataRequest = false, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + params: this._getParams(params), + ...opts, + }; + + if (formDataRequest) return axios(config); + return axiosPipedream.axios($, config); + }, + createVideo(args = {}) { + return this._makeRequest({ + method: "POST", + path: "createVideo", + ...args, + }); + }, + deleteVideo(args = {}) { + return this._makeRequest({ + method: "POST", + path: "deleteVideo", + ...args, + }); + }, + getViews(args = {}) { + return this._makeRequest({ + path: "views/getViews", + ...args, + }); + }, + listGroups(args = {}) { + return this._makeRequest({ + path: "groups", + ...args, + }); + }, + listVideos(args = {}) { + return this._makeRequest({ + path: "videos", + ...args, + }); + }, + async *paginate({ + fn, params = {}, maxResults = null, + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.page = ++page; + const { + data, + current_page: currentPage, + last_page: lastPage, + } = await fn({ + params, + }); + + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = currentPage != lastPage; + + } while (hasMore); + }, + }, +}; diff --git a/components/starloop/actions/list-ids/list-ids.mjs b/components/starloop/actions/list-ids/list-ids.mjs new file mode 100644 index 0000000000000..f907ab2a9b3cc --- /dev/null +++ b/components/starloop/actions/list-ids/list-ids.mjs @@ -0,0 +1,18 @@ +import app from "../../starloop.app.mjs"; + +export default { + key: "starloop-list-ids", + name: "List IDs", + description: "Returns your business id and a list of all profile id’s and names. [See the documentation](https://help.starloop.com/article/46-api-documentation)", + version: "0.0.1", + type: "action", + props: { + app, + }, + run({ $: step }) { + return this.app.listIds({ + step, + summary: () => "Successfully listed ids", + }); + }, +}; diff --git a/components/starloop/actions/send-invite/send-invite.mjs b/components/starloop/actions/send-invite/send-invite.mjs new file mode 100644 index 0000000000000..6f6d7e6810134 --- /dev/null +++ b/components/starloop/actions/send-invite/send-invite.mjs @@ -0,0 +1,80 @@ +import app from "../../starloop.app.mjs"; + +export default { + key: "starloop-send-invite", + name: "Send Invite", + description: "Creates a new recipient and sends a Starloop invite (Email | SMS | both) to your customer to leave a review. [See the documentation](https://help.starloop.com/article/46-api-documentation)", + version: "0.0.1", + type: "action", + props: { + app, + firstName: { + propDefinition: [ + app, + "firstName", + ], + }, + email: { + propDefinition: [ + app, + "email", + ], + }, + phone: { + propDefinition: [ + app, + "phone", + ], + }, + businessId: { + propDefinition: [ + app, + "businessId", + ], + }, + profileId: { + propDefinition: [ + app, + "profileId", + ], + }, + testMode: { + propDefinition: [ + app, + "testMode", + ], + }, + }, + methods: { + sendInvite(args = {}) { + return this.app.post({ + path: "/send_invite", + ...args, + }); + }, + }, + run({ $: step }) { + const { + sendInvite, + firstName, + email, + phone, + businessId, + profileId, + testMode, + } = this; + + return sendInvite({ + step, + params: { + first_name: firstName, + email, + phone, + business_id: businessId, + profile_id: profileId, + test_mode: testMode, + }, + summary: () => "Successfully sent invite", + }); + }, +}; diff --git a/components/starloop/common/constants.mjs b/components/starloop/common/constants.mjs new file mode 100644 index 0000000000000..ec8ced447a178 --- /dev/null +++ b/components/starloop/common/constants.mjs @@ -0,0 +1,13 @@ +const SUMMARY_LABEL = "$summary"; +const BASE_URL = "https://go.starloop.com"; +const VERSION_PATH = "/api"; +const LAST_CREATED_AT = "lastCreatedAt"; +const DEFAULT_MAX = 600; + +export default { + SUMMARY_LABEL, + BASE_URL, + VERSION_PATH, + DEFAULT_MAX, + LAST_CREATED_AT, +}; diff --git a/components/starloop/package.json b/components/starloop/package.json index b12b6fddb861b..f2bd8dfaf2bdc 100644 --- a/components/starloop/package.json +++ b/components/starloop/package.json @@ -1,7 +1,7 @@ { "name": "@pipedream/starloop", - "version": "0.0.1", - "description": "Pipedream Starloop Components", + "version": "0.1.0", + "description": "Pipedream Starloop Components", "main": "starloop.app.mjs", "keywords": [ "pipedream", @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/starloop/sources/new-profile-created/new-profile-created.mjs b/components/starloop/sources/new-profile-created/new-profile-created.mjs new file mode 100644 index 0000000000000..25f26f52a952b --- /dev/null +++ b/components/starloop/sources/new-profile-created/new-profile-created.mjs @@ -0,0 +1,32 @@ +import app from "../../starloop.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + key: "starloop-new-profile-created", + name: "New Profile Created", + description: "This source triggers when a new profile is created in Starloop.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + app, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + async run() { + const { profile_ids: profiles } = await this.app.listIds(); + + profiles.forEach((profile) => { + this.$emit(profile, { + id: profile.id, + summary: `New Profile: ${profile.id}`, + ts: Date.now(), + }); + }); + }, +}; diff --git a/components/starloop/starloop.app.mjs b/components/starloop/starloop.app.mjs index 633acfe08d124..dce48f4b0d692 100644 --- a/components/starloop/starloop.app.mjs +++ b/components/starloop/starloop.app.mjs @@ -1,11 +1,125 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "starloop", - propDefinitions: {}, + propDefinitions: { + businessId: { + type: "string", + label: "Business ID", + description: "The ID of the business", + optional: true, + async options() { + const { business_id: businessId } = await this.listIds(); + return [ + businessId, + ]; + }, + }, + profileId: { + type: "string", + label: "Profile ID", + description: "The ID of the profile", + optional: true, + async options() { + const { profile_ids: profileIds } = await this.listIds(); + return profileIds.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the recipient", + }, + email: { + type: "string", + label: "Email", + description: "The email of the recipient", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the recipient", + optional: true, + }, + testMode: { + type: "boolean", + label: "Test Mode", + description: "If set to true, the invite will not be sent", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + exportSummary(step) { + if (!step?.export) { + throw new ConfigurationError("The summary method should be bind to the step object aka `$`"); + } + return (msg = "") => step.export(constants.SUMMARY_LABEL, msg); + }, + getUrl(path) { + return `${constants.BASE_URL}${constants.VERSION_PATH}${path}`; + }, + getParams(params = {}) { + return { + token: this.$auth.api_key, + ...params, + }; + }, + getHeaders(headers = {}) { + return { + "Content-Type": "application/x-www-form-urlencoded", + "accept": "application/json", + ...headers, + }; + }, + async makeRequest({ + step = this, path, headers, params, summary, ...args + } = {}) { + const { + getUrl, + getParams, + getHeaders, + } = this; + + const config = { + url: getUrl(path), + params: getParams(params), + headers: getHeaders(headers), + ...args, + }; + + const response = await axios(step, config); + + if (typeof summary === "function") { + this.exportSummary(step)(summary(response)); + } + + if (response.error_msg) { + throw new Error(JSON.stringify(response, null, 2)); + } + + return response; + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + listIds(args = {}) { + return this.post({ + path: "/list_ids", + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/starshipit/package.json b/components/starshipit/package.json new file mode 100644 index 0000000000000..ac7dd44228383 --- /dev/null +++ b/components/starshipit/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/starshipit", + "version": "0.0.1", + "description": "Pipedream Starshipit Components", + "main": "starshipit.app.mjs", + "keywords": [ + "pipedream", + "starshipit" + ], + "homepage": "https://pipedream.com/apps/starshipit", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/starshipit/starshipit.app.mjs b/components/starshipit/starshipit.app.mjs new file mode 100644 index 0000000000000..931ad78ee7eb3 --- /dev/null +++ b/components/starshipit/starshipit.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "starshipit", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/stripe/package.json b/components/stripe/package.json index 5a2a9d5b502f4..5d90bd2ffdb20 100644 --- a/components/stripe/package.json +++ b/components/stripe/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/stripe", - "version": "0.4.1", + "version": "0.4.2", "description": "Pipedream Stripe Components", "main": "stripe.app.mjs", "keywords": [ diff --git a/components/stripe/sources/canceled-subscription/canceled-subscription.mjs b/components/stripe/sources/canceled-subscription/canceled-subscription.mjs index bcf6439643cdd..d2d91b14ee628 100644 --- a/components/stripe/sources/canceled-subscription/canceled-subscription.mjs +++ b/components/stripe/sources/canceled-subscription/canceled-subscription.mjs @@ -5,13 +5,14 @@ export default { key: "stripe-canceled-subscription", name: "Canceled Subscription", type: "source", - version: "0.0.1", + version: "0.0.2", description: "Emit new event for each new canceled subscription", methods: { ...common.methods, getEvents() { return [ "subscription_schedule.canceled", + "customer.subscription.deleted", ]; }, }, diff --git a/components/teamup/package.json b/components/teamup/package.json new file mode 100644 index 0000000000000..0301d51c48aaf --- /dev/null +++ b/components/teamup/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/teamup", + "version": "0.0.1", + "description": "Pipedream TeamUp Components", + "main": "teamup.app.mjs", + "keywords": [ + "pipedream", + "teamup" + ], + "homepage": "https://pipedream.com/apps/teamup", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/teamup/teamup.app.mjs b/components/teamup/teamup.app.mjs new file mode 100644 index 0000000000000..8eb8b608f012d --- /dev/null +++ b/components/teamup/teamup.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "teamup", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/upollo/package.json b/components/upollo/package.json new file mode 100644 index 0000000000000..daf959f97e35d --- /dev/null +++ b/components/upollo/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/upollo", + "version": "0.0.1", + "description": "Pipedream Upollo Components", + "main": "upollo.app.mjs", + "keywords": [ + "pipedream", + "upollo" + ], + "homepage": "https://pipedream.com/apps/upollo", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/upollo/upollo.app.mjs b/components/upollo/upollo.app.mjs new file mode 100644 index 0000000000000..10195d094dfc1 --- /dev/null +++ b/components/upollo/upollo.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "upollo", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/upviral/actions/add-contact/add-contact.mjs b/components/upviral/actions/add-contact/add-contact.mjs new file mode 100644 index 0000000000000..5e2fff791b713 --- /dev/null +++ b/components/upviral/actions/add-contact/add-contact.mjs @@ -0,0 +1,105 @@ +import { ConfigurationError } from "@pipedream/platform"; +import FormData from "form-data"; +import upviral from "../../upviral.app.mjs"; + +export default { + key: "upviral-add-contact", + name: "Add Contact", + version: "0.0.1", + description: "Add a new contact in your particular campaign. [See the documentation](https://api.upviral.com/#add-contact)", + type: "action", + props: { + upviral, + campaignId: { + propDefinition: [ + upviral, + "campaignId", + ], + reloadProps: true, + }, + email: { + type: "string", + label: "Email", + description: "The user's email address.", + }, + name: { + type: "string", + label: "Name", + description: "The user's name.", + optional: true, + }, + ipAddress: { + type: "string", + label: "IP Address", + description: "The user's IP Address.", + optional: true, + }, + referralCode: { + type: "string", + label: "Referral Code", + description: "The unique referral code (Last part of unique Referral URL). For example - https://upvir.al/ref/XXXX, Referral Code will be XXXX.This will be the referral code of the person who referred this new contact. The original participant will get the credit for this new contact.", + optional: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.campaignId) { + let count = 0; + let data = new FormData(); + data.append("campaign_id", this.campaignId); + + const { custom_fields: customFields } = await this.upviral.listCustomFields({ + data, + headers: data.getHeaders(), + }); + + for (const field of customFields) { + props[`customField-${field}`] = { + type: "string", + label: field, + description: `Custom field ${++count}`, + optional: true, + }; + } + } + return props; + }, + methods: { + parseCustomFields() { + const customFields = Object.entries(this).filter(([ + key, + ]) => key.includes("customField-")) + .map(([ + key, + value, + ]) => ([ + key.split("-")[1], + value, + ])); + + return JSON.stringify(Object.fromEntries(customFields)) || null; + }, + }, + async run({ $ }) { + var bodyFormData = new FormData(); + bodyFormData.append("campaign_id", this.campaignId); + bodyFormData.append("email", this.email); + if (this.name) bodyFormData.append("name", this.name); + if (this.ipAddress) bodyFormData.append("ip_address", this.ipAddress); + if (this.referralCode) bodyFormData.append("referral_code", this.referralCode); + bodyFormData.append("custom_fields", this.parseCustomFields() ); + + const response = await this.upviral.addContact({ + $, + data: bodyFormData, + headers: bodyFormData.getHeaders(), + }); + + if (response.result === "error") { + throw new ConfigurationError(response.message); + } + + $.export("$summary", `A new contact with UID: '${response.uid}' was successfully created!`); + return response; + }, +}; diff --git a/components/upviral/actions/add-points/add-points.mjs b/components/upviral/actions/add-points/add-points.mjs new file mode 100644 index 0000000000000..27dc11b028e60 --- /dev/null +++ b/components/upviral/actions/add-points/add-points.mjs @@ -0,0 +1,51 @@ +import { ConfigurationError } from "@pipedream/platform"; +import FormData from "form-data"; +import upviral from "../../upviral.app.mjs"; + +export default { + key: "upviral-add-points", + name: "Add Points", + version: "0.0.1", + description: "Add points in user profile. [See the documentation](https://api.upviral.com/#add-points)", + type: "action", + props: { + upviral, + campaignId: { + propDefinition: [ + upviral, + "campaignId", + ], + }, + leadId: { + propDefinition: [ + upviral, + "leadId", + ({ campaignId }) => ({ + campaignId, + }), + ], + }, + points: { + type: "string", + label: "Points", + description: "The points to add.", + }, + }, + async run({ $ }) { + var bodyFormData = new FormData(); + bodyFormData.append("campaign_id", this.campaignId); + bodyFormData.append("lead_id", this.leadId); + bodyFormData.append("points", this.points); + + const response = await this.upviral.addPoints({ + $, + data: bodyFormData, + headers: bodyFormData.getHeaders(), + }); + + if (response.result === "error") throw new ConfigurationError(response.message); + + $.export("$summary", `${this.points} points were successfully added!`); + return response; + }, +}; diff --git a/components/upviral/common/constants.mjs b/components/upviral/common/constants.mjs new file mode 100644 index 0000000000000..ea830c15a04cb --- /dev/null +++ b/components/upviral/common/constants.mjs @@ -0,0 +1 @@ +export const LIMIT = 100; diff --git a/components/upviral/package.json b/components/upviral/package.json new file mode 100644 index 0000000000000..acc3df365493d --- /dev/null +++ b/components/upviral/package.json @@ -0,0 +1,19 @@ +{ + "name": "@pipedream/upviral", + "version": "0.1.0", + "description": "Pipedream UpViral Components", + "main": "upviral.app.mjs", + "keywords": [ + "pipedream", + "upviral" + ], + "homepage": "https://pipedream.com/apps/upviral", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "form-data": "^4.0.0" + } +} diff --git a/components/upviral/upviral.app.mjs b/components/upviral/upviral.app.mjs index 4e443c7110a33..4698f7acfdf21 100644 --- a/components/upviral/upviral.app.mjs +++ b/components/upviral/upviral.app.mjs @@ -1,11 +1,135 @@ +import { axios } from "@pipedream/platform"; +import FormData from "form-data"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "upviral", - propDefinitions: {}, + propDefinitions: { + campaignId: { + type: "string", + label: "Campaign Id", + description: "The Id of the campaign.", + async options() { + const { data } = await this.listCampaigns({}); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + leadId: { + type: "string", + label: "Lead Id", + description: "The Id of the lead.", + async options({ + page, campaignId, + }) { + var formData = new FormData(); + formData.append("campaign_id", campaignId); + const { data } = await this.listLeads({ + params: { + start: LIMIT * page, + size: LIMIT, + }, + data: formData, + headers: formData.getHeaders(), + }); + + return data.leads.map(({ + id: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiKey() { + return this.$auth.api_key; + }, + _apiUrl() { + return "https://app.upviral.com/api/v1"; + }, + _getParams(params) { + return { + uvapikey: this._apiKey(), + ...params, + }; + }, + _makeRequest({ + $ = this, params, ...opts + }) { + const config = { + url: `${this._apiUrl()}`, + params: this._getParams(params), + ...opts, + }; + + return axios($, config); + }, + addContact({ + params, ...args + }) { + return this._makeRequest({ + method: "POST", + params: { + uvmethod: "add_contact", + ...params, + }, + ...args, + }); + }, + addPoints({ + params, ...args + }) { + return this._makeRequest({ + method: "POST", + params: { + uvmethod: "add_points", + ...params, + }, + ...args, + }); + }, + listCampaigns({ + params = {}, ...args + }) { + return this._makeRequest({ + params: { + uvmethod: "lists", + ...params, + }, + ...args, + }); + }, + listCustomFields({ + params, ...args + }) { + return this._makeRequest({ + method: "POST", + params: { + uvmethod: "get_custom_fields", + ...params, + }, + ...args, + }); + }, + listLeads({ + params, ...args + }) { + return this._makeRequest({ + method: "POST", + params: { + uvmethod: "get_leads", + ...params, + }, + ...args, + }); }, }, }; diff --git a/components/vendasta/package.json b/components/vendasta/package.json new file mode 100644 index 0000000000000..93fa4b562a348 --- /dev/null +++ b/components/vendasta/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/vendasta", + "version": "0.0.1", + "description": "Pipedream Vendasta Components", + "main": "vendasta.app.mjs", + "keywords": [ + "pipedream", + "vendasta" + ], + "homepage": "https://pipedream.com/apps/vendasta", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/vendasta/vendasta.app.mjs b/components/vendasta/vendasta.app.mjs new file mode 100644 index 0000000000000..c6abff7ca3be6 --- /dev/null +++ b/components/vendasta/vendasta.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "vendasta", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/vies_api/actions/retrieve-vat-data/retrieve-vat-data.mjs b/components/vies_api/actions/retrieve-vat-data/retrieve-vat-data.mjs new file mode 100644 index 0000000000000..ca62c8cc4e209 --- /dev/null +++ b/components/vies_api/actions/retrieve-vat-data/retrieve-vat-data.mjs @@ -0,0 +1,26 @@ +import app from "../../vies_api.app.mjs"; + +export default { + key: "vies_api-retrieve-vat-data", + name: "Retrieve VAT Data", + version: "0.0.1", + description: "Get firm data from VIES registry. [See the documentation](https://viesapi.eu/public/rest/viesapi.html#/API/getVIESData)", + type: "action", + props: { + app, + number: { + type: "string", + label: "VAT Number", + description: "The VAT number to retrieve data.", + }, + }, + async run({ $ }) { + const response = await this.app.getVATData({ + $, + number: this.number, + }); + + $.export("$summary", `The data of the VAT ${this.number} was successfully fetched!`); + return response; + }, +}; diff --git a/components/vies_api/package.json b/components/vies_api/package.json index 3f59f46cba473..8512c16f8560c 100644 --- a/components/vies_api/package.json +++ b/components/vies_api/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/vies_api", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream VIES API Components", "main": "vies_api.app.mjs", "keywords": [ @@ -11,5 +11,9 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "viesapi-client": "^1.2.5" } -} \ No newline at end of file +} diff --git a/components/vies_api/vies_api.app.mjs b/components/vies_api/vies_api.app.mjs index 68ecd433f7fb9..fac554fcf5bb3 100644 --- a/components/vies_api/vies_api.app.mjs +++ b/components/vies_api/vies_api.app.mjs @@ -1,11 +1,14 @@ +import VIESAPI from "viesapi-client"; + export default { type: "app", app: "vies_api", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _client() { + return new VIESAPI.VIESAPIClient(); + }, + getVATData({ number }) { + return this._client().getVIESData(number); }, }, }; diff --git a/components/webinarfuel/actions/add-new-registrant/add-new-registrant.mjs b/components/webinarfuel/actions/add-new-registrant/add-new-registrant.mjs new file mode 100644 index 0000000000000..ef3b0e02c2f56 --- /dev/null +++ b/components/webinarfuel/actions/add-new-registrant/add-new-registrant.mjs @@ -0,0 +1,89 @@ +import app from "../../webinarfuel.app.mjs"; + +export default { + key: "webinarfuel-add-new-registrant", + name: "Add New Registrant", + description: "Creates a new registrant for a selected webinar. [See the documentation](https://webinarfuel.docs.apiary.io/#/reference/registrant/registrants-collection/create-registrant/200?mc=reference%2Fregistrant%2Fregistrants-collection%2Fcreate-registrant%2F200)", + version: "0.0.1", + type: "action", + props: { + app, + webinarId: { + propDefinition: [ + app, + "webinarId", + ], + }, + email: { + propDefinition: [ + app, + "email", + ], + }, + firstName: { + propDefinition: [ + app, + "firstName", + ], + }, + tags: { + optional: true, + propDefinition: [ + app, + "tags", + ], + }, + webinarSessionId: { + propDefinition: [ + app, + "webinarSessionId", + ({ webinarId }) => ({ + webinarId, + }), + ], + }, + timeZone: { + optional: true, + propDefinition: [ + app, + "timeZone", + ], + }, + }, + methods: { + createRegistrant(args = {}) { + return this.app.post({ + path: "/registrants", + ...args, + }); + }, + }, + async run({ $: step }) { + const { + createRegistrant, + webinarId, + email, + firstName, + tags, + webinarSessionId, + timeZone, + } = this; + + return createRegistrant({ + step, + data: { + webinar_id: webinarId, + registrant: { + email, + first_name: firstName, + tags, + }, + session: { + webinar_session_id: webinarSessionId, + timezone: timeZone, + }, + }, + summary: (response) => `Successfully added new registrant with email: ${response.email}`, + }); + }, +}; diff --git a/components/webinarfuel/actions/add-tags-to-registrant/add-tags-to-registrant.mjs b/components/webinarfuel/actions/add-tags-to-registrant/add-tags-to-registrant.mjs new file mode 100644 index 0000000000000..838d7b81c257f --- /dev/null +++ b/components/webinarfuel/actions/add-tags-to-registrant/add-tags-to-registrant.mjs @@ -0,0 +1,48 @@ +import app from "../../webinarfuel.app.mjs"; + +export default { + key: "webinarfuel-add-tags-to-registrant", + name: "Add Tags To Registrant", + description: "Adds tags to an existing registrant. [See the documentation](https://webinarfuel.docs.apiary.io/#/reference/registrant/add-tags)", + version: "0.0.1", + type: "action", + props: { + app, + email: { + propDefinition: [ + app, + "email", + ], + }, + tags: { + propDefinition: [ + app, + "tags", + ], + }, + }, + methods: { + addTags(args = {}) { + return this.app.post({ + path: "/registrants/add_tags", + ...args, + }); + }, + }, + async run({ $: step }) { + const { + addTags, + email, + tags, + } = this; + + return addTags({ + step, + data: { + email, + tags, + }, + summary: (response) => `Successfully added tags to registrant email ${response.registrant.email}.`, + }); + }, +}; diff --git a/components/webinarfuel/common/constants.mjs b/components/webinarfuel/common/constants.mjs new file mode 100644 index 0000000000000..0c5b2cbd964c9 --- /dev/null +++ b/components/webinarfuel/common/constants.mjs @@ -0,0 +1,7 @@ +const SUMMARY_LABEL = "$summary"; +const BASE_URL = "https://api.webinarfuel.com"; + +export default { + SUMMARY_LABEL, + BASE_URL, +}; diff --git a/components/webinarfuel/package.json b/components/webinarfuel/package.json index 28a3c843f2b6b..dfb039417f348 100644 --- a/components/webinarfuel/package.json +++ b/components/webinarfuel/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/webinarfuel", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream WebinarFuel Components", "main": "webinarfuel.app.mjs", "keywords": [ @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/webinarfuel/webinarfuel.app.mjs b/components/webinarfuel/webinarfuel.app.mjs index 2f1091730f141..05f6dc5f1332f 100644 --- a/components/webinarfuel/webinarfuel.app.mjs +++ b/components/webinarfuel/webinarfuel.app.mjs @@ -1,11 +1,113 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "webinarfuel", - propDefinitions: {}, + propDefinitions: { + webinarId: { + type: "string", + label: "Webinar ID", + description: "The ID of the webinar", + async options({ page }) { + const { webinars } = await this.getWebinars({ + params: { + page: page + 1, + }, + }); + return webinars.map(({ + id: value, name: label, + }) => ({ + value, + label, + })); + }, + }, + webinarSessionId: { + type: "string", + label: "Session ID", + description: "The webinar session id.", + async options({ webinarId }) { + const { webinar: { sessions } } = await this.getWebinar({ + webinarId, + }); + return sessions.map(({ + id: value, formatted_scheduled_at: label, + }) => ({ + value, + label, + })); + }, + }, + timeZone: { + type: "string", + label: "Time Zone", + description: "Session [TimeZone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Required if webinar timezone is `registrant`. Example: `America/New_York`.", + }, + email: { + type: "string", + label: "Email", + description: "The email of the registrant", + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the registrant", + }, + tags: { + type: "string[]", + label: "Tags", + description: "The tags to add to the registrant", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + exportSummary(step) { + if (!step?.export) { + throw new ConfigurationError("The summary method should be bind to the step object aka `$`"); + } + return (msg = "") => step.export(constants.SUMMARY_LABEL, msg); + }, + async makeRequest({ + step = this, path, headers, summary, ...args + } = {}) { + const config = { + ...args, + url: constants.BASE_URL + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.api_key}`, + }, + }; + + const response = await axios(step, config); + + if (typeof summary === "function") { + this.exportSummary(step)(summary(response)); + } + + return response; + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + getWebinars(args = {}) { + return this.makeRequest({ + path: "/webinars", + ...args, + }); + }, + getWebinar({ + webinarId, ...args + } = {}) { + return this.makeRequest({ + path: `/webinars/${webinarId}`, + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/webvizio/package.json b/components/webvizio/package.json new file mode 100644 index 0000000000000..97e607ffa4337 --- /dev/null +++ b/components/webvizio/package.json @@ -0,0 +1,15 @@ +{ + "name": "@pipedream/webvizio", + "version": "0.0.1", + "description": "Pipedream Webvizio Components", + "main": "webvizio.app.mjs", + "keywords": [ + "pipedream", + "webvizio" + ], + "homepage": "https://pipedream.com/apps/webvizio", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + } +} \ No newline at end of file diff --git a/components/webvizio/webvizio.app.mjs b/components/webvizio/webvizio.app.mjs new file mode 100644 index 0000000000000..d0ba5157507e9 --- /dev/null +++ b/components/webvizio/webvizio.app.mjs @@ -0,0 +1,11 @@ +export default { + type: "app", + app: "webvizio", + propDefinitions: {}, + methods: { + // this.$auth contains connected account data + authKeys() { + console.log(Object.keys(this.$auth)); + }, + }, +}; diff --git a/components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs b/components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs index 91e4f292caccb..dbcfd1809b05c 100644 --- a/components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs +++ b/components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs @@ -1,130 +1,149 @@ -// legacy_hash_id: a_jQi84m -import { axios } from "@pipedream/platform"; +import xero from "../../xero_accounting_api.app.mjs"; +import { formatLineItems } from "../../common/util.mjs"; export default { key: "xero_accounting_api-xero-create-sales-invoice", name: "Create Sales Invoice", - description: "Creates a new sales invoice.", - version: "0.3.1", + description: "Creates a new sales invoice. [See the documentation](https://developer.xero.com/documentation/api/invoices#post)", + version: "0.3.2", type: "action", props: { - xero_accounting_api: { - type: "app", - app: "xero_accounting_api", - }, - contact_id: { + xero, + tenantId: { + propDefinition: [ + xero, + "tenantId", + ], + }, + contactId: { type: "string", + label: "Contact", description: "Id of the contact associated to the invoice.", + async options() { + if (!this.tenantId) { + return []; + } + const { Contacts: contacts } = await this.xero.getContact(null, this.tenantId); + return contacts?.map(({ + ContactID: value, Name: label, + }) => ({ + value, + label, + })) || []; + }, optional: true, }, - contact_name: { + contactName: { type: "string", + label: "Contact Name", description: "Name of the contact associated to the invoice. If there is no contact matching this name, a new contact is created.", optional: true, }, - tenant_id: { - type: "string", - description: "Id of the organization tenant to use on the Xero Accounting API. See [Get Tenant Connections](https://pipedream.com/@sergio/xero-accounting-api-get-tenant-connections-p_OKCzOgn/edit) for a workflow example on how to pull this data.", - }, - line_items: { - type: "any", - description: "See [LineItems](https://developer.xero.com/documentation/api/invoices#LineItemsPOST). The LineItems collection can contain any number of individual LineItem sub-elements. At least * **one** * is required to create a complete Invoice.", + lineItems: { + propDefinition: [ + xero, + "lineItems", + ], }, date: { type: "string", + label: "Date", description: "Date invoice was issued - YYYY-MM-DD. If the Date element is not specified it will default to the current date based on the timezone setting of the organisation.", optional: true, }, - due_date: { + dueDate: { type: "string", + label: "Due Date", description: "Date invoice is due - YYYY-MM-DD.", optional: true, }, - line_amount_type: { + lineAmountType: { type: "string", + label: "Line Amount Type", description: "Line amounts are exclusive of tax by default if you don't specify this element. See [Line Amount Types](https://developer.xero.com/documentation/api/types#LineAmountTypes)", optional: true, }, - invoice_number: { + invoiceNumber: { type: "string", + label: "Invoice Number", description: "Unique alpha numeric code identifying invoice (* when missing will auto-generate from your Organisation Invoice Settings*) (max length = 255)", optional: true, }, reference: { type: "string", + label: "Reference", description: "Additional reference number (max length = 255)", optional: true, }, - branding_theme_id: { + brandingThemeId: { type: "string", + label: "Branding Theme ID", description: "See [BrandingThemes](https://developer.xero.com/documentation/api/branding-themes)", optional: true, }, url: { type: "string", + label: "URL", description: "URL link to a source document - shown as \"Go to [appName]\" in the Xero app", optional: true, }, - currency_code: { + currencyCode: { type: "string", + label: "Currency Code", description: "The currency that invoice has been raised in (see [Currencies](https://developer.xero.com/documentation/api/currencies))", optional: true, }, - currency_rate: { + currencyRate: { type: "string", + label: "Currency Rate", description: "The currency rate for a multicurrency invoice. If no rate is specified, the [XE.com day rate](http://help.xero.com/#CurrencySettings$Rates) is used. (max length = [18].[6])", optional: true, }, status: { type: "string", + label: "Status", description: "See [Invoice Status Codes](https://developer.xero.com/documentation/api/invoices#status-codes)", optional: true, }, - sent_to_contact: { + sentToContact: { type: "string", + label: "Sent To Contact", description: "Boolean to set whether the invoice in the Xero app should be marked as \"sent\". This can be set only on invoices that have been approved", optional: true, }, - expected_payment_data: { + expectedPaymentData: { type: "string", + label: "Expected Payment Date", description: "Shown on the sales invoices when this has been set", optional: true, }, }, async run({ $ }) { - //See the API docs: https://developer.xero.com/documentation/api/invoices#post - - if ((!this.contact_id && !this.contact_name) || !this.tenant_id || !this.line_items) { - throw new Error("Must provide one of contact_id or contact_name, and tenant_id, type, line_items parameters."); + if ((!this.contactId && !this.contactName) || !this.tenantId || !this.lineItems) { + throw new Error("Must provide one of `contactId` or `contactName`, and `tenantId`, `type`, `lineItems` parameters."); } - return await axios($, { - method: "post", - url: "https://api.xero.com/api.xro/2.0/Invoices", - headers: { - "Authorization": `Bearer ${this.xero_accounting_api.$auth.oauth_access_token}`, - "xero-tenant-id": this.tenant_id, - }, - data: { - Type: "ACCREC", //ACCREC = Sales Invoice - Contact: { - ContactID: this.contact_id, - Name: this.contact_name, - }, - LineItems: this.line_items, - Date: this.date, - DueDate: this.due_date, - LineAmountTypes: this.line_amount_type, - InvoiceNumber: this.invoice_number, - Reference: this.reference, - BrandingThemeID: this.branding_theme_id, - Url: this.url, - CurrencyCode: this.currency_code, - CurrencyRate: this.currency_rate, - Status: this.status, - SentToContact: this.sent_to_contact, - ExpectedPaymentDate: this.expected_payment_data, + const response = await this.xero.createInvoice($, this.tenantId, { + Type: "ACCREC", //ACCREC = Sales Invoice + Contact: { + ContactID: this.contactId, + Name: this.contactName, }, + LineItems: formatLineItems(this.lineItems), + Date: this.date, + DueDate: this.dueDate, + LineAmountTypes: this.lineAmountType, + InvoiceNumber: this.invoiceNumber, + Reference: this.reference, + BrandingThemeID: this.brandingThemeId, + Url: this.url, + CurrencyCode: this.currencyCode, + CurrencyRate: this.currencyRate, + Status: this.status, + SentToContact: this.sentToContact, + ExpectedPaymentDate: this.expectedPaymentData, }); + + return response; }, }; diff --git a/components/xero_accounting_api/package.json b/components/xero_accounting_api/package.json index 7138ecd394301..ffd2aa9d3b603 100644 --- a/components/xero_accounting_api/package.json +++ b/components/xero_accounting_api/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/xero_accounting_api", - "version": "0.1.0", + "version": "0.1.1", "description": "Pipedream Xero Components", "main": "xero_accounting_api.app.mjs", "keywords": [ diff --git a/components/zoho_crm/package.json b/components/zoho_crm/package.json index b61af031ba0a5..2834317469d94 100644 --- a/components/zoho_crm/package.json +++ b/components/zoho_crm/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/zoho_crm", - "version": "0.5.0", + "version": "0.5.1", "description": "Pipedream Zoho CRM Components", "main": "zoho_crm.app.mjs", "keywords": [ diff --git a/components/zoho_crm/sources/common/http-based/common-custom-module.mjs b/components/zoho_crm/sources/common/http-based/common-custom-module.mjs index 4020f97b119d4..b612fd3291973 100644 --- a/components/zoho_crm/sources/common/http-based/common-custom-module.mjs +++ b/components/zoho_crm/sources/common/http-based/common-custom-module.mjs @@ -14,6 +14,7 @@ export default { const { modules } = await this.zohoCrm.listModules(); const options = modules .filter(this.areEventsSupportedByModule) + .filter(({ generated_type: type }) => type !== "custom") .map(({ api_name: type, singular_label: name, diff --git a/components/zoho_crm/sources/new-module-entry/new-module-entry.mjs b/components/zoho_crm/sources/new-module-entry/new-module-entry.mjs index 740b22447a367..0fd9c4fac28c9 100644 --- a/components/zoho_crm/sources/new-module-entry/new-module-entry.mjs +++ b/components/zoho_crm/sources/new-module-entry/new-module-entry.mjs @@ -6,7 +6,7 @@ export default { key: "zoho_crm-new-module-entry", name: "New Module Entry (Instant)", description: "Emit new events each time a new module/record is created in Zoho CRM", - version: "0.0.13", + version: "0.0.14", type: "source", methods: { ...common.methods, diff --git a/components/zoho_crm/sources/new-or-updated-module-entry/new-or-updated-module-entry.mjs b/components/zoho_crm/sources/new-or-updated-module-entry/new-or-updated-module-entry.mjs index 459f14f767a6e..6423c0de434e8 100644 --- a/components/zoho_crm/sources/new-or-updated-module-entry/new-or-updated-module-entry.mjs +++ b/components/zoho_crm/sources/new-or-updated-module-entry/new-or-updated-module-entry.mjs @@ -6,7 +6,7 @@ export default { key: "zoho_crm-new-or-updated-module-entry", name: "New or Updated Module Entry (Instant)", description: "Emits an event each time a module/record is created or edited in Zoho CRM", - version: "0.0.12", + version: "0.0.13", type: "source", methods: { ...common.methods, diff --git a/components/zoho_crm/sources/updated-module-entry/updated-module-entry.mjs b/components/zoho_crm/sources/updated-module-entry/updated-module-entry.mjs index fd8fc88d35f93..0aee287ad3a52 100644 --- a/components/zoho_crm/sources/updated-module-entry/updated-module-entry.mjs +++ b/components/zoho_crm/sources/updated-module-entry/updated-module-entry.mjs @@ -6,7 +6,7 @@ export default { key: "zoho_crm-updated-module-entry", name: "Updated Module Entry (Instant)", description: "Emits an event each time a new module/record is updated in Zoho CRM", - version: "0.0.12", + version: "0.0.13", type: "source", methods: { ...common.methods, diff --git a/components/zoho_workdrive/actions/upload-file/upload-file.mjs b/components/zoho_workdrive/actions/upload-file/upload-file.mjs new file mode 100644 index 0000000000000..6cc62d45378bd --- /dev/null +++ b/components/zoho_workdrive/actions/upload-file/upload-file.mjs @@ -0,0 +1,73 @@ +import { ConfigurationError } from "@pipedream/platform"; +import FormData from "form-data"; +import fs from "fs"; +import { getFilePath } from "../../common/utils.mjs"; +import app from "../../zoho_workdrive.app.mjs"; + +export default { + key: "zoho_workdrive-upload-file", + name: "Upload File", + version: "0.0.1", + description: "Upload a new file to your WorkDrive account. [See the documentation](https://workdrive.zoho.com/apidocs/v1/chunkupload/chunkuploadcreatesession)", + type: "action", + props: { + app, + teamId: { + propDefinition: [ + app, + "teamId", + ], + }, + parentId: { + propDefinition: [ + app, + "parentId", + ({ teamId }) => ({ + teamId, + }), + ], + }, + filename: { + label: "Filename", + description: "The name of the file should be URL encoded with UTF-8 Charset.", + type: "string", + optional: true, + }, + overrideNameExist: { + type: "boolean", + label: "Override Name Exist", + description: "If set as **true**, a file with the matching name will be uploaded as a top version over the existing file. If set as **false**, a file with the matching name will have the timestamp added to it on upload.", + optional: true, + }, + content: { + label: "File Path", + description: "Full path to the file in `/tmp/` directory. E.g. `/tmp/file.jpg`", + type: "string", + }, + }, + async run({ $ }) { + const path = getFilePath(this.content); + if (!fs.existsSync(path)) { + throw new ConfigurationError("File does not exist!"); + } + const file = fs.createReadStream(path); + const data = new FormData(); + const override = this.overrideNameExist?.toString(); + + data.append("content", file); + data.append("parent_id", this.parentId); + if (this.filename)data.append("filename", this.filename); + if (override)data.append("override-name-exist", override); + + const response = await this.app.uploadFile({ + $, + data, + headers: { + "Content-Type": `multipart/form-data; boundary=${data._boundary}`, + }, + }); + + $.export("$summary", "The file was successfully uploaded!"); + return response; + }, +}; diff --git a/components/zoho_workdrive/common/constants.mjs b/components/zoho_workdrive/common/constants.mjs new file mode 100644 index 0000000000000..9975390fe8151 --- /dev/null +++ b/components/zoho_workdrive/common/constants.mjs @@ -0,0 +1 @@ +export const LIMIT = 50; diff --git a/components/zoho_workdrive/common/utils.mjs b/components/zoho_workdrive/common/utils.mjs new file mode 100644 index 0000000000000..cfd711fb233cf --- /dev/null +++ b/components/zoho_workdrive/common/utils.mjs @@ -0,0 +1,6 @@ +export const getFilePath = (filename) => { + if (filename.indexOf("/tmp") === -1) { + return `/tmp/${filename}`; + } + return filename; +}; diff --git a/components/zoho_workdrive/package.json b/components/zoho_workdrive/package.json new file mode 100644 index 0000000000000..96ef4774cdd9e --- /dev/null +++ b/components/zoho_workdrive/package.json @@ -0,0 +1,19 @@ +{ + "name": "@pipedream/zoho_workdrive", + "version": "0.1.0", + "description": "Pipedream Zoho WorkDrive Components", + "main": "zoho_workdrive.app.mjs", + "keywords": [ + "pipedream", + "zoho_workdrive" + ], + "homepage": "https://pipedream.com/apps/zoho_workdrive", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1", + "axios": "^1.5.1" + } +} diff --git a/components/zoho_workdrive/sources/new-file-in-folder/new-file-in-folder.mjs b/components/zoho_workdrive/sources/new-file-in-folder/new-file-in-folder.mjs new file mode 100644 index 0000000000000..85b5c1891312a --- /dev/null +++ b/components/zoho_workdrive/sources/new-file-in-folder/new-file-in-folder.mjs @@ -0,0 +1,92 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import app from "../../zoho_workdrive.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "zoho_workdrive-new-file-in-folder", + name: "New File In Folder", + version: "0.0.1", + description: "Emit new event when a new file is created in a specific folder.", + type: "source", + dedupe: "unique", + props: { + app, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Zoho WorkDrive on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + teamId: { + propDefinition: [ + app, + "teamId", + ], + }, + folderId: { + propDefinition: [ + app, + "parentId", + ({ teamId }) => ({ + teamId, + }), + ], + label: "Folder Id", + description: "The unique ID of the folder.", + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async startEvent(maxResults = 0) { + const { + app, + folderId, + } = this; + + const lastDate = this._getLastDate(); + const items = app.paginate({ + fn: app.listFiles, + maxResults, + filter: "allfiles", + sort: "created_time", + folderId, + }); + + let responseArray = []; + + for await (const item of items) { + if (new Date(item.created_time) <= new Date(lastDate)) break; + responseArray.push(item); + } + if (responseArray.length) this._setLastDate(responseArray[0].created_time); + + for (const item of responseArray) { + this.$emit( + item, + { + id: item.id, + summary: `A new file with id: "${item.id}" was created!`, + ts: item.created_time, + }, + ); + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/zoho_workdrive/sources/new-file-in-folder/test-event.mjs b/components/zoho_workdrive/sources/new-file-in-folder/test-event.mjs new file mode 100644 index 0000000000000..ebfcda77612a0 --- /dev/null +++ b/components/zoho_workdrive/sources/new-file-in-folder/test-event.mjs @@ -0,0 +1,259 @@ +export default { + id: "cjrrr1b298cf52cf44acebc713ab5b73bbd77", + type: "files", + attributes: { + modified_by_zuid: "747938014", + is_locked: false, + conv_engine_type: 0, + is_fillable_resource: false, + is_published: false, + destination_id: "cjrrr27f73d5b59a54b16aaf3cd74478a0fa2", + storage_info: { + size: "0 bytes", + storage_used: "0 bytes", + files_count: 0, + folders_count: 0, + size_in_bytes: 0, + storage_used_in_bytes: 0 + }, + type: "file", + created_time_i18: "Apr 13, 10:58 AM", + modified_time_in_millisecond: 1618291736244, + opened_time: "Apr 13, 10:58 AM", + status_change_time: "Apr 13, 10:58 AM", + download_url: "https://www.zohoapis.com/workdrive/download/cjrrr1b298cf52cf44acebc713ab5b73bbd77", + comment_badge_count: 0, + is_app_associated: false, + created_time: "Apr 13, 10:58 AM", + lock_status: 2, + is_folder: false, + resource_type: 1001, + is_email_in_upload: false, + display_attr_name: "April", + created_by: "Zylker Creation", + display_html_name: "April", + labels: [], + parent_id: "cjrrr27f73d5b59a54b16aaf3cd74478a0fa2", + name: "April", + status_change_time_in_millisecond: 1618291736244, + permalink: "https://www.zohoapis.com/workdrive/folder/cjrrr1b298cf52cf44acebc713ab5b73bbd77", + favorite: false, + new_badge_count: 0, + status: 1, + modified_time_i18: "Apr 13, 10:58 AM", + opened_time_i18: "Apr 13, 10:58 AM", + extn: "", + view_pref: { + sort_by: "last_modified", + sort_order: "desc", + filtered_by: "all", + layout: "list" + }, + shortcut_link: "", + status_change_time_i18: "Apr 13, 10:58 AM", + description: "", + uploaded_time_in_millisecond: 1618291736244, + thumbnail_url: "", + title: "", + files_view_pref: { + sort_by: "last_modified", + sort_order: "desc", + filtered_by: "all", + layout: "list" + }, + modified_time: "Apr 13, 10:58 AM", + library_id: "ly9zm0170fb40015f4e2297a144e2b68cfa68", + icon_class: "folder", + created_time_in_millisecond: 1618291736244, + owner: "747937818", + creator: "747938014", + capabilities: { + can_read: true, + can_share: true, + can_remove_share: false, + can_delete: false, + can_edit: true, + can_create_files: true, + can_upload_files: true, + can_trash: true, + can_rename: true, + can_restore: false, + can_copy: true, + can_move: true, + can_zip: true, + can_download: true, + can_emailattach: true, + can_publish: true, + can_create_task: false, + can_share_support: true, + can_label: true, + can_trash_files: true + }, + uploaded_time_i18: "Apr 13, 10:58 AM", + is_external_upload: false, + watch_preference: { + watch: false, + notifyemail: false, + notifybell: false + }, + opened_time_in_millisecond: 1618291736244, + edit_badge_count: 0, + share_data: [], + uploaded_time: "Apr 13, 10:58 AM", + has_folders: false, + service_type: "upload", + display_url_name: "April", + is_unread: false, + modified_by: "Zylker Creation", + embed_props: {} + }, + relationships: { + folders: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/folders", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/folders" + } + }, + comments: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/comments", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/comments" + } + }, + previewinfo: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/previewinfo", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/previewinfo" + } + }, + unzip: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/unzip", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/unzip" + } + }, + publiclink: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/publiclink", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/publiclink" + } + }, + resourceproperty: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/resourceproperty", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/resourceproperty" + } + }, + parentfolders: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/parentfolders", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/parentfolders" + } + }, + shortcut: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/shortcut", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/shortcut" + } + }, + importfile: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/importfile", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/importfile" + } + }, + versions: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/versions", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/versions" + } + }, + supportshare: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/supportshare", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/supportshare" + } + }, + permissions: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/permissions", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/permissions" + } + }, + timeline: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/timeline", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/timeline" + } + }, + files: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/files", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/files" + } + }, + saveastemplate: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/saveastemplate", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/saveastemplate" + } + }, + links: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/links", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/links" + } + }, + copy: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/copy", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/copy" + } + }, + previewzip: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/previewzip", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/previewzip" + } + }, + tasks: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/tasks", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/tasks" + } + }, + breadcrumbs: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/breadcrumbs", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/breadcrumbs" + } + }, + entity: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/entity", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/entity" + } + }, + custommetadata: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/custommetadata", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/custommetadata" + } + }, + statistics: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/statistics", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/statistics" + } + }, + appdata: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/appdata", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/appdata" + } + } + }, + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77" + } +} \ No newline at end of file diff --git a/components/zoho_workdrive/sources/new-folder/new-folder.mjs b/components/zoho_workdrive/sources/new-folder/new-folder.mjs new file mode 100644 index 0000000000000..0a49fcfca1964 --- /dev/null +++ b/components/zoho_workdrive/sources/new-folder/new-folder.mjs @@ -0,0 +1,92 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import app from "../../zoho_workdrive.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "zoho_workdrive-new-folder", + name: "New Folder", + version: "0.0.1", + description: "Emit new event when a new folder is created in a specific folder.", + type: "source", + dedupe: "unique", + props: { + app, + db: "$.service.db", + timer: { + label: "Polling interval", + description: "Pipedream will poll the Zoho WorkDrive on this schedule", + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + teamId: { + propDefinition: [ + app, + "teamId", + ], + }, + folderId: { + propDefinition: [ + app, + "parentId", + ({ teamId }) => ({ + teamId, + }), + ], + label: "Folder Id", + description: "The unique ID of the folder.", + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async startEvent(maxResults = 0) { + const { + app, + folderId, + } = this; + + const lastDate = this._getLastDate(); + const items = app.paginate({ + fn: app.listFiles, + maxResults, + filter: "folder", + sort: "created_time", + folderId, + }); + + let responseArray = []; + + for await (const item of items) { + if (new Date(item.created_time) <= new Date(lastDate)) break; + responseArray.push(item); + } + if (responseArray.length) this._setLastDate(responseArray[0].created_time); + + for (const item of responseArray) { + this.$emit( + item, + { + id: item.id, + summary: `A new folder with id: "${item.id}" was created!`, + ts: item.created_time, + }, + ); + } + }, + }, + hooks: { + async deploy() { + await this.startEvent(25); + }, + }, + async run() { + await this.startEvent(); + }, + sampleEmit, +}; diff --git a/components/zoho_workdrive/sources/new-folder/test-event.mjs b/components/zoho_workdrive/sources/new-folder/test-event.mjs new file mode 100644 index 0000000000000..a6b2247d763ce --- /dev/null +++ b/components/zoho_workdrive/sources/new-folder/test-event.mjs @@ -0,0 +1,259 @@ +export default { + id: "cjrrr1b298cf52cf44acebc713ab5b73bbd77", + type: "files", + attributes: { + modified_by_zuid: "747938014", + is_locked: false, + conv_engine_type: 0, + is_fillable_resource: false, + is_published: false, + destination_id: "cjrrr27f73d5b59a54b16aaf3cd74478a0fa2", + storage_info: { + size: "0 bytes", + storage_used: "0 bytes", + files_count: 0, + folders_count: 0, + size_in_bytes: 0, + storage_used_in_bytes: 0 + }, + type: "folder", + created_time_i18: "Apr 13, 10:58 AM", + modified_time_in_millisecond: 1618291736244, + opened_time: "Apr 13, 10:58 AM", + status_change_time: "Apr 13, 10:58 AM", + download_url: "https://www.zohoapis.com/workdrive/download/cjrrr1b298cf52cf44acebc713ab5b73bbd77", + comment_badge_count: 0, + is_app_associated: false, + created_time: "Apr 13, 10:58 AM", + lock_status: 2, + is_folder: false, + resource_type: 1001, + is_email_in_upload: false, + display_attr_name: "April", + created_by: "Zylker Creation", + display_html_name: "April", + labels: [], + parent_id: "cjrrr27f73d5b59a54b16aaf3cd74478a0fa2", + name: "April", + status_change_time_in_millisecond: 1618291736244, + permalink: "https://www.zohoapis.com/workdrive/folder/cjrrr1b298cf52cf44acebc713ab5b73bbd77", + favorite: false, + new_badge_count: 0, + status: 1, + modified_time_i18: "Apr 13, 10:58 AM", + opened_time_i18: "Apr 13, 10:58 AM", + extn: "", + view_pref: { + sort_by: "last_modified", + sort_order: "desc", + filtered_by: "all", + layout: "list" + }, + shortcut_link: "", + status_change_time_i18: "Apr 13, 10:58 AM", + description: "", + uploaded_time_in_millisecond: 1618291736244, + thumbnail_url: "", + title: "", + files_view_pref: { + sort_by: "last_modified", + sort_order: "desc", + filtered_by: "all", + layout: "list" + }, + modified_time: "Apr 13, 10:58 AM", + library_id: "ly9zm0170fb40015f4e2297a144e2b68cfa68", + icon_class: "folder", + created_time_in_millisecond: 1618291736244, + owner: "747937818", + creator: "747938014", + capabilities: { + can_read: true, + can_share: true, + can_remove_share: false, + can_delete: false, + can_edit: true, + can_create_files: true, + can_upload_files: true, + can_trash: true, + can_rename: true, + can_restore: false, + can_copy: true, + can_move: true, + can_zip: true, + can_download: true, + can_emailattach: true, + can_publish: true, + can_create_task: false, + can_share_support: true, + can_label: true, + can_trash_files: true + }, + uploaded_time_i18: "Apr 13, 10:58 AM", + is_external_upload: false, + watch_preference: { + watch: false, + notifyemail: false, + notifybell: false + }, + opened_time_in_millisecond: 1618291736244, + edit_badge_count: 0, + share_data: [], + uploaded_time: "Apr 13, 10:58 AM", + has_folders: false, + service_type: "upload", + display_url_name: "April", + is_unread: false, + modified_by: "Zylker Creation", + embed_props: {} + }, + relationships: { + folders: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/folders", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/folders" + } + }, + comments: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/comments", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/comments" + } + }, + previewinfo: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/previewinfo", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/previewinfo" + } + }, + unzip: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/unzip", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/unzip" + } + }, + publiclink: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/publiclink", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/publiclink" + } + }, + resourceproperty: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/resourceproperty", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/resourceproperty" + } + }, + parentfolders: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/parentfolders", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/parentfolders" + } + }, + shortcut: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/shortcut", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/shortcut" + } + }, + importfile: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/importfile", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/importfile" + } + }, + versions: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/versions", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/versions" + } + }, + supportshare: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/supportshare", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/supportshare" + } + }, + permissions: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/permissions", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/permissions" + } + }, + timeline: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/timeline", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/timeline" + } + }, + files: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/files", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/files" + } + }, + saveastemplate: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/saveastemplate", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/saveastemplate" + } + }, + links: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/links", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/links" + } + }, + copy: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/copy", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/copy" + } + }, + previewzip: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/previewzip", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/previewzip" + } + }, + tasks: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/tasks", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/tasks" + } + }, + breadcrumbs: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/breadcrumbs", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/breadcrumbs" + } + }, + entity: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/entity", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/entity" + } + }, + custommetadata: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/custommetadata", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/custommetadata" + } + }, + statistics: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/statistics", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/statistics" + } + }, + appdata: { + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/relationships/appdata", + related: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77/appdata" + } + } + }, + links: { + self: "https://www.zohoapis.com/workdrive/files/cjrrr1b298cf52cf44acebc713ab5b73bbd77" + } +} \ No newline at end of file diff --git a/components/zoho_workdrive/zoho_workdrive.app.mjs b/components/zoho_workdrive/zoho_workdrive.app.mjs index e133f0f48aca4..159618bbc0720 100644 --- a/components/zoho_workdrive/zoho_workdrive.app.mjs +++ b/components/zoho_workdrive/zoho_workdrive.app.mjs @@ -1,11 +1,216 @@ +import axios from "axios"; +import qs from "qs"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "zoho_workdrive", - propDefinitions: {}, + propDefinitions: { + teamId: { + type: "string", + label: "Team Id", + description: "The unique ID that represents a team.", + async options() { + const { data: { id: userId } } = await this.getUser(); + const { data } = await this.listTeams({ + userId, + }); + + return data.map(({ + id: value, attributes: { name: label }, + }) => ({ + label, + value, + })); + }, + }, + parentId: { + type: "string", + label: "Parent Id", + description: "The unique ID of the folder where files are to be uploaded.", + async options({ + page, teamId, + }) { + return await this.listRootFolders({ + teamId, + limit: LIMIT, + offset: LIMIT * page, + }); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiUrl(path, params = "") { + return `https://workdrive.${this.$auth.base_api_uri}/api/v1/${path}${params + ? `?${params}` + : ""}`; + }, + _getHeaders(headers = {}) { + return { + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + ...headers, + }; + }, + async _makeRequest({ + path, params, headers, ...opts + }) { + const config = { + url: this._apiUrl(path, params), + headers: this._getHeaders(headers), + ...opts, + }; + + const { data } = await axios(config); + return data; + }, + getPrivateSpace({ + teamCurrentUserId, ...args + }) { + return this._makeRequest({ + path: `users/${teamCurrentUserId}/privatespace`, + ...args, + }); + }, + getTeamCurrentUser({ + teamId, ...args + }) { + return this._makeRequest({ + path: `teams/${teamId}/currentuser`, + ...args, + }); + }, + getUser(args = {}) { + return this._makeRequest({ + path: "users/me", + ...args, + }); + }, + listFiles({ + folderId, params = "", filter = "folder", sort = "name", ...args + }) { + return this._makeRequest({ + path: `files/${folderId}/files`, + params: `${params}&filter%5Btype%5D=${filter}&sort=${sort}`, + ...args, + }); + }, + async listRootFolders({ + teamId, limit, offset, params = {}, ...args + }) { + const { data: { id: teamCurrentUserId } } = await this.getTeamCurrentUser({ + teamId, + }); + + const { + data: [ + { id: privateSpaceId }, + ], + } = await this.getPrivateSpace({ + teamCurrentUserId, + }); + params["page[limit]"] = limit; + params["page[offset]"] = offset; + params["sort"] = "name"; + const reponseArray = []; + const { data: rootFolders } = await this._makeRequest({ + path: `privatespace/${privateSpaceId}/folders`, + params: qs.stringify(params), + ...args, + }); + + for (const { + id, attributes, + } of rootFolders) { + reponseArray.push({ + value: id, + label: attributes.name, + }); + const subFolders = await this.paginateFolders({ + folderId: id, + prefix: "- ", + }); + reponseArray.push(...subFolders); + } + return reponseArray; + }, + listTeams({ + userId, ...args + }) { + return this._makeRequest({ + path: `users/${userId}/teams`, + ...args, + }); + }, + uploadFile(args = {}) { + return this._makeRequest({ + method: "POST", + path: "upload", + ...args, + }); + }, + async paginateFolders({ + folderId, prefix = "", params = {}, + }) { + let hasMore = false; + let page = 0; + const responseArray = []; + + do { + params["page[limit]"] = LIMIT; + params["page[offset]"] = LIMIT * page; + page++; + + const { data: subFolders } = await this.listFiles({ + folderId, + params: qs.stringify(params), + }); + + for (const { + id, attributes: { name }, + } of subFolders) { + responseArray.push({ + value: id, + label: `${prefix}${name}`, + }); + responseArray.push(...await this.paginateFolders({ + folderId: id, + prefix: `${prefix}- `, + })); + } + + hasMore = subFolders.length; + } while (hasMore); + + return responseArray; + }, + async *paginate({ + fn, params = {}, maxResults = null, ...args + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params["page[limit]"] = LIMIT; + params["page[offset]"] = LIMIT * page; + page++; + + const { data } = await fn({ + params: qs.stringify(params), + ...args, + }); + + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); }, }, }; diff --git a/docs/docs/.vuepress/configs/envVars.js b/docs/docs/.vuepress/configs/envVars.js index 520db2af93d5c..79d4b1db501e7 100644 --- a/docs/docs/.vuepress/configs/envVars.js +++ b/docs/docs/.vuepress/configs/envVars.js @@ -13,7 +13,7 @@ module.exports = { seconds: 30, }, DATA_STORES_MAX_KEYS: "1,024", - DAILY_CREDITS_LIMIT: "100", + DAILY_CREDITS_LIMIT: "25", DAILY_TESTING_LIMIT: "30 minutes", INSPECTOR_EVENT_EXPIRY_DAYS: "365", FUNCTION_PAYLOAD_LIMIT: "6MB", @@ -36,7 +36,7 @@ module.exports = { TMP_SIZE_LIMIT: "2GB", DELAY_MIN_MAX_TIME: "You can pause your workflow for as little as one millisecond, or as long as one year", - PUBLIC_APPS: "1,400", + PUBLIC_APPS: "1,600", WARM_WORKERS_INTERVAL: "10 minutes", WARM_WORKERS_CREDITS_PER_INTERVAL: 5, }; diff --git a/docs/docs/.vuepress/configs/navConfig.js b/docs/docs/.vuepress/configs/navConfig.js index 643f4afd1f7f8..df827632c2390 100644 --- a/docs/docs/.vuepress/configs/navConfig.js +++ b/docs/docs/.vuepress/configs/navConfig.js @@ -24,7 +24,7 @@ module.exports = { { text: "Handling Cold Starts", link: "/workflows/settings/#eliminate-cold-starts"}, ], }, - ], + ], right: [ { text: "Support", @@ -44,13 +44,13 @@ module.exports = { text: "v2", link: "https://pipedream.com/docs", internal: true, - badge: "New", - badgeVariation: "primary", }, { text: "v1", - internal: true, link: "https://pipedream.com/docs/v1", + internal: true, + badge: "Deprecated", + badgeVariation: "danger", }, ], }, diff --git a/docs/docs/apps/README.md b/docs/docs/apps/README.md index 8e9fb5139c991..860073d5c6441 100644 --- a/docs/docs/apps/README.md +++ b/docs/docs/apps/README.md @@ -10,37 +10,68 @@ But Pipedream-integrated apps provide a few benefits: ## Premium Apps -The vast majority of integrated apps on Pipedream are free to use in your workflows across any plan. However, in order to use any of the below apps in an active workflow, your workspace will need to have a paid plan (Basic or higher): +The vast majority of integrated apps on Pipedream are free to use in your workflows across any plan. However, in order to use any of the below apps in an active workflow, your workspace will need to have access to [Premium Apps](https://pipedream.com/pricing): - [ActiveCampaign](https://pipedream.com/apps/activecampaign) +- [ADP](https://pipedream.com/apps/adp) +- [Amazon Advertising](https://pipedream.com/apps/amazon_advertising) - [Asana](https://pipedream.com/apps/asana) +- [AWS](https://pipedream.com/apps/aws) - [Azure OpenAI Service](https://pipedream.com/apps/azure-openai-service) +- [BigCommerce](https://pipedream.com/apps/bigcommerce) +- [Cisco Webex](https://pipedream.com/apps/cisco-webex) +- [Cisco Webex (Custom App)](https://pipedream.com/apps/cisco-webex-custom-app) - [Close](https://pipedream.com/apps/close) +- [Cloudinary](https://pipedream.com/apps/cloudinary) +- [Customer.io](https://pipedream.com/apps/customer-io) +- [Datadog](https://pipedream.com/apps/datadog) +- [dbt Cloud](https://pipedream.com/apps/dbt) - [ERPNext](https://pipedream.com/apps/erpnext) +- [Exact](https://pipedream.com/apps/exact) +- [Freshdesk](https://pipedream.com/apps/freshdesk) +- [Google Cloud](https://pipedream.com/apps/google-cloud) +- [Gorgias](https://pipedream.com/apps/gorgias-oauth) - [HubSpot](https://pipedream.com/apps/hubspot) - [Intercom](https://pipedream.com/apps/intercom) +- [Jira](https://pipedream.com/apps/jira) +- [Jira Service Desk](https://pipedream.com/apps/jira-service-desk) - [Klaviyo](https://pipedream.com/apps/klaviyo) - [Linkedin](https://pipedream.com/apps/linkedin) +- [Linkedin Ads](https://pipedream.com/apps/linkedin-ads) +- [Mailchimp](https://pipedream.com/apps/mailchimp) - [Mailgun](https://pipedream.com/apps/mailgun) +- [MongoDB](https://pipedream.com/apps/mongodb) +- [Outreach](https://pipedream.com/apps/outreach) +- [PagerDuty](https://pipedream.com/apps/pagerduty) - [Pipedrive](https://pipedream.com/apps/pipedrive) - [Pipefy](https://pipedream.com/apps/pipefy) +- [Propeller](https://pipedream.com/apps/propeller) - [Quickbooks](https://pipedream.com/apps/quickbooks) +- [Rebrandly](https://pipedream.com/apps/rebrandly) - [ReCharge](https://pipedream.com/apps/recharge) - [Salesforce (REST API)](https://pipedream.com/apps/salesforce_rest_api) +- [Segment](https://pipedream.com/apps/segment) - [SendinBlue](https://pipedream.com/apps/sendinblue) - [ServiceNow](https://pipedream.com/apps/servicenow) +- [ShipStation](https://pipedream.com/apps/shipstation) +- [Shopify](https://pipedream.com/apps/shopify) - [Snowflake](https://pipedream.com/apps/snowflake) +- [Stripe](https://pipedream.com/apps/stripe) - [Twilio SendGrid](https://pipedream.com/apps/sendgrid) +- [WhatsApp Business](https://pipedream.com/apps/whatsapp-business) +- [WooCommerce](https://pipedream.com/apps/woocommerce) - [Xero Accounting](https://pipedream.com/apps/xero_accounting_api) - [Zendesk](https://pipedream.com/apps/zendesk) +- [Zoom Admin](https://pipedream.com/apps/zoom_admin) - [Zoho Books](https://pipedream.com/apps/zoho_books) - [Zoho CRM](https://pipedream.com/apps/zoho_crm) -- [Zoom Admin](https://pipedream.com/apps/zoom_admin) +- [Zoho People](https://pipedream.com/apps/zoho_people) +- [Zoho SalesIQ](https://pipedream.com/apps/zoho_salesiq)

::: tip Missing an integration? -If we don't have an integration for an app that you'd like to see, please [request it on our Github roadmap](https://github.com/PipedreamHQ/pipedream/issues/new?assignees=&labels=app%2C+enhancement&template=app---service-integration.md&title=%5BAPP%5D) or [contribute it to the open source Pipedream registry](/apps/contributing/). +If we don't have an integration for an app that you'd like to see, please [request let us know](https://pipedream.com/support) or [contribute it to the source available Pipedream registry](/apps/contributing/). ::: **Check out the full list of integrated apps [here](https://pipedream.com/apps).** diff --git a/docs/docs/code/bash/README.md b/docs/docs/code/bash/README.md index f7d91cfe0e960..13177e349109b 100644 --- a/docs/docs/code/bash/README.md +++ b/docs/docs/code/bash/README.md @@ -68,7 +68,7 @@ In this example, we'll pretend this data is coming into our HTTP trigger via a P In our Bash script, we can access this data via the `$PIPEDREAM_STEPS` file. Specifically, this data from the POST request into our workflow is available in the `trigger` object. ```bash -echo $PIPEDREAM_STEPS | jq .trigger.event +cat $PIPEDREAM_STEPS | jq .trigger.event # Results in { id: 1, name: "Bulbasaur", type: "plant" } ``` diff --git a/docs/docs/code/nodejs/browser-automation/README.md b/docs/docs/code/nodejs/browser-automation/README.md index 0f27447ba5c1d..1738606ac8bc2 100644 --- a/docs/docs/code/nodejs/browser-automation/README.md +++ b/docs/docs/code/nodejs/browser-automation/README.md @@ -524,7 +524,7 @@ export default defineComponent({ await page.keyboard.type("Hello, from a Pipedream workflow."); await page.click('input[type=submit]'); - await page.context().browser(); + await page.context().close(); await browser.close(); }, }); diff --git a/docs/docs/limits/README.md b/docs/docs/limits/README.md index b50a4ec92246e..2ee11f63baeae 100644 --- a/docs/docs/limits/README.md +++ b/docs/docs/limits/README.md @@ -25,9 +25,15 @@ The limit of active workflows depends on your current plan. [See our pricing pag ## Daily Credits Limit -Free Pipedream accounts have a limit of number of free incuded credits per day. Paid plans do not have a daily credit limit. [See our pricing page](https://pipedream.com/pricing) for a breakdown of Free tier limits. +Free Pipedream accounts are limited to {{$site.themeConfig.DAILY_CREDITS_LIMIT}} [credits](/pricing/#credits) per day. Paid plans do not have a daily credit limit. -You can view your credits usage in your [Billing and Usage Settings](https://pipedream.com/settings/billing). Here you'll find your usage for the last 30 days, broken out by day, and by source / workflow. +You can view your credits usage at the bottom-left of the Pipedream UI. + +
+ +
+ +You can also see more detailed usage in [Billing and Usage Settings](https://pipedream.com/settings/billing). Here you'll find your usage for the last 30 days, broken out by day, by resource (e.g. your source / workflow). Your included credits count is reset, daily, at 00:00 (midnight) UTC. diff --git a/docs/docs/pricing/README.md b/docs/docs/pricing/README.md index e29f8f154ecbf..c32797703c93f 100644 --- a/docs/docs/pricing/README.md +++ b/docs/docs/pricing/README.md @@ -7,7 +7,7 @@ next: false We believe anyone should be able to run simple, low-volume workflows at no cost. We also hope that you share your [sources](/components#sources), [workflows](/workflows), [actions](/components#actions), and other integration components so that other Pipedream users benefit from your work. -To support these goals, **Pipedream offers a [free trial of our Advanced plan](#free-trial) and a generous [free tier](#free-tier)**. You can run sources and workflows for free within the limits of the free tier. If you hit these limits, you can upgrade to one of our [paid tiers](#paid-tiers). +To support these goals, Pipedream offers a generous [free tier](#free-tier), and you can **[request a free trial of our Advanced or Business plan](https://pipedream.com/pricing)**. You can run sources and workflows for free within the limits of the free tier. If you hit these limits, you can upgrade to one of our [paid tiers](#paid-tiers). [Read more about our plans and pricing here](https://pipedream.com/pricing). @@ -27,9 +27,7 @@ Free Tier accounts can connect up to 3 different service accounts like Twitter, ### Free Tier Workflow Limitations -Free Tier accounts have a [daily credit limit](/limits#daily-credits-limit) and have the lowest amount of active workflows available. - -Upgrading to a [paid tier](https://pipedream.com/pricing) will increase the number of available active workflows and connected accounts. +Free Tier accounts have a [daily credit limit](/limits#daily-credits-limit) and have limits on the number of active workflows. Upgrading to a [paid tier](https://pipedream.com/pricing) will increase the number of available active workflows and connected accounts. ### Free Tier Polling Interval Limitations @@ -37,15 +35,15 @@ Free Tier account triggers powered by polling are limited to the longest interva ### Free Tier Support options -Users on the Developer Tier have access to community support, on [our forum](https://pipedream.com/community) and Slack. [Visit out Support page](https://pipedream.com/support) for more information. +Users on the Free Tier have access to community support, on [our forum](https://pipedream.com/community) and Slack. [Visit out Support page](https://pipedream.com/support) for more information. ## Paid Tiers -[Visit our pricing page](https://pipedream.com/pricing) to learn more about our paid plan options. +[Visit our pricing page](https://pipedream.com/pricing) to learn more about our paid plans. All paid plans vary features based on tier, but each paid plan option will: -- Lift the daily 100 credits limit +- Remove the daily {{$site.themeConfig.DAILY_CREDITS_LIMIT}} [credits](#credits) limit - Increase the number of active workflows available - Increase the number of connected accounts diff --git a/docs/docs/sources/README.md b/docs/docs/sources/README.md index 5c48ed95cc332..a37628dfaa98c 100644 --- a/docs/docs/sources/README.md +++ b/docs/docs/sources/README.md @@ -86,6 +86,9 @@ You can run an event source that polls an RSS for new items and emits them in re [**Create an RSS event source here**](https://pipedream.com/sources/new?app=rss&key=rss-new-item-in-feed). ## Test Events + + + Test events are critical for developing workflows effectively. Without a test event, you won't be able to test your workflow end to end in the workflow builder or see the shape of the event data that triggers the workflow. Test events can be sourced from recent data, generated by real actions, or generated manually. diff --git a/docs/docs/user-settings/README.md b/docs/docs/user-settings/README.md index 0ac75bbd2fe92..1bbeb56779d00 100644 --- a/docs/docs/user-settings/README.md +++ b/docs/docs/user-settings/README.md @@ -17,6 +17,10 @@ Pipedream marketing emails may still be sent to the original email address you u ### Two-Factor Authentication +
+ + + Two-factor authentication (2FA) adds an additional layer of security for your Pipedream account and is recommended for all users. #### Configuring 2FA @@ -53,6 +57,15 @@ Pipedream recommends enabling 2FA with your identity provider. ::: +#### Requiring 2-Factor Authentication +Workspaces on the Business and Enterprise plans can [require all workspace members to configure 2FA](/workspaces/#requiring-two-factor-authentication) in order to log in to Pipedream. + +If you are a member of any workspace where 2FA is required, you cannot disable 2FA, but you can still reconfigure it in your [account settings](https://pipedream.com/account/) if necessary. + +:::tip Admins and Owners control 2FA settings + +Only workspace owner and admin members can enable or disable 2FA for an entire workspace. +::: ### Pipedream API Key Pipedream provides a [REST API](/api/) for interacting with Pipedream programmatically. You'll find your API key here, which you use to [authorize requests to the API](/api/auth/). diff --git a/docs/docs/workflows/steps/README.md b/docs/docs/workflows/steps/README.md index 8cb1fbc81ba90..4c6ebf58c2148 100644 --- a/docs/docs/workflows/steps/README.md +++ b/docs/docs/workflows/steps/README.md @@ -67,4 +67,30 @@ For examples of supported data types in your steps language, see the examples be * [Bash](/code/bash/#sharing-data-between-steps) * [Go](/code/go/#sharing-data-between-steps) +## Step Notes + +Pipedream lets you add notes to individual steps in your workflow so you can include helpful context to other workspace members or even yourself, and you can even write markdown! + +![Viewing step notes](https://res.cloudinary.com/pipedreamin/image/upload/v1698167274/view_notes_zto8bp.png) + +### Adding or editing a note +1. Enter build mode on any workflow +2. Click into the overflow menu (3 dots) at the top right of any step +3. Select **Add note** (or **Edit note** if making changes to an existing note) +4. Add any text or markdown, then click **Update** + +![Add note](https://res.cloudinary.com/pipedreamin/image/upload/v1698167274/add_note_kvvxju.png) + +![Adding step notes](https://res.cloudinary.com/pipedreamin/image/upload/v1698167274/add_note_zg62i2.gif) + +### Showing notes +1. Any step that has a note will indicate this with a special icon (shown below) +2. Click on the icon to hide or show the note + +![View notes](https://res.cloudinary.com/pipedreamin/image/upload/v1698167274/view_note_p5uvkg.gif) + +### Current limitations +- Step notes are not currently included when copying or [sharing](/workflows/sharing) a workflow +- Step notes are only accessible in Edit mode, and not from the Inspector +