UNPKG

@crowdin/app-project-module

Version:

Module that generates for you all common endpoints for serving standalone Crowdin App

452 lines (451 loc) 21.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.groupFieldsByCategory = exports.getOAuthLoginFormId = exports.getOAuthPollingId = exports.constructOauthUrl = exports.applyIntegrationModuleDefaults = exports.getOauthRoute = exports.getRootFolder = void 0; const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions")); const types_1 = require("../types"); function getRootFolder(config, integration, client, projectId) { return __awaiter(this, void 0, void 0, function* () { if (!integration.withRootFolder) { return; } const folder = integration.appFolderName || config.name; const directories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId)).data.map((d) => d.data); const { folder: rootFolder } = yield crowdinAppFunctions.getOrCreateFolder({ directories, client, projectId, directoryName: folder, }); return rootFolder; }); } exports.getRootFolder = getRootFolder; function getOauthRoute(integration) { var _a; return ((_a = integration.oauthLogin) === null || _a === void 0 ? void 0 : _a.redirectUriRoute) || '/oauth/code'; } exports.getOauthRoute = getOauthRoute; function applyIntegrationModuleDefaults(config, integration) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; if (!integration.getCrowdinFiles) { integration.getCrowdinFiles = (projectId, client, rootFolder, config, mode) => __awaiter(this, void 0, void 0, function* () { let options = {}; if (rootFolder) { options = { directoryId: rootFolder.id, recursion: 'true', }; } const needDirectories = !mode || mode === 'directories' || mode === 'files'; const needFiles = !mode || mode === 'files'; const [branchesResponse, directoriesResponse, filesResponse] = yield Promise.all([ client.sourceFilesApi.withFetchAll().listProjectBranches(projectId), needDirectories ? client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, options) : { data: [] }, needFiles ? client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options) : { data: [] }, ]); const allBranches = branchesResponse.data.map((d) => d.data); const allDirectories = directoriesResponse.data.map((d) => d.data); const allFiles = filesResponse.data.map((d) => d.data); const branchesMap = new Map(allBranches.map((b) => [b.id, b])); const addedBranchIds = new Set(); const res = []; const addBranchIfNeeded = (branchId) => { const branch = branchesMap.get(branchId); if (branch && !addedBranchIds.has(branch.id)) { addedBranchIds.add(branch.id); res.push({ id: branch.id.toString(), name: branch.name, nodeType: '2', }); } return branch === null || branch === void 0 ? void 0 : branch.id; }; if (!mode || mode === 'directories') { allDirectories.forEach((e) => { let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId; if (!parentId && e.branchId) { parentId = addBranchIfNeeded(e.branchId); } res.push({ id: e.id.toString(), parentId: parentId ? parentId.toString() : undefined, name: e.name, }); }); } if (!mode || mode === 'files') { const directoryIds = mode === 'files' ? allDirectories.map((d) => d.id) : res.filter((item) => !item.type && item.nodeType !== '2').map((d) => parseInt(d.id)); const filteredFiles = allFiles.filter((f) => (rootFolder && f.directoryId === rootFolder.id) || directoryIds.includes(f.directoryId) || (!rootFolder && !f.directoryId)); filteredFiles.forEach((e) => { let parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId; if (!parentId && e.branchId) { parentId = addBranchIfNeeded(e.branchId); } res.push({ id: e.id.toString(), parentId: parentId ? parentId.toString() : undefined, name: e.title || e.name, type: e.type, excludedTargetLanguages: e.excludedTargetLanguages, }); }); } return res; }); } if (!integration.getFileProgress) { integration.getFileProgress = (projectId, client, fileId) => __awaiter(this, void 0, void 0, function* () { const progress = yield client.translationStatusApi.withFetchAll().getFileProgress(projectId, fileId); return { [fileId]: progress.data.map((e) => e.data) }; }); } if (!integration.oauthLogin && !integration.loginForm) { integration.loginForm = { fields: [ { helpText: 'You need to create standard api key', key: 'apikey', label: `${config.name} API Key`, }, ], }; } if ((integration.filterByPathIntegrationFiles === undefined || integration.filterByPathIntegrationFiles) && !integration.integrationOneLevelFetching) { integration.filterByPathIntegrationFiles = true; } const getUserSettings = integration.getConfiguration; integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => __awaiter(this, void 0, void 0, function* () { var _p, _q, _r; let fields = []; const project = (yield crowdinClient.projectsGroupsApi.getProject(projectId)); if (getUserSettings) { fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials); } const defaultSettings = []; if (project.data.inContext) { defaultSettings.push({ key: 'inContext', label: 'Show In-Context Pseudo Language', type: 'checkbox', defaultValue: 'false', category: types_1.DefaultCategory.GENERAL, }); } if (integration.withCronSync || integration.webhooks) { const userSchedule = fields.find((field) => 'key' in field && field.key === 'schedule'); if (userSchedule) { userSchedule.position = (_p = userSchedule.position) !== null && _p !== void 0 ? _p : 0; userSchedule.category = types_1.DefaultCategory.SYNC; } else { defaultSettings.push({ key: 'schedule', label: 'Sync schedule', helpText: `Defines how often content is synced between ${config.name} and Crowdin. Make sure Auto Sync is enabled for selected directories and files in the dual pane view.`, type: 'select', defaultValue: '0', category: types_1.DefaultCategory.SYNC, position: 0, options: [ { value: '0', label: 'Disabled', }, { value: '1', label: '1 hour', }, { value: '3', label: '3 hours', }, { value: '6', label: '6 hours', }, { value: '12', label: '12 hours', }, { value: '24', label: '24 hours', }, ], }); } if ((_q = integration.syncNewElements) === null || _q === void 0 ? void 0 : _q.crowdin) { defaultSettings.push({ key: 'new-crowdin-files', label: 'Automatically sync new translations from Crowdin', type: 'checkbox', dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]), category: types_1.DefaultCategory.SYNC, position: 1, }); } if ((_r = integration.syncNewElements) === null || _r === void 0 ? void 0 : _r.integration) { defaultSettings.push({ key: 'new-integration-files', label: `Automatically sync new content from ${config.name}`, type: 'checkbox', dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]), category: types_1.DefaultCategory.SYNC, position: 2, }); } if (integration.uploadTranslations) { defaultSettings.push({ labelHtml: `<b>Translation sync settings (${config.name} → Crowdin)</b>`, category: types_1.DefaultCategory.SYNC, position: 3, }, { key: 'importEqSuggestions', label: 'Add translations that are the same as the source text', type: 'checkbox', category: types_1.DefaultCategory.SYNC, position: 4, }, { key: 'autoApproveImported', label: 'Mark added translations as Approved in Crowdin', type: 'checkbox', category: types_1.DefaultCategory.SYNC, position: 5, }, { key: 'translateHidden', label: 'Add translations for hidden source strings in Crowdin', type: 'checkbox', category: types_1.DefaultCategory.SYNC, position: 6, }); } defaultSettings.push({ key: 'condition', label: 'Files export settings', type: 'select', defaultValue: '0', dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]), category: types_1.DefaultCategory.SYNC, position: 7, options: [ { value: '0', label: 'Export all', }, { value: '1', label: 'Export translated only', }, { value: '2', label: 'Export approved only', }, ], }); } if (integration.filterByPathIntegrationFiles) { defaultSettings.push({ label: 'File Filters', }, { key: 'includeByFilePath', label: 'Source files path', type: 'textarea', helpText: 'Enter the file path patterns to include files for synchronization. Use wildcard selectors like `*` and `**` to match multiple files. Example: `/pages/**', }, { key: 'excludeByFilePath', label: 'Ignore files or folders', type: 'textarea', helpText: 'Enter the path patterns for files or folders to exclude. Use wildcard selectors like `*` and `**` to match multiple files. Example: `/drafts/**`', }); } if (integration.skipIntegrationNodes && integration.skipIntegrationNodesToggle) { defaultSettings.push({ key: 'skipIntegrationNodesToggle', label: integration.skipIntegrationNodesToggle.title, type: 'checkbox', helpText: integration.skipIntegrationNodesToggle.description, defaultValue: integration.skipIntegrationNodesToggle.value, category: types_1.DefaultCategory.GENERAL, }); } return [...defaultSettings, ...fields]; }); if (!integration.checkConnection) { integration.checkConnection = (apiCredentials) => __awaiter(this, void 0, void 0, function* () { yield integration.getIntegrationFiles(apiCredentials); }); } if (integration.webhooks && !((_a = integration.webhooks) === null || _a === void 0 ? void 0 : _a.urlParam)) { integration.webhooks.urlParam = 'crowdinData'; } if (!((_b = integration.filtering) === null || _b === void 0 ? void 0 : _b.hasOwnProperty('crowdinLanguages'))) { integration.filtering = Object.assign(Object.assign({}, (integration.filtering || {})), { crowdinLanguages: true }); } integration.filtering.integrationFileStatus = Object.assign(Object.assign({}, (integration.integrationOneLevelFetching ? {} : { notSynced: true })), integration.filtering.integrationFileStatus); if (!((_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.hasOwnProperty('integrationFilterConfig'))) { const filterItems = [ { value: 'all', label: 'All', }, ]; if ((_e = (_d = integration.filtering) === null || _d === void 0 ? void 0 : _d.integrationFileStatus) === null || _e === void 0 ? void 0 : _e.isNew) { filterItems.push({ value: 'isNew', label: 'New', }); } if ((_g = (_f = integration.filtering) === null || _f === void 0 ? void 0 : _f.integrationFileStatus) === null || _g === void 0 ? void 0 : _g.isUpdated) { filterItems.push({ value: 'isUpdated', label: 'Modified', }); } if ((_j = (_h = integration.filtering) === null || _h === void 0 ? void 0 : _h.integrationFileStatus) === null || _j === void 0 ? void 0 : _j.failed) { filterItems.push({ value: 'failed', label: 'Sync Error', }); } if ((_l = (_k = integration.filtering) === null || _k === void 0 ? void 0 : _k.integrationFileStatus) === null || _l === void 0 ? void 0 : _l.notSynced) { filterItems.push({ value: 'notSynced', label: 'Never Synced', }); } if ((_o = (_m = integration.filtering) === null || _m === void 0 ? void 0 : _m.integrationFileStatus) === null || _o === void 0 ? void 0 : _o.synced) { filterItems.push({ value: 'synced', label: 'Previously Synced', }); } integration.filtering = Object.assign(Object.assign({}, (integration.filtering || {})), { integrationFilterConfig: filterItems.length > 1 ? [ { key: 'file', type: 'list_single', label: 'File', items: filterItems, defaultValue: 'all', defaultLabel: 'All', }, ] : [] }); } if (!integration.userErrorLifetimeDays) { integration.userErrorLifetimeDays = 30; } integration.jobStoreType = integration.jobStoreType || 'db'; } exports.applyIntegrationModuleDefaults = applyIntegrationModuleDefaults; function constructOauthUrl({ config, integration, clientId, loginForm, }) { var _a, _b, _c, _d, _e; const oauth = integration.oauthLogin; if (!oauth) { return; } if (oauth.getAuthorizationUrl) { if (!loginForm) { return; } let url = oauth.getAuthorizationUrl(`${config.baseUrl}${getOauthRoute(integration)}`, loginForm); url += `&${((_a = oauth.fieldsMapping) === null || _a === void 0 ? void 0 : _a.state) || 'state'}=${Buffer.from(clientId).toString('base64')}`; return url; } if (!oauth.authorizationUrl) { return; } let url = oauth.authorizationUrl || ''; url += `?${((_b = oauth.fieldsMapping) === null || _b === void 0 ? void 0 : _b.clientId) || 'client_id'}=${oauth.clientId}`; url += `&${((_c = oauth.fieldsMapping) === null || _c === void 0 ? void 0 : _c.redirectUri) || 'redirect_uri'}=${config.baseUrl}${getOauthRoute(integration)}`; url += `&${((_d = oauth.fieldsMapping) === null || _d === void 0 ? void 0 : _d.state) || 'state'}=${Buffer.from(clientId).toString('base64')}`; if (oauth.scope) { url += `&${((_e = oauth.fieldsMapping) === null || _e === void 0 ? void 0 : _e.scope) || 'scope'}=${oauth.scope}`; } if (oauth.extraAutorizationUrlParameters) { Object.entries(oauth.extraAutorizationUrlParameters).forEach(([key, value]) => (url += `&${key}=${value}`)); } return url; } exports.constructOauthUrl = constructOauthUrl; function getOAuthPollingId(clientId) { return `oauth_${clientId}`; } exports.getOAuthPollingId = getOAuthPollingId; function getOAuthLoginFormId(clientId) { return `oauth_form_${clientId}`; } exports.getOAuthLoginFormId = getOAuthLoginFormId; function groupFieldsByCategory(fields) { const groupedFields = fields.reduce((acc, field) => { const category = (field === null || field === void 0 ? void 0 : field.category) || types_1.DefaultCategory.GENERAL; if (!acc[category]) { acc[category] = []; } acc[category].push(field); return acc; }, {}); // Sort fields by position within each category Object.keys(groupedFields).forEach((category) => { groupedFields[category].sort((a, b) => { // If neither has position, maintain original order if (!('position' in a) && !('position' in b)) { return 0; } // If only one has position, the one without position goes to the end if (!('position' in a)) { return 1; } if (!('position' in b)) { return -1; } // If both have position, sort by position value (lower numbers first) return (a.position || 0) - (b.position || 0); }); }); return Object.entries(groupedFields).map(([category, fields]) => ({ name: category, fields, })); } exports.groupFieldsByCategory = groupFieldsByCategory;