UNPKG

@crowdin/app-project-module

Version:

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

572 lines (571 loc) 16.9 kB
import Crowdin, { SourceFilesModel, TranslationStatusModel } from '@crowdin/crowdin-api-client'; import { Request } from 'express'; import { CrowdinClientRequest, ModuleKey } from '../../types'; import { JobClient, JobStoreType } from './util/types'; export interface IntegrationLogic extends ModuleKey { /** * Customize your app login form */ loginForm?: LoginForm; /** * Define login process via OAuth2 protocol */ oauthLogin?: OAuthLogin; /** * name of the root folder in Crowdin where files from integration will be stored, default your app name, will be ignored in case if {@link withRootFolder} is false */ appFolderName?: string; /** * flag that defines if the app should have a dedicated root folder in Crowdin files, default 'false' */ withRootFolder?: boolean; /** * Validate integration settings before saving */ validateSettings?: ({ client, credentials, appSettings, }: { client: Crowdin; credentials: any; appSettings: AppSettings; }) => Promise<{ [key: string]: string; } | null>; /** * function which will be used to check connection with integration service */ checkConnection?: (apiCredentials: any) => Promise<void>; /** * function to get crowdin files that are related with this integration */ getCrowdinFiles?: (projectId: number, client: Crowdin, appRootFolder?: SourceFilesModel.Directory, config?: any, mode?: CrowdinFilesLoadMode) => Promise<TreeItem[]>; /** * function to get data from integration */ getIntegrationFiles: (apiCredentials: any, config?: any, parentId?: any, search?: any, page?: any, paginationData?: any) => Promise<TreeItem[] | ExtendedResult<TreeItem[]>>; /** * function to update crowdin files (e.g. pull integration data to crowdin source files) */ updateCrowdin: ({ projectId, client, credentials, request, rootFolder, appSettings, uploadTranslations, job, excludedTargetLanguages, }: { projectId: number; client: Crowdin; credentials: any; request: IntegrationFile[]; rootFolder?: SourceFilesModel.Directory; appSettings?: any; uploadTranslations?: boolean; job: JobClient; excludedTargetLanguages?: string[]; }) => Promise<void | ExtendedResult<void>>; /** * function to update integration content (e.g. load crowdin translations and push them to integration service) */ updateIntegration: ({ projectId, client, credentials, request, rootFolder, appSettings, job, }: { projectId: number; client: Crowdin; credentials: any; request: UpdateIntegrationRequest; rootFolder?: SourceFilesModel.Directory; appSettings?: any; job: JobClient; }) => Promise<void | ExtendedResult<void>>; /** * Store to use for memorizing job data */ jobStoreType?: JobStoreType; /** * function to define configuration(settings) modal for you app (by default app will not have any custom settings) */ getConfiguration?: (projectId: number, client: Crowdin, apiCredentials: any) => Promise<FormEntity[]>; /** * function to normalize saved settings */ normalizeSettings?: ({ appSettings, apiCredentials, client, }: { appSettings: FormEntity[]; apiCredentials: any; client?: Crowdin; }) => Promise<FormEntity[]>; /** * Logout hook for cleanup logic */ onLogout?: (projectId: number, client: Crowdin, apiCredentials: any, config?: any) => Promise<void>; /** * flag to turn on auto reload of the tree whenever user updates the configuration */ reloadOnConfigSave?: boolean; /** * define info modal (help section) for you app (by default app will not have own info section) */ infoModal?: { title: string; content: string; }; /** * background jobs that will be executed for each crowdin project and user */ cronJobs?: CronJob[]; /** * Enable new file sync when syncing via cron or webhook */ syncNewElements?: { crowdin: boolean; integration: boolean; }; withCronSync?: { crowdin: boolean; integration: boolean; }; withWebhookSync?: { crowdin: boolean; integration: boolean; }; /** * Enable file filtering */ filtering?: { crowdinLanguages?: boolean; /** * Configuration for integration file filtering */ integrationFilterConfig?: any; /** * Enable file status filtering */ integrationFileStatus?: { /** * Enable file filtering by "isNew" status */ isNew?: boolean; /** * Enable file filtering by "isUpdated" status */ isUpdated?: boolean; /** * Enable file filtering by "failed" status */ failed?: boolean; /** * Enable file filtering by "notSynced" status */ notSynced?: boolean; /** * Enable file filtering by "synced" status */ synced?: boolean; }; }; /** * Enable integration folder open event */ integrationOneLevelFetching?: boolean; /** * Enable integration search event */ integrationSearchListener?: boolean; /** * Enable integration next page event */ integrationPagination?: boolean; /** * Enable progressive loading for Crowdin files (directories first, then files). */ progressiveCrowdinFilesLoading?: boolean; /** * Enable the option to upload translations to crowdin that are already present in the integration. */ uploadTranslations?: boolean; /** * Force sync translations from Crowdin to integration regardless of etag. */ forcePushTranslations?: boolean; /** * Force sync sources from integration to Crowdin. */ forcePushSources?: boolean; /** * Enable the option to upload file for translation into selected languages. */ excludedTargetLanguages?: boolean; /** * Enable the option to add 'Exclude paths' and 'Include paths' text fields to integration settings */ filterByPathIntegrationFiles?: boolean; /** * function to get crowdin file translation progress */ getFileProgress?: (projectId: number, client: Crowdin, fileId: number) => Promise<{ [key: number]: TranslationStatusModel.LanguageProgress[]; }>; /** * Register Crowdin webhook to get notified when translations are ready */ webhooks?: Webhooks; /** * define a notification for your application at the top of the screen */ notice?: { title: string; content: string; type: NoticeType; icon: boolean; close: boolean; }; /** * Skip integration nodes */ skipIntegrationNodes?: SkipIntegrationNodes; /** * Configuration for toggling skipIntegrationNodes functionality with custom title and description */ skipIntegrationNodesToggle?: { title: string; description: string; value: boolean; }; /** * Async progress checking time interval to update job progress, im ms. * * Default 1000 */ asyncProgress?: { checkInterval?: number; }; /** * The duration for storing user errors, default is 30 days. */ userErrorLifetimeDays?: number; /** * When true, folder filtering during automatic translation sync is bypassed, and the file tree is returned unchanged. */ skipAutoSyncFoldersFilter?: boolean; } export interface LoginForm { fields: FormEntity[]; /** * Override to implement request for retrieving access token (and refresh token if 'refresh' is enabled) */ performGetTokenRequest?: (fieldsCredentials: any) => Promise<LoginFormTokenResponse>; /** * Override to implement request for refreshing token (only if 'refresh' is enabled) */ performRefreshTokenRequest?: (currentCredentials: any) => Promise<LoginFormTokenResponse>; /** * default 'false' which means that the access token has no expiration date */ refresh?: boolean; } export interface OAuthLogin { /** * Extra field for login form */ loginFields?: FormField[]; /** * Authorization url (e.g. https://github.com/login/oauth/authorize or https://accounts.google.com/o/oauth2/v2/auth) */ authorizationUrl?: string; /** * Authorization url getter */ getAuthorizationUrl?: (redirectUrl: string, loginForm: any) => string; /** * Access token url (e.g. https://github.com/login/oauth/access_token) */ accessTokenUrl: string; /** * Url to refresh token, default will use {@link accessTokenUrl}. Needed when {@link refresh} is enabled */ refreshTokenUrl?: string; mode?: 'standard' | 'polling'; /** * The scopes of access, usually expressed as a list of space-delimited, case-sensitive strings */ scope?: string; /** * Client id */ clientId: string; /** * Client secret */ clientSecret: string; /** * default '/oauth/code' */ redirectUriRoute?: string; /** * request/response fields mapping */ fieldsMapping?: { /** * default 'client_id' */ clientId?: string; /** * default 'client_secret' */ clientSecret?: string; /** * default 'scope' */ scope?: string; /** * default 'redirect_uri' */ redirectUri?: string; /** * default 'code' */ code: string; /** * default 'access_token' */ accessToken?: string; /** * default 'refresh_token' */ refreshToken?: string; /** * default 'expires_in' */ expiresIn?: string; /** * default 'state' */ state?: string; }; /** * default 'false' which means that the access token has no expiration date */ refresh?: boolean; /** * Additional URL parameters for authorizarion url */ extraAutorizationUrlParameters?: { [key: string]: string; }; /** * Additional parameters for access token request */ extraAccessTokenParameters?: { [key: string]: any; }; /** * Additional parameters for refresh token request */ extraRefreshTokenParameters?: { [key: string]: any; }; /** * Override to implement request for retrieving access token (and refresh token if 'refresh' is enabled) */ performGetTokenRequest?: (code: string, query: { [key: string]: any; }, url: string, redirectUri: string, loginForm?: any) => Promise<any>; /** * Override to implement request for refreshing token (only if 'refresh' is enabled) */ performRefreshTokenRequest?: (currentCredentials: any, loginForm?: any) => Promise<any>; } export type CrowdinFilesLoadMode = 'directories' | 'files'; export interface BaseTreeItem { id: string; name: string; parentId?: string; nodeType?: IntegrationTreeElementType; customContent?: string; labels?: LabelTreeElement[]; disabled?: boolean; tooltip?: string; path?: string; type?: string; syncedAt?: string; } export interface File extends BaseTreeItem { type: SourceFilesModel.FileType; failed?: boolean; excludedTargetLanguages?: string[]; } export type Folder = BaseTreeItem; export type TreeItem = File | Folder; /** * 0 - folder * 1 - file * 2 - branch */ type IntegrationTreeElementType = '0' | '1' | '2'; export type FormEntity = FormField | FormDelimiter; export interface FormDelimiter { label?: string; labelHtml?: string; category?: string; } export interface FormField { key: string; helpText?: string; helpTextHtml?: string; label: string; type?: 'text' | 'password' | 'checkbox' | 'select' | 'textarea' | 'file' | 'notice'; defaultValue?: any; /** * only for select */ isMulti?: boolean; /** * only for select */ isSearchable?: boolean; /** * only for select */ options?: { label: string; value: string; }[]; /** * only for type file */ accept?: string; /** * field dependency settings */ dependencySettings?: string; /** * only for notice type */ noticeType?: string; /** * only for notice type */ noIcon?: boolean; category?: string; position?: number; } type NoticeType = 'info' | 'warning' | 'danger' | 'success' | 'error' | 'dataLostWarning'; export interface IntegrationCredentials { id: string; credentials: any; crowdinId: string; managers?: any; } export interface IntegrationConfig { id: number; integrationId: string; crowdinId: string; config: any; } export interface IntegrationFile { id: string; name: string; type: SourceFilesModel.FileType; parentId: string; nodeType?: IntegrationTreeElementType; } export interface UpdateIntegrationRequest { [fileId: string]: string[]; } export interface IntegrationRequest extends CrowdinClientRequest { integrationCredentials: any; integrationSettings?: any; } export interface CronJob { task: (projectId: number, client: Crowdin, apiCredentials: any, appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void>; expression: string; } export interface ExtendedResult<T> { data?: T; message?: string; stopPagination?: boolean; } type LabelTreeElementType = 'primary' | 'secondary' | 'success' | 'warning' | 'info' | 'danger' | 'dark' | 'light'; export interface LabelTreeElement { text: string; type?: LabelTreeElementType; tooltip?: string; color?: string; } export declare enum Provider { CROWDIN = "crowdin", INTEGRATION = "integration" } export interface IntegrationSyncSettings { id: number; files?: any; integrationId: string; crowdinId: string; provider: Provider; } export interface IntegrationFilesSnapshot { id: number; files?: any; integrationId: string; crowdinId: string; provider: Provider; } export interface IntegrationWebhooks { id: number; fileId: number; integrationId: string; crowdinId: string; provider: Provider; } export interface SkipIntegrationNodes { fileNamePattern?: string; folderNamePattern?: string; } export interface Webhooks { crowdinWebhookUrl?: string; integrationWebhookUrl?: string; urlParam?: string; crowdinWebhooks?: (client: Crowdin, projectId: number, available: boolean, config?: AppSettings) => Promise<void>; integrationWebhooks?: (apiCredentials: any, urlParam: string, available: boolean, config?: AppSettings, syncSettings?: any) => Promise<void>; crowdinWebhookInterceptor?: (projectId: number, client: Crowdin, appRootFolder?: SourceFilesModel.Directory, config?: any, syncSettings?: any, webhookRequest?: any) => Promise<UpdateIntegrationRequest>; integrationWebhookInterceptor?: (projectId: number, client: Crowdin, apiCredentials: any, appRootFolder?: SourceFilesModel.Directory, config?: AppSettings, syncSettings?: any, webhookRequests?: any) => Promise<IntegrationFile[]>; queueUrl: string; } export declare enum SyncCondition { ALL = 0, TRANSLATED = 1, APPROVED = 2 } export declare enum SyncSchedule { DISABLED = 0, ACTIVE } export type Payload = { event: string; projectId: string; language: string; fileId: string; }; export type WebhookUrlParams = { projectId: number; crowdinId: string; clientId: string; }; export interface UpdateCrowdinWebhookPayloadsArgs { integration: IntegrationLogic; webhookData: any; req: Request[]; } export interface FilterSyncFilesArgs { projectId: number; crowdinClient: Crowdin; events: Payload[]; syncFileSettings: UpdateIntegrationRequest; appSettings: AppSettings; } export interface AppSettings { schedule?: number; condition?: number; 'new-crowdin-files'?: boolean; 'new-integration-files'?: boolean; [key: string]: any; } interface LoginFormTokenResponse { access_token: string; expires_in: number; refresh_token: string; } export declare enum DefaultCategory { GENERAL = "General Settings", SYNC = "Sync settings" } export {};