@crowdin/app-project-module
Version:
Module that generates for you all common endpoints for serving standalone Crowdin App
1,019 lines (1,018 loc) • 47.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getApiManifest = getApiManifest;
exports.updateCrowdinContext = updateCrowdinContext;
exports.addDefaultApiEndpoints = addDefaultApiEndpoints;
exports.addSwagerApiDocumentation = addSwagerApiDocumentation;
const path_1 = __importDefault(require("path"));
const swagger_jsdoc_1 = __importDefault(require("swagger-jsdoc"));
const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
const integration_credentials_1 = __importDefault(require("../../middlewares/integration-credentials"));
const json_response_1 = __importDefault(require("../../middlewares/json-response"));
const api_call_1 = __importDefault(require("../../middlewares/api-call"));
const redoc_html_template_1 = __importDefault(require("./redoc-helper/redoc-html-template"));
const crowdin_file_progress_1 = __importDefault(require("../integration/handlers/crowdin-file-progress"));
const crowdin_files_1 = __importDefault(require("../integration/handlers/crowdin-files"));
const crowdin_update_1 = __importDefault(require("../integration/handlers/crowdin-update"));
const integration_data_1 = __importDefault(require("../integration/handlers/integration-data"));
const job_info_deprecated_1 = __importDefault(require("../integration/handlers/job-info-deprecated"));
const job_info_1 = __importDefault(require("../integration/handlers/job-info"));
const job_list_1 = __importDefault(require("../integration/handlers/job-list"));
const job_cancel_1 = __importDefault(require("../integration/handlers/job-cancel"));
const integration_login_1 = __importDefault(require("../integration/handlers/integration-login"));
const integration_update_1 = __importDefault(require("../integration/handlers/integration-update"));
const settings_1 = __importDefault(require("../integration/handlers/settings"));
const settings_save_1 = __importDefault(require("../integration/handlers/settings-save"));
const settings_schema_1 = __importDefault(require("../integration/handlers/settings-schema"));
const sync_settings_1 = __importDefault(require("../integration/handlers/sync-settings"));
const sync_settings_save_1 = __importDefault(require("../integration/handlers/sync-settings-save"));
const types_1 = require("./types");
function getApiManifest(config, apiModule) {
const apiModuleManifest = [];
if (apiModule.endpoints) {
const userEndpoints = getUsersApiManifest(apiModule.endpoints);
apiModuleManifest.push(...userEndpoints);
}
if (apiModule.default) {
const defaultEndpoints = getDefaultApiEndpointsManifest(config);
apiModuleManifest.push(...defaultEndpoints);
}
return apiModuleManifest;
}
function generateModuleKey(moduleName) {
return moduleName.toLowerCase().split(' ').join('-') + '-api';
}
function getUsersApiManifest(endpoints) {
const apiModuleManifest = [];
for (const endpoint of endpoints) {
apiModuleManifest.push(Object.assign({ key: generateModuleKey(endpoint.name), name: endpoint.name, url: endpoint.url, method: endpoint.method, description: endpoint.description }, (endpoint.documentationUrl ? { documentationUrl: endpoint.documentationUrl } : {})));
}
return apiModuleManifest;
}
function getDefaultApiEndpointsManifest(config) {
const apiModuleManifest = [];
if (config.projectIntegration) {
apiModuleManifest.push({
key: 'crowdin-files-api',
name: 'Get Crowdin Files',
url: '/crowdin-files',
method: types_1.RequestMethods.GET,
description: 'Get a list of synced files',
documentationUrl: '/api-docs#tag/Files/operation/crowdin.files',
}, {
key: 'file-translation-progress-api',
name: 'File Translation Progress',
url: '/file-progress',
method: types_1.RequestMethods.GET,
description: 'Get file translation progress',
documentationUrl: '/api-docs#tag/Files/operation/file.progress',
}, {
key: 'integration-files-api',
name: 'Get Integration Files',
url: '/integration-files',
method: types_1.RequestMethods.GET,
description: 'Get integration data',
documentationUrl: '/api-docs#tag/Files/operation/integration.files',
}, {
key: 'crowdin-update-api',
name: 'Update Crowdin',
url: '/crowdin-update',
method: types_1.RequestMethods.POST,
description: 'Update crowdin data',
documentationUrl: '/api-docs#tag/Files/operation/crowdin.update',
}, {
key: 'integration-update-api',
name: 'Update Integration',
url: '/integration-update',
method: types_1.RequestMethods.POST,
description: 'Update integration data',
documentationUrl: '/api-docs#tag/Files/operation/integration.update',
}, {
key: 'job-list-api',
name: 'Job List',
url: '/all-jobs',
method: types_1.RequestMethods.GET,
description: 'Get All Jobs',
documentationUrl: '/api-docs#tag/Jobs/operation/job.list',
}, {
key: 'job-info-api',
name: 'Job Info',
url: '/job-info',
method: types_1.RequestMethods.GET,
description: 'Get Job Info',
documentationUrl: '/api-docs#tag/Jobs/operation/job.info',
}, {
key: 'job-get-api',
name: 'Job Status',
url: '/jobs',
method: types_1.RequestMethods.GET,
description: 'Get Job Info',
documentationUrl: '/api-docs#tag/Jobs/operation/job.get',
}, {
key: 'job-cancel-api',
name: 'Cancel Job',
url: '/jobs',
method: types_1.RequestMethods.DELETE,
description: 'Cancel Job',
documentationUrl: '/api-docs#tag/Jobs/operation/job.cancel',
}, {
key: 'settings-schema-api',
name: 'Get App Settings Schema',
url: '/settings/schema',
method: types_1.RequestMethods.GET,
description: 'Get settings configuration schema with field types and options',
documentationUrl: '/api-docs#tag/Settings/operation/settings.schema',
}, {
key: 'settings-api',
name: 'Get App Settings',
url: '/settings',
method: types_1.RequestMethods.GET,
documentationUrl: '/api-docs#tag/Settings/operation/settings.get',
}, {
key: 'settings-update-api',
name: 'Update App Settings',
url: '/settings',
method: types_1.RequestMethods.POST,
description: 'Update application configuration settings',
documentationUrl: '/api-docs#tag/Settings/operation/settings.update',
}, {
key: 'sync-settings-api',
name: 'Get Auto-Sync Files',
url: '/sync-settings',
method: types_1.RequestMethods.GET,
description: 'Get list of files configured for automatic synchronization',
documentationUrl: '/api-docs#tag/Settings/operation/sync.settings.get',
}, {
key: 'sync-settings-update-api',
name: 'Update Auto-Sync Files',
url: '/sync-settings',
method: types_1.RequestMethods.POST,
description: 'Configure which files should be automatically synchronized',
documentationUrl: '/api-docs#tag/Settings/operation/sync.settings.update',
});
if (config.projectIntegration.loginForm) {
apiModuleManifest.push({
key: 'login-data',
name: 'Get Login Fields',
url: '/login-fields',
method: types_1.RequestMethods.GET,
documentationUrl: '/api-docs#tag/Login/operation/integration.fields',
}, {
key: 'login',
name: 'Login',
url: '/login',
method: types_1.RequestMethods.POST,
documentationUrl: '/api-docs#tag/Login/operation/integration.login',
});
}
}
return apiModuleManifest;
}
function updateCrowdinContext(req, context) {
var _a, _b, _c;
if ((_a = req.body) === null || _a === void 0 ? void 0 : _a.projectId) {
if (context.clientId.includes('undefined')) {
context.clientId = `${context.jwtPayload.domain || context.jwtPayload.context.organization_id}__${(_b = req.body) === null || _b === void 0 ? void 0 : _b.projectId}__${context.jwtPayload.sub}`;
}
context.jwtPayload.context.project_id = (_c = req.body) === null || _c === void 0 ? void 0 : _c.projectId;
}
return context;
}
function getFormFields(fields) {
const formFields = [];
for (const field of fields) {
if (field === null || field === void 0 ? void 0 : field.key) {
formFields.push(Object.assign({ name: field.label, key: field.key, type: field.type }, (field.type === 'select' && field.options ? { options: field.options } : {})));
}
}
return formFields;
}
function addDefaultApiEndpoints(app, config) {
if (config.projectIntegration) {
/**
* @openapi
* /crowdin-files:
* get:
* summary: List Crowdin Files
* operationId: crowdin.files
* tags:
* - 'Files'
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* responses:
* 200:
* description: 'Project files list'
* content:
* application/json:
* schema:
* properties:
* data:
* $ref: '#/components/schemas/CrowdinFiles'
*/
app.get('/crowdin-files', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'crowdin-files-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, crowdin_files_1.default)(config, config.projectIntegration));
/**
* @openapi
* /file-progress:
* get:
* tags:
* - 'Files'
* summary: 'Get File Progress'
* operationId: file.progress
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* -
* name: fileId
* in: query
* required: true
* description: 'Get via [List Crowdin Files](#operation/crowdin.files)'
* schema:
* type: integer
* example: 102
* responses:
* 200:
* description: 'File translation progress'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* description: 'Translation progress data grouped by file ID'
* additionalProperties:
* type: array
* items:
* $ref: '#/components/schemas/FileProgress'
* example:
* data:
* '78866':
* - languageId: uk
* eTag: '35ddc6c8f634f062e0de95c1c159f59d'
* language:
* id: uk
* name: Ukrainian
* editorCode: uk
* twoLettersCode: uk
* threeLettersCode: ukr
* locale: uk-UA
* androidCode: uk-rUA
* osxCode: uk.lproj
* osxLocale: uk
* pluralCategoryNames: ['one', 'few', 'many', 'other']
* pluralRules: '((n%10==1 && n%100!=11) ? 0 : ...)'
* pluralExamples: ['1, 21, 31...', '2-4, 22-24...']
* textDirection: ltr
* dialectOf: null
* words:
* total: 9
* translated: 0
* preTranslateAppliedTo: 0
* approved: 0
* phrases:
* total: 3
* translated: 0
* preTranslateAppliedTo: 0
* approved: 0
* translationProgress: '0'
* approvalProgress: '0'
* qaChecksStatus:
* total: 0
* inProgress: 0
* passed: 0
* failed: 0
*/
app.get('/file-progress', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'file-translation-progress-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, crowdin_file_progress_1.default)(config.projectIntegration));
/**
* @openapi
* /integration-files:
* get:
* summary: 'List Integration Files'
* operationId: integration.files
* tags:
* - 'Files'
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* responses:
* 200:
* description: 'Integration files list'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* properties:
* files:
* $ref: '#/components/schemas/IntegrationFiles'
* message:
* type: string
* nullable: true
* description: 'Optional message (e.g., error info)'
* stopPagination:
* type: boolean
* nullable: true
* description: 'Indicates if there are no more items to load (only for pagination)'
* example:
* data:
* files:
* - id: 'space_302374::region-eu::'
* name: 'TestSpace2808'
* path: '/TestSpace2808'
* - id: 'story_134603631080538::region-eu::'
* name: 'Test Oleksii'
* parentId: 'space_302374::region-eu::'
* path: '/TestSpace2808/Test Oleksii'
* createdAt: '2026-01-16T08:23:34.544Z'
* updatedAt: '2026-01-16T08:23:34.544Z'
* - id: 'story_302374-133936904052915::region-eu::'
* name: 'The very first after release'
* type: 'html'
* parentId: 'space_302374::region-eu::'
* path: '/TestSpace2808/The very first after release'
* createdAt: '2026-01-14T11:10:39.387Z'
* updatedAt: '2026-01-14T11:11:05.165Z'
* isNew: false
* notSynced: true
* isUpdated: false
* synced: false
* message: null
*
*/
app.get('/integration-files', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'integration-files-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, integration_data_1.default)(config.projectIntegration));
/**
* @openapi
* /crowdin-update:
* post:
* tags:
* - 'Files'
* summary: 'Update Crowdin Files'
* operationId: crowdin.update
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UpdateCrowdinFiles'
* responses:
* 200:
* content:
* application/json:
* schema:
* properties:
* data:
* $ref: '#/components/schemas/UpdateResponse'
*/
app.post('/crowdin-update', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'crowdin-update-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, crowdin_update_1.default)(config, config.projectIntegration));
/**
* @openapi
* /integration-update:
* post:
* tags:
* - 'Files'
* summary: 'Update Integration Files'
* operationId: integration.update
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/FileLanguagePair'
* responses:
* 200:
* content:
* application/json:
* schema:
* properties:
* data:
* $ref: '#/components/schemas/UpdateResponse'
*/
app.post('/integration-update', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'integration-update-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, integration_update_1.default)(config, config.projectIntegration));
/**
* @openapi
* /all-jobs:
* get:
* tags:
* - 'Jobs'
* summary: 'List Jobs'
* description: 'Retrieve a paginated list of all jobs for the current integration'
* operationId: job.list
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* - name: limit
* in: query
* required: false
* description: 'Maximum number of jobs to return'
* schema:
* type: integer
* default: 25
* minimum: 1
* maximum: 100
* example: 25
* - name: offset
* in: query
* required: false
* description: 'Number of jobs to skip for pagination'
* schema:
* type: integer
* default: 0
* minimum: 0
* example: 0
* responses:
* 200:
* description: 'List of jobs retrieved successfully'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/JobListResponse'
*/
app.get('/all-jobs', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'job-list-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, job_list_1.default)());
/**
* @openapi
* /job-info:
* get:
* tags:
* - 'Jobs'
* summary: 'Get Job Info'
* description: 'Retrieve detailed information about a specific job including progress, status, and timing data'
* operationId: job.info
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* - name: jobId
* in: query
* required: true
* description: 'Unique identifier of the job. Get via [List Jobs](#operation/job.list)'
* schema:
* type: string
* example: 067da473-fc0b-43e3-b0a2-09d26af130c1
* responses:
* 200:
* description: 'Job information retrieved successfully'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/JobInfoResponse'
* 400:
* description: 'Bad Request - jobId parameter is missing'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* 404:
* description: 'Job not found'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
*/
app.get('/job-info', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'job-info-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, job_info_1.default)());
/**
* @openapi
* /jobs:
* get:
* tags:
* - 'Jobs'
* summary: 'Get Job Info'
* operationId: job.get
* deprecated: true
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* - name: jobId
* in: query
* required: false
* schema:
* type: string
* example: 067da473-fc0b-43e3-b0a2-09d26af130c1
* responses:
* 200:
* description: 'Job information retrieved successfully'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/JobResponse'
*/
app.get('/jobs', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'job-get-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, job_info_deprecated_1.default)(config));
/**
* @openapi
* /jobs:
* delete:
* tags:
* - 'Jobs'
* summary: 'Cancel Job'
* description: 'Cancel a running job. Only jobs with status "created" or "inProgress" can be canceled'
* operationId: job.cancel
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* - name: jobId
* in: query
* required: true
* description: 'Unique identifier of the job to cancel. Get via [List Jobs](#operation/job.list)'
* schema:
* type: string
* example: 067da473-fc0b-43e3-b0a2-09d26af130c1
* responses:
* 204:
* description: 'Job canceled successfully'
* 400:
* description: 'Bad Request - jobId parameter is missing'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* 404:
* description: 'Job not found'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
*/
app.delete('/jobs', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'job-cancel-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, job_cancel_1.default)());
/**
* @openapi
* /settings/schema:
* get:
* tags:
* - 'Settings'
* summary: 'Get Application Settings Schema'
* operationId: settings.schema
* description: 'Retrieve configuration schema with field definitions, types, options, and metadata'
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* responses:
* 200:
* description: 'Settings schema with field definitions'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* $ref: '#/components/schemas/SettingsSchemaResponse'
* example:
* data:
* - key: 'skipIntegrationNodesToggle'
* label: 'Skip Integration Nodes'
* type: 'checkbox'
* helpText: 'Skip certain files and folders...'
* category: 'general'
* position: 3
* - key: 'schedule'
* label: 'Sync schedule'
* type: 'select'
* defaultValue: '0'
* category: 'sync'
* position: 0
* helpText: 'Defines how often content is synced...'
* options:
* - value: '0'
* label: 'Disabled'
* - value: '3'
* label: '3 hours'
* - value: '24'
* label: '24 hours'
* - key: 'new-crowdin-files'
* label: 'Automatically sync new translations from Crowdin'
* type: 'checkbox'
* category: 'sync'
* position: 1
* dependencySettings: '[{\"#schedule-settings\":{\"type\":\"!equal\",\"value\":[\"0\"]}}]'
* - key: 'inContext'
* label: 'Sync In-Context Pseudo Language'
* type: 'checkbox'
* category: 'advanced'
* position: 3
* helpTextHtml: '<p><strong>What is In-Context?</strong></p>...'
*/
app.get('/settings/schema', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'settings-schema-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, settings_schema_1.default)(config.projectIntegration));
/**
* @openapi
* /settings:
* get:
* tags:
* - 'Settings'
* summary: 'Get Application Settings'
* operationId: settings.get
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* responses:
* 200:
* description: 'Application Settings'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* $ref: '#/components/schemas/SettingsResponse'
* example:
* data:
* schedule: '3'
* condition: '1'
* new-crowdin-files: true
* new-integration-files: false
* inContext: false
* importEqSuggestions: true
* autoApproveImported: false
* translateHidden: false
* includeByFilePath: '/content/**'
* excludeByFilePath: '/drafts/**'
* skipIntegrationNodesToggle: true
*/
app.get('/settings', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'settings-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, settings_1.default)());
/**
* @openapi
* /settings:
* post:
* tags:
* - 'Settings'
* summary: 'Update Application Settings'
* operationId: settings.update
* description: 'Update application settings. All config keys are validated against the schema from GET /settings/schema'
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UpdateSettingsData'
* responses:
* 202:
* description: 'Settings save job started successfully'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* properties:
* jobId:
* type: string
* description: 'Job identifier to track operation progress. Use GET /job-info to check status.'
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
* 400:
* description: 'Bad Request - validation error'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* examples:
* missingConfig:
* summary: 'Missing config parameter'
* value:
* error:
* message: 'Missing required parameter: config'
* invalidType:
* summary: 'Config is not an object'
* value:
* error:
* message: 'Parameter "config" must be an object'
* invalidKeys:
* summary: 'Unknown config keys'
* value:
* error:
* message: 'Invalid configuration keys: unknownKey. Use GET /settings/schema to see available fields.'
*/
app.post('/settings', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'settings-update-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, settings_save_1.default)(config, config.projectIntegration));
/**
* @openapi
* /sync-settings:
* get:
* tags:
* - 'Settings'
* summary: 'Get Auto-Sync Files'
* operationId: sync.settings.get
* description: 'Retrieve list of files configured for automatic synchronization based on schedule'
* parameters:
* - $ref: '#/components/parameters/ProjectId'
* - name: provider
* in: query
* required: true
* description: 'Sync direction: "crowdin" returns Crowdin files with target languages, "integration" returns integration files with schedule settings'
* schema:
* type: string
* enum:
* - crowdin
* - integration
* responses:
* 200:
* description: 'List of files configured for auto-sync'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* oneOf:
* - $ref: '#/components/schemas/CrowdinSyncSettingsResponse'
* - $ref: '#/components/schemas/IntegrationSyncSettingsResponse'
* examples:
* crowdinProvider:
* summary: 'Crowdin files auto-sync (provider=crowdin)'
* value:
* data:
* '102': ['uk', 'de', 'fr']
* '999': ['uk', 'es']
* integrationProvider:
* summary: 'Integration files auto-sync (provider=integration)'
* value:
* data:
* - id: 'story_302374-541001856::region-eu::'
* name: 'Home'
* parent_id: 'space_302374::region-eu::'
* type: 'html'
* node_type: '1'
* schedule: true
* sync: false
* isNew: false
* isUpdated: false
* notSynced: false
* synced: false
* - id: 'story_314675-584362389::region-eu::'
* name: 'Test Story'
* parent_id: 'story_584358589::region-eu::'
* type: 'html'
* node_type: '1'
* schedule: true
* sync: false
* isNew: false
* isUpdated: false
* notSynced: false
* synced: true
* 400:
* description: 'Bad Request'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* examples:
* missingProvider:
* summary: 'Missing provider parameter'
* value:
* error:
* message: 'Missing required parameter: provider'
* invalidProvider:
* summary: 'Invalid provider value'
* value:
* error:
* message: 'Invalid provider. Must be one of: crowdin, integration'
*/
app.get('/sync-settings', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'sync-settings-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, sync_settings_1.default)());
/**
* @openapi
* /sync-settings:
* post:
* tags:
* - 'Settings'
* summary: 'Update Auto-Sync Files'
* operationId: sync.settings.update
* description: 'Configure which files should be automatically synchronized based on schedule. Returns a job ID to track the operation progress.'
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UpdateSyncSettingsData'
* examples:
* crowdinFiles:
* summary: 'Configure Crowdin files for auto-sync'
* value:
* projectId: 12
* provider: 'crowdin'
* files:
* '102': ['uk', 'de']
* '999': ['uk', 'fr', 'es']
* integrationFiles:
* summary: 'Configure integration files for auto-sync'
* value:
* projectId: 12
* provider: 'integration'
* files:
* - id: 'story_302374-541001856'
* name: 'Home'
* type: 'html'
* node_type: '1'
* parent_id: 'space_302374'
* schedule: true
* sync: false
* responses:
* 200:
* description: 'Auto-sync configuration job started successfully'
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* properties:
* jobId:
* type: string
* description: 'Job identifier to track operation progress. Use GET /job-info to check status.'
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
* 400:
* description: 'Bad Request - validation error'
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* examples:
* missingFiles:
* summary: 'Missing files parameter'
* value:
* error:
* message: 'Missing required parameter: files'
* missingProvider:
* summary: 'Missing provider parameter'
* value:
* error:
* message: 'Missing required parameter: provider'
* invalidProvider:
* summary: 'Invalid provider value'
* value:
* error:
* message: 'Invalid provider. Must be one of: crowdin, integration'
*/
app.post('/sync-settings', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: true,
moduleKey: 'sync-settings-update-api',
}), (0, integration_credentials_1.default)({ config, integration: config.projectIntegration }), (0, sync_settings_save_1.default)(config, config.projectIntegration));
if (config.projectIntegration.loginForm) {
/**
* @openapi
* /login-fields:
* get:
* tags:
* - 'Login'
* summary: 'Integration Login Form Fields'
* operationId: integration.fields
* responses:
* 200:
* description: 'Login Form Fields'
* content:
* application/json:
* schema:
* properties:
* data:
* $ref: '#/components/schemas/LoginFieldsResponse'
*/
app.get('/login-fields', api_call_1.default, json_response_1.default, (req, res) => {
var _a, _b;
let fields = [];
if ((_b = (_a = config.projectIntegration) === null || _a === void 0 ? void 0 : _a.loginForm) === null || _b === void 0 ? void 0 : _b.fields) {
fields = getFormFields(config === null || config === void 0 ? void 0 : config.projectIntegration.loginForm.fields);
}
res.send({ fields });
});
/**
* @openapi
* /login:
* post:
* tags:
* - 'Login'
* summary: 'Integration Login'
* operationId: integration.login
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Login'
* responses:
* 204:
* description: 'Login successful'
*/
app.post('/login', api_call_1.default, json_response_1.default, (0, crowdin_client_1.default)({
config,
optional: false,
checkSubscriptionExpiration: false,
moduleKey: 'login',
}), (0, integration_login_1.default)(config, config.projectIntegration));
}
}
}
function addSwagerApiDocumentation(app, config) {
var _a, _b, _c;
const options = {
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: `${config.name} Application API`,
description: "API methods of this application cannot be called directly. Instead, you should trigger Crowdin's platform API endpoint, Crowdin will authorize your request and route it to this app.\n\n" +
'Example call for crowdin.com \n\n' +
' curl --request GET \n' +
' --url "https://crowdin.com/api/v2/applications/' +
config.identifier +
'/api/data?report=raw&projects=479" \n' +
' --header "Authorization: Bearer 70cf05deasdas78c84c73c4cf986ee0a3ee911w1dsad384e3c4asd1qdbbcf6b6db5732e" \n' +
'Example call for Crowdin Enterprise \n\n' +
' curl --request GET \n' +
' --url "https://acme.crowdin.com/api/v2/applications/' +
config.identifier +
'/api/data?report=raw&projects=479&startDate=2023-06-01T06%3A32%3A01.048Z&endDate=2023-06-23T06%3A32%3A01.048Z" \n' +
' --header "Authorization: Bearer 70cf05dee35sad12rfes0a3ee911462e7b0723rfdfrg1e45bbcf6b6db5732e" \n',
version: '1.0.0',
'x-logo': {
url: 'https://support.crowdin.com/assets/crowdin-logo.svg',
},
},
servers: [
{
url: `{protocol}//{host}/api/v2/applications/${config.identifier}/api`,
variables: {
protocol: {
default: 'https',
enum: ['https', 'http'],
description: 'Protocol (https for production)',
},
host: {
default: 'crowdin.com',
description: 'Crowdin host (crowdin.com or {organization}.crowdin.com for Enterprise)',
},
},
},
],
},
apis: config.projectIntegration && ((_a = config.api) === null || _a === void 0 ? void 0 : _a.default)
? [path_1.default.resolve(__dirname, './base.js'), path_1.default.resolve(__dirname, './components.js'), __filename]
: [],
};
if ((_b = config.api) === null || _b === void 0 ? void 0 : _b.docFile) {
options.apis.push(config.api.docFile);
}
const swaggerSpec = (0, swagger_jsdoc_1.default)(options);
// remove Login info from doc
if (config.projectIntegration && !((_c = config.projectIntegration) === null || _c === void 0 ? void 0 : _c.loginForm)) {
delete swaggerSpec.paths['/login'];
delete swaggerSpec.paths['/login-fields'];
delete swaggerSpec.components.schemas['Login'];
delete swaggerSpec.components.schemas['LoginData'];
swaggerSpec.tags = swaggerSpec.tags.filter((tag) => tag.name !== 'Login');
}
app.get('/api-docs/swagger.json', (req, res) => res.send(swaggerSpec));
app.use('/api-docs', (req, res) => res.send((0, redoc_html_template_1.default)({ title: `${config.name} Application API`, specUrl: '/api-docs/swagger.json' })));
}