@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
JavaScript
;
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;