diff --git a/components/lokalise/actions/create-project/create-project.mjs b/components/lokalise/actions/create-project/create-project.mjs new file mode 100644 index 0000000000000..59a040a866872 --- /dev/null +++ b/components/lokalise/actions/create-project/create-project.mjs @@ -0,0 +1,60 @@ +import lokalise from "../../lokalise.app.mjs"; + +export default { + key: "lokalise-create-project", + name: "Create Project", + description: "Initializes an empty project in Lokalise. [See the documentation](https://developers.lokalise.com/reference/create-a-project)", + version: "0.0.1", + type: "action", + props: { + lokalise, + name: { + type: "string", + label: "Name", + description: "Name of the project", + }, + description: { + type: "string", + label: "Description", + description: "Description of the project", + optional: true, + }, + language: { + propDefinition: [ + lokalise, + "language", + ], + optional: true, + }, + projectType: { + type: "string", + label: "Project Type", + description: "The type of project", + options: [ + "localization_files", + "paged_documents", + ], + optional: true, + }, + isSegmentationEnabled: { + type: "boolean", + label: "Is Segmentation Enabled", + description: "Whether to enable Segmentation feature for project", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.lokalise.createProject({ + $, + data: { + name: this.name, + description: this.description, + base_lang_iso: this.language, + project_type: this.projectType, + is_segmentation_enabled: this.isSegmentationEnabled, + }, + }); + $.export("$summary", `Successfully created project with ID: ${response.project_id}`); + return response; + }, +}; diff --git a/components/lokalise/actions/download-files/download-files.mjs b/components/lokalise/actions/download-files/download-files.mjs new file mode 100644 index 0000000000000..cd86bbe73df10 --- /dev/null +++ b/components/lokalise/actions/download-files/download-files.mjs @@ -0,0 +1,35 @@ +import lokalise from "../../lokalise.app.mjs"; + +export default { + key: "lokalise-download-files", + name: "Download Files", + description: "Retrieves and downloads files from a specified Lokalise project. [See the documentation](https://developers.lokalise.com/reference/download-files)", + version: "0.0.1", + type: "action", + props: { + lokalise, + projectId: { + propDefinition: [ + lokalise, + "projectId", + ], + }, + fileFormat: { + type: "string", + label: "File Format", + description: "File format (e.g. json, strings, xml). Must be file extension of any of the [supported file formats](https://docs.lokalise.com/en/collections/2909229-supported-file-formats). May also be `ios_sdk` or `android_sdk` for respective OTA SDK bundles.", + }, + }, + async run({ $ }) { + const response = await this.lokalise.downloadFiles({ + $, + projectId: this.projectId, + data: { + format: this.fileFormat, + }, + }); + + $.export("$summary", `Successfully downloaded files from project ${this.projectId}`); + return response; + }, +}; diff --git a/components/lokalise/actions/upload-file/upload-file.mjs b/components/lokalise/actions/upload-file/upload-file.mjs new file mode 100644 index 0000000000000..510bf37d2734a --- /dev/null +++ b/components/lokalise/actions/upload-file/upload-file.mjs @@ -0,0 +1,53 @@ +import lokalise from "../../lokalise.app.mjs"; +import fs from "fs"; + +export default { + key: "lokalise-upload-file", + name: "Upload File", + description: "Uploads a specified file to a Lokalise project. [See the documentation](https://developers.lokalise.com/reference/upload-a-file)", + version: "0.0.1", + type: "action", + props: { + lokalise, + projectId: { + propDefinition: [ + lokalise, + "projectId", + ], + }, + filePath: { + type: "string", + label: "File Path", + description: "The path to a file of a [supported file format](https://docs.lokalise.com/en/collections/2909229-supported-file-formats) in the `/tmp` directory. [See the documentation on working with files](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp).", + }, + language: { + propDefinition: [ + lokalise, + "language", + ], + }, + filename: { + type: "string", + label: "Filename", + description: "Set the filename. You may optionally use a relative path in the filename", + }, + }, + async run({ $ }) { + const fileData = fs.readFileSync(this.filePath.startsWith("/tmp") + ? this.filePath + : `/tmp/${this.filePath}`, { + encoding: "base64", + }); + const response = await this.lokalise.uploadFile({ + $, + projectId: this.projectId, + data: { + data: fileData, + filename: this.filename, + lang_iso: this.language, + }, + }); + $.export("$summary", "Successfully uploaded file"); + return response; + }, +}; diff --git a/components/lokalise/lokalise.app.mjs b/components/lokalise/lokalise.app.mjs index 9eb7316a8fd8d..6739459519378 100644 --- a/components/lokalise/lokalise.app.mjs +++ b/components/lokalise/lokalise.app.mjs @@ -1,11 +1,118 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "lokalise", - propDefinitions: {}, + propDefinitions: { + projectId: { + type: "string", + label: "Project ID", + description: "Identifier of a project", + async options({ page }) { + const { projects } = await this.listProjects({ + params: { + page: page + 1, + }, + }); + return projects?.map(({ + project_id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + language: { + type: "string", + label: "Language", + description: "Language/locale code of the project base language", + async options({ page }) { + const { languages } = await this.listLanguages({ + params: { + page: page + 1, + }, + }); + return languages?.map(({ + lang_iso: value, lang_name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.lokalise.com/api2"; + }, + _makeRequest(opts = {}) { + const { + $ = this, + path, + ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + url: `${this._baseUrl()}${path}`, + headers: { + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + }, + }); + }, + createWebhook({ + projectId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/projects/${projectId}/webhooks`, + ...opts, + }); + }, + deleteWebhook({ + projectId, hookId, ...opts + }) { + return this._makeRequest({ + method: "DELETE", + path: `/projects/${projectId}/webhooks/${hookId}`, + ...opts, + }); + }, + listProjects(opts = {}) { + return this._makeRequest({ + path: "/projects", + ...opts, + }); + }, + listLanguages(opts = {}) { + return this._makeRequest({ + path: "/system/languages", + ...opts, + }); + }, + createProject(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/projects", + ...opts, + }); + }, + uploadFile({ + projectId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/projects/${projectId}/files/upload`, + ...opts, + }); + }, + downloadFiles({ + projectId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/projects/${projectId}/files/download`, + ...opts, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/lokalise/package.json b/components/lokalise/package.json index 95c09afa23e44..c8853ae704b26 100644 --- a/components/lokalise/package.json +++ b/components/lokalise/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/lokalise", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Lokalise Components", "main": "lokalise.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } -} \ No newline at end of file +} diff --git a/components/lokalise/sources/common/base.mjs b/components/lokalise/sources/common/base.mjs new file mode 100644 index 0000000000000..217cc694aa591 --- /dev/null +++ b/components/lokalise/sources/common/base.mjs @@ -0,0 +1,65 @@ +import lokalise from "../../lokalise.app.mjs"; + +export default { + props: { + lokalise, + http: "$.interface.http", + db: "$.service.db", + projectId: { + propDefinition: [ + lokalise, + "projectId", + ], + }, + }, + hooks: { + async activate() { + const { webhook } = await this.lokalise.createWebhook({ + projectId: this.projectId, + data: { + url: this.http.endpoint, + events: this.getEvents(), + }, + }); + this._setHookId(webhook.webhook_id); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.lokalise.deleteWebhook({ + projectId: this.projectId, + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta(event) { + return { + id: `${event.event}-${event.created_at_timestamp}`, + summary: this.getSummary(event), + ts: event.created_at_timestamp, + }; + }, + getEvents() { + throw new Error("getEvents is not implemented"); + }, + getSummary() { + throw new Error("getSummary is not implemented"); + }, + }, + async run(event) { + const { body } = event; + if (!body.event) { + return; + } + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, +}; diff --git a/components/lokalise/sources/new-task-closed-instant/new-task-closed-instant.mjs b/components/lokalise/sources/new-task-closed-instant/new-task-closed-instant.mjs new file mode 100644 index 0000000000000..3b9f209118fd3 --- /dev/null +++ b/components/lokalise/sources/new-task-closed-instant/new-task-closed-instant.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "lokalise-new-task-closed-instant", + name: "New Task Closed (Instant)", + description: "Emit new event when a task is closed in Lokalise", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "project.task.closed", + ]; + }, + getSummary({ task }) { + return `Task Closed with ID: ${task.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/lokalise/sources/new-task-closed-instant/test-event.mjs b/components/lokalise/sources/new-task-closed-instant/test-event.mjs new file mode 100644 index 0000000000000..38e6458d8db97 --- /dev/null +++ b/components/lokalise/sources/new-task-closed-instant/test-event.mjs @@ -0,0 +1,21 @@ +export default { + "event": "project.task.closed", + "task": { + "id": 5022, + "type": "translation", + "title": "Headings translation", + "due_date": "2019-08-01 00:00:00", + "description": "Task description" + }, + "project": { + "id": "138c1ffa0ad94848f01f980e7f2f2af19d1bd553", + "name": "TheApp Project", + "branch": "master" + }, + "user": { + "email": "jdoe@mycompany.com", + "full_name": "John Doe" + }, + "created_at": "2019-07-29 12:18:31", + "created_at_timestamp": 1564395511 +} \ No newline at end of file diff --git a/components/lokalise/sources/new-task-created-instant/new-task-created-instant.mjs b/components/lokalise/sources/new-task-created-instant/new-task-created-instant.mjs new file mode 100644 index 0000000000000..a342548b70132 --- /dev/null +++ b/components/lokalise/sources/new-task-created-instant/new-task-created-instant.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "lokalise-new-task-created-instant", + name: "New Task Created (Instant)", + description: "Emit new event when a new task is created in Lokalise", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "project.task.created", + ]; + }, + getSummary({ task }) { + return `New Task with ID: ${task.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/lokalise/sources/new-task-created-instant/test-event.mjs b/components/lokalise/sources/new-task-created-instant/test-event.mjs new file mode 100644 index 0000000000000..6aea90f65171d --- /dev/null +++ b/components/lokalise/sources/new-task-created-instant/test-event.mjs @@ -0,0 +1,21 @@ +export default { + "event": "project.task.created", + "task": { + "id": 5022, + "type": "translation", + "title": "Headings translation", + "due_date": "2019-08-01 00:00:00", + "description": "Task description" + }, + "project": { + "id": "138c1ffa0ad94848f01f980e7f2f2af19d1bd553", + "name": "TheApp Project", + "branch": "master" + }, + "user": { + "email": "jdoe@mycompany.com", + "full_name": "John Doe" + }, + "created_at": "2019-07-29 12:18:31", + "created_at_timestamp": 1564395511 +} \ No newline at end of file diff --git a/components/lokalise/sources/project-imported-instant/project-imported-instant.mjs b/components/lokalise/sources/project-imported-instant/project-imported-instant.mjs new file mode 100644 index 0000000000000..a61a9b69241ec --- /dev/null +++ b/components/lokalise/sources/project-imported-instant/project-imported-instant.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "lokalise-project-imported-instant", + name: "New Project Imported (Instant)", + description: "Emit new event when data is imported into a project", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvents() { + return [ + "project.imported", + ]; + }, + getSummary() { + return "Data imported to project"; + }, + }, + sampleEmit, +}; diff --git a/components/lokalise/sources/project-imported-instant/test-event.mjs b/components/lokalise/sources/project-imported-instant/test-event.mjs new file mode 100644 index 0000000000000..ed85fc7c05d41 --- /dev/null +++ b/components/lokalise/sources/project-imported-instant/test-event.mjs @@ -0,0 +1,43 @@ +export default { + "event": "project.imported", + "import": { + "filename": "ru.yml", + "format": "yml", + "inserted": 231, + "updated": 0, + "skipped": 0 + }, + "import_options": { + "replace_line_breaks": false, + "convert_placeholders": true, + "replace_modified": false, + "key_tags": [ + "tag1", "tag2" + ], + "tag_keys_inserted": true, + "tag_keys_updated": true, + "tag_keys_skipped": false, + "detect_icu_plurals": true, + "fill_empty_with_keys": false, + "hide_from_contributors": false, + "diff_by_file": false, + "use_tm": false, + "cleanup": false + }, + "project": { + "id": "138c1ffa0ad94848f01f980e7f2f2af19d1bd553", + "name": "TheApp Project", + "branch": "develop" + }, + "language": { + "id": 597, + "iso": "ru", + "name": "Russian" + }, + "user": { + "email": "jdoe@mycompany.com", + "full_name": "John Doe" + }, + "created_at": "2019-07-29 12:18:31", + "created_at_timestamp": 1564395511 +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cc3b372e7b90..74ac4a4dc562f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5816,7 +5816,10 @@ importers: specifiers: {} components/lokalise: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 components/looker: specifiers: {} @@ -13347,6 +13350,55 @@ packages: - aws-crt dev: false + /@aws-sdk/client-sso-oidc/3.600.0_tdq3komn4zwyd65w7klbptsu34: + resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 + '@smithy/config-resolver': 3.0.3 + '@smithy/core': 2.2.3 + '@smithy/fetch-http-handler': 3.2.1 + '@smithy/hash-node': 3.0.2 + '@smithy/invalid-dependency': 3.0.2 + '@smithy/middleware-content-length': 3.0.2 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.6 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.2 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.6 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.6 + '@smithy/util-defaults-mode-node': 3.0.6 + '@smithy/util-endpoints': 2.0.3 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sts' + - aws-crt + dev: false + /@aws-sdk/client-sso/3.423.0: resolution: {integrity: sha512-znIufHkwhCIePgaYciIs3x/+BpzR57CZzbCKHR9+oOvGyufEPPpUT5bFLvbwTgfiVkTjuk6sG/ES3U5Bc+xtrA==} engines: {node: '>=14.0.0'} @@ -13582,7 +13634,7 @@ packages: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0_tdq3komn4zwyd65w7klbptsu34 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 '@aws-sdk/middleware-host-header': 3.598.0 @@ -13624,55 +13676,6 @@ packages: - aws-crt dev: false - /@aws-sdk/client-sts/3.600.0_dseaa2p5u2yk67qiepewcq3hkq: - resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.2.1 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.2 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.6 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.2 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - dev: false - /@aws-sdk/core/3.556.0: resolution: {integrity: sha512-vJaSaHw2kPQlo11j/Rzuz0gk1tEaKdz+2ser0f0qZ5vwFlANjt08m/frU17ctnVKC1s58bxpctO/1P894fHLrA==} engines: {node: '>=14.0.0'} @@ -18016,7 +18019,7 @@ packages: '@aws-sdk/client-sns': 3.423.0 '@aws-sdk/client-sqs': 3.423.0 '@aws-sdk/client-ssm': 3.423.0 - '@aws-sdk/client-sts': 3.600.0_dseaa2p5u2yk67qiepewcq3hkq + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/s3-request-presigner': 3.609.0 '@pipedream/helper_functions': 0.3.12 '@pipedream/platform': 1.6.6