@crowdin/app-project-module 0.85.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,7 @@ const defaults_1 = require("../util/defaults");
19
19
  const logger_1 = require("../../../util/logger");
20
20
  const job_1 = require("../util/job");
21
21
  const files_1 = require("../util/files");
22
+ const types_2 = require("../types");
22
23
  function handle(config, integration) {
23
24
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
24
25
  var _a, _b, _c;
@@ -33,6 +34,7 @@ function handle(config, integration) {
33
34
  if (((_b = config.api) === null || _b === void 0 ? void 0 : _b.default) && req.body.files) {
34
35
  req.body = req.body.files;
35
36
  }
37
+ let payload = req.body;
36
38
  const excludedTargetLanguages = yield (0, files_1.getExcludedTargetLanguages)({
37
39
  client: req.crowdinApiClient,
38
40
  projectId,
@@ -43,29 +45,34 @@ function handle(config, integration) {
43
45
  crowdinId: req.crowdinContext.crowdinId,
44
46
  type: types_1.JobType.UPDATE_TO_CROWDIN,
45
47
  title: 'Sync files to Crowdin',
46
- payload: req.body,
48
+ payload,
47
49
  res,
48
50
  projectId,
49
51
  client: req.crowdinApiClient,
50
52
  jobType: types_1.JobClientType.MANUAL,
51
53
  jobStoreType: integration.jobStoreType,
52
54
  jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
53
- var _d;
54
- if (req.body && ((_d = req.body) === null || _d === void 0 ? void 0 : _d.length)) {
55
- req.body = yield (0, files_1.expandFilesTree)(req.body, req, integration, job);
56
- req.body = (0, lodash_uniqby_1.default)(req.body, 'id');
55
+ if (payload && (payload === null || payload === void 0 ? void 0 : payload.length)) {
56
+ payload = yield (0, files_1.expandFilesTree)(payload, req, integration, job);
57
+ payload = (0, lodash_uniqby_1.default)(payload, 'id');
57
58
  }
58
59
  const result = yield integration.updateCrowdin({
59
60
  projectId,
60
61
  client: req.crowdinApiClient,
61
62
  credentials: req.integrationCredentials,
62
- request: req.body,
63
+ request: payload,
63
64
  rootFolder,
64
65
  appSettings: req.integrationSettings,
65
66
  uploadTranslations,
66
67
  job,
67
68
  excludedTargetLanguages: req.query.languages ? excludedTargetLanguages : undefined,
68
69
  });
70
+ try {
71
+ yield (0, files_1.updateSyncedData)(req.crowdinContext.clientId, req.crowdinContext.crowdinId, payload, types_2.Provider.INTEGRATION);
72
+ }
73
+ catch (e) {
74
+ (0, logger_1.logError)(e, req.crowdinContext);
75
+ }
69
76
  let message;
70
77
  if ((0, files_1.isExtendedResultType)(result)) {
71
78
  message = result.message;
@@ -12,31 +12,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../../util");
13
13
  const logger_1 = require("../../../util/logger");
14
14
  const files_1 = require("../util/files");
15
+ const types_1 = require("../types");
16
+ const storage_1 = require("../../../storage");
15
17
  function handle(integration) {
16
18
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
19
+ var _a, _b, _c, _d, _e, _f, _g, _h;
17
20
  const { parent_id: parentId, search, page } = req.query;
18
21
  req.logInfo('Received request to get integration data');
19
22
  let message;
20
23
  let stopPagination;
21
- let files;
24
+ let files = [];
25
+ const { crowdinId, clientId } = req.crowdinContext;
22
26
  try {
23
- const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
27
+ const appSettings = req.integrationSettings;
28
+ const result = yield integration.getIntegrationFiles(req.integrationCredentials, appSettings, parentId, search, page);
24
29
  if ((0, files_1.isExtendedResultType)(result)) {
25
- files = result.data;
30
+ files = result.data || [];
26
31
  message = result.message;
27
32
  stopPagination = result.stopPagination;
28
33
  }
29
34
  else {
30
- files = result;
35
+ files = result || [];
31
36
  }
32
37
  files = (0, files_1.skipFilesByRegex)(files, integration.skipIntegrationNodes);
38
+ if (integration.filterByPathIntegrationFiles) {
39
+ const includePatterns = (_a = appSettings === null || appSettings === void 0 ? void 0 : appSettings.includeByFilePath) === null || _a === void 0 ? void 0 : _a.split('\n').filter(Boolean);
40
+ const excludePatterns = (_b = appSettings === null || appSettings === void 0 ? void 0 : appSettings.excludeByFilePath) === null || _b === void 0 ? void 0 : _b.split('\n').filter(Boolean);
41
+ files = (0, files_1.filterFilesByPath)(files, includePatterns, excludePatterns);
42
+ }
43
+ if (((_d = (_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.integrationFileStatus) === null || _d === void 0 ? void 0 : _d.isNew) ||
44
+ ((_f = (_e = integration.filtering) === null || _e === void 0 ? void 0 : _e.integrationFileStatus) === null || _f === void 0 ? void 0 : _f.isUpdated) ||
45
+ ((_h = (_g = integration.filtering) === null || _g === void 0 ? void 0 : _g.integrationFileStatus) === null || _h === void 0 ? void 0 : _h.notSynced)) {
46
+ const syncedData = yield (0, storage_1.getStorage)().getSyncedData(clientId, crowdinId, types_1.Provider.INTEGRATION);
47
+ const syncedFiles = (syncedData === null || syncedData === void 0 ? void 0 : syncedData.files) ? JSON.parse(syncedData.files) : [];
48
+ const lastSyncTimestamp = (syncedData === null || syncedData === void 0 ? void 0 : syncedData.updatedAt) ? Number(syncedData.updatedAt) : null;
49
+ files = files.map((file) => {
50
+ var _a, _b, _c, _d, _e, _f;
51
+ if (!file.type) {
52
+ return file;
53
+ }
54
+ const notSynced = ((_b = (_a = integration.filtering) === null || _a === void 0 ? void 0 : _a.integrationFileStatus) === null || _b === void 0 ? void 0 : _b.notSynced) === true
55
+ ? !syncedFiles.some((syncedItem) => syncedItem.id === file.id)
56
+ : false;
57
+ const isNew = ((_d = (_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.integrationFileStatus) === null || _d === void 0 ? void 0 : _d.isNew) === true
58
+ ? !syncedFiles.some((syncedItem) => syncedItem.id === file.id) &&
59
+ lastSyncTimestamp &&
60
+ file.createdAt &&
61
+ (typeof file.createdAt === 'string'
62
+ ? new Date(file.createdAt).getTime()
63
+ : file.createdAt) > Number(lastSyncTimestamp)
64
+ : false;
65
+ const isUpdated = ((_f = (_e = integration.filtering) === null || _e === void 0 ? void 0 : _e.integrationFileStatus) === null || _f === void 0 ? void 0 : _f.isUpdated) === true
66
+ ? lastSyncTimestamp &&
67
+ file.updatedAt &&
68
+ (typeof file.updatedAt === 'string'
69
+ ? new Date(file.updatedAt).getTime()
70
+ : file.updatedAt) > Number(lastSyncTimestamp)
71
+ : false;
72
+ return Object.assign(Object.assign({}, file), { isNew,
73
+ notSynced,
74
+ isUpdated });
75
+ });
76
+ }
33
77
  }
34
78
  catch (e) {
35
79
  yield (0, logger_1.handleUserError)({
36
80
  action: 'Get External Service data',
37
81
  error: e,
38
- crowdinId: req.crowdinContext.crowdinId,
39
- clientId: req.crowdinContext.clientId,
82
+ crowdinId,
83
+ clientId,
40
84
  });
41
85
  res.send({ data: [], message: (e === null || e === void 0 ? void 0 : e.message) || e });
42
86
  throw e;
@@ -39,7 +39,7 @@ const defaults_1 = require("../util/defaults");
39
39
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
40
40
  function handle(config, integration) {
41
41
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
42
- var _a, _b, _c;
42
+ var _a, _b, _c, _d;
43
43
  const logger = req.logInfo || logger_1.log;
44
44
  const installed = !!req.crowdinApiClient;
45
45
  const loggedIn = !!req.integrationCredentials;
@@ -82,7 +82,7 @@ function handle(config, integration) {
82
82
  }
83
83
  options.infoModal = integration.infoModal;
84
84
  options.syncNewElements = integration.syncNewElements;
85
- options.filtering = integration.filtering;
85
+ options.filtering = Object.assign(Object.assign({}, integration.filtering), { integrationFilterConfig: JSON.stringify(((_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.integrationFilterConfig) || []) });
86
86
  options.withCronSync = integration.withCronSync;
87
87
  options.webhooks = integration.webhooks
88
88
  ? {
@@ -103,7 +103,7 @@ function handle(config, integration) {
103
103
  : null;
104
104
  options.notice = integration.notice;
105
105
  options.asyncProgress = {
106
- checkInterval: ((_c = integration.asyncProgress) === null || _c === void 0 ? void 0 : _c.checkInterval) || 1000,
106
+ checkInterval: ((_d = integration.asyncProgress) === null || _d === void 0 ? void 0 : _d.checkInterval) || 1000,
107
107
  };
108
108
  logger(`Routing user to ${view} view`);
109
109
  return res.render(view, options);
@@ -107,8 +107,36 @@ export interface IntegrationLogic extends ModuleKey {
107
107
  crowdin: boolean;
108
108
  integration: boolean;
109
109
  };
110
+ /**
111
+ * Enable file filtering
112
+ */
110
113
  filtering?: {
111
- crowdinLanguages: boolean;
114
+ crowdinLanguages?: boolean;
115
+ /**
116
+ * Configuration for integration file filtering
117
+ */
118
+ integrationFilterConfig?: any;
119
+ /**
120
+ * Enable file status filtering
121
+ */
122
+ integrationFileStatus?: {
123
+ /**
124
+ * Enable file filtering by "isNew" status
125
+ */
126
+ isNew?: boolean;
127
+ /**
128
+ * Enable file filtering by "isUpdated" status
129
+ */
130
+ isUpdated?: boolean;
131
+ /**
132
+ * Enable file filtering by "failed" status
133
+ */
134
+ failed?: boolean;
135
+ /**
136
+ * Enable file filtering by "notSynced" status
137
+ */
138
+ notSynced?: boolean;
139
+ };
112
140
  };
113
141
  /**
114
142
  * Enable integration folder open event
@@ -130,6 +158,10 @@ export interface IntegrationLogic extends ModuleKey {
130
158
  * Enable the option to upload file for translation into selected languages.
131
159
  */
132
160
  excludedTargetLanguages?: boolean;
161
+ /**
162
+ * Enable the option to add 'Exclude paths' and 'Include paths' text fields to integration settings
163
+ */
164
+ filterByPathIntegrationFiles?: boolean;
133
165
  /**
134
166
  * function to get crowdin file translation progress
135
167
  */
@@ -305,6 +337,7 @@ export interface File {
305
337
  labels?: LabelTreeElement[];
306
338
  failed?: boolean;
307
339
  excludedTargetLanguages?: string[];
340
+ path?: string;
308
341
  }
309
342
  export interface Folder {
310
343
  id: string;
@@ -313,6 +346,7 @@ export interface Folder {
313
346
  nodeType?: IntegrationTreeElementType;
314
347
  customContent?: string;
315
348
  labels?: LabelTreeElement[];
349
+ path?: string;
316
350
  }
317
351
  export type TreeItem = File | Folder;
318
352
  /**
@@ -43,6 +43,7 @@ const types_1 = require("../types");
43
43
  const job_1 = require("./job");
44
44
  const types_2 = require("./types");
45
45
  const subscription_1 = require("../../../util/subscription");
46
+ const files_1 = require("./files");
46
47
  function runJob({ config, integration, job, }) {
47
48
  return __awaiter(this, void 0, void 0, function* () {
48
49
  (0, logger_1.log)(`Starting cron job with expression [${job.expression}]`);
@@ -106,6 +107,12 @@ function runUpdateProviderJob({ integrationId, crowdinId, type, title, payload,
106
107
  };
107
108
  if (type === types_2.JobType.UPDATE_TO_CROWDIN) {
108
109
  yield integration.updateCrowdin(updateParams);
110
+ try {
111
+ yield (0, files_1.updateSyncedData)(integrationId, crowdinId, payload, types_1.Provider.INTEGRATION);
112
+ }
113
+ catch (e) {
114
+ (0, logger_1.logError)(e, context);
115
+ }
109
116
  }
110
117
  else if (type === types_2.JobType.UPDATE_TO_INTEGRATION) {
111
118
  yield integration.updateIntegration(updateParams);
@@ -57,7 +57,7 @@ function getOauthRoute(integration) {
57
57
  }
58
58
  exports.getOauthRoute = getOauthRoute;
59
59
  function applyIntegrationModuleDefaults(config, integration) {
60
- var _a, _b;
60
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
61
61
  if (!integration.getCrowdinFiles) {
62
62
  integration.getCrowdinFiles = (projectId, client, rootFolder) => __awaiter(this, void 0, void 0, function* () {
63
63
  const allBranches = (yield client.sourceFilesApi.withFetchAll().listProjectBranches(projectId)).data.map((d) => d.data);
@@ -139,9 +139,13 @@ function applyIntegrationModuleDefaults(config, integration) {
139
139
  ],
140
140
  };
141
141
  }
142
+ if ((integration.filterByPathIntegrationFiles === undefined || integration.filterByPathIntegrationFiles) &&
143
+ !integration.integrationOneLevelFetching) {
144
+ integration.filterByPathIntegrationFiles = true;
145
+ }
142
146
  const getUserSettings = integration.getConfiguration;
143
147
  integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => __awaiter(this, void 0, void 0, function* () {
144
- var _c, _d;
148
+ var _m, _o;
145
149
  let fields = [];
146
150
  const project = (yield crowdinClient.projectsGroupsApi.getProject(projectId));
147
151
  if (getUserSettings) {
@@ -190,7 +194,7 @@ function applyIntegrationModuleDefaults(config, integration) {
190
194
  },
191
195
  ],
192
196
  });
193
- if ((_c = integration.syncNewElements) === null || _c === void 0 ? void 0 : _c.crowdin) {
197
+ if ((_m = integration.syncNewElements) === null || _m === void 0 ? void 0 : _m.crowdin) {
194
198
  defaultSettings.push({
195
199
  key: 'new-crowdin-files',
196
200
  label: 'Automatically sync new translations from Crowdin',
@@ -198,7 +202,7 @@ function applyIntegrationModuleDefaults(config, integration) {
198
202
  dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
199
203
  });
200
204
  }
201
- if ((_d = integration.syncNewElements) === null || _d === void 0 ? void 0 : _d.integration) {
205
+ if ((_o = integration.syncNewElements) === null || _o === void 0 ? void 0 : _o.integration) {
202
206
  defaultSettings.push({
203
207
  key: 'new-integration-files',
204
208
  label: `Automatically sync new content from ${config.name}`,
@@ -245,6 +249,21 @@ function applyIntegrationModuleDefaults(config, integration) {
245
249
  ],
246
250
  });
247
251
  }
252
+ if (integration.filterByPathIntegrationFiles) {
253
+ defaultSettings.push({
254
+ label: 'File Filters',
255
+ }, {
256
+ key: 'includeByFilePath',
257
+ label: 'Source files path',
258
+ type: 'textarea',
259
+ helpText: 'Enter the file path patterns to include files for synchronization. Use wildcard selectors like `*` and `**` to match multiple files. Example: `/pages/**',
260
+ }, {
261
+ key: 'excludeByFilePath',
262
+ label: 'Ignore files or folders',
263
+ type: 'textarea',
264
+ helpText: 'Enter the path patterns for files or folders to exclude. Use wildcard selectors like `*` and `**` to match multiple files. Example: `/drafts/**`',
265
+ });
266
+ }
248
267
  return [...defaultSettings, ...fields];
249
268
  });
250
269
  if (!integration.checkConnection) {
@@ -258,6 +277,51 @@ function applyIntegrationModuleDefaults(config, integration) {
258
277
  if (!((_b = integration.filtering) === null || _b === void 0 ? void 0 : _b.hasOwnProperty('crowdinLanguages'))) {
259
278
  integration.filtering = Object.assign(Object.assign({}, (integration.filtering || {})), { crowdinLanguages: true });
260
279
  }
280
+ integration.filtering.integrationFileStatus = Object.assign({ notSynced: true }, integration.filtering.integrationFileStatus);
281
+ if (!((_c = integration.filtering) === null || _c === void 0 ? void 0 : _c.hasOwnProperty('integrationFilterConfig'))) {
282
+ const filterItems = [
283
+ {
284
+ value: 'all',
285
+ label: 'All',
286
+ },
287
+ ];
288
+ if ((_e = (_d = integration.filtering) === null || _d === void 0 ? void 0 : _d.integrationFileStatus) === null || _e === void 0 ? void 0 : _e.isNew) {
289
+ filterItems.push({
290
+ value: 'isNew',
291
+ label: 'New',
292
+ });
293
+ }
294
+ if ((_g = (_f = integration.filtering) === null || _f === void 0 ? void 0 : _f.integrationFileStatus) === null || _g === void 0 ? void 0 : _g.isUpdated) {
295
+ filterItems.push({
296
+ value: 'isUpdated',
297
+ label: 'Modified',
298
+ });
299
+ }
300
+ if ((_j = (_h = integration.filtering) === null || _h === void 0 ? void 0 : _h.integrationFileStatus) === null || _j === void 0 ? void 0 : _j.failed) {
301
+ filterItems.push({
302
+ value: 'failed',
303
+ label: 'Sync Error',
304
+ });
305
+ }
306
+ if ((_l = (_k = integration.filtering) === null || _k === void 0 ? void 0 : _k.integrationFileStatus) === null || _l === void 0 ? void 0 : _l.notSynced) {
307
+ filterItems.push({
308
+ value: 'notSynced',
309
+ label: 'Never Synced',
310
+ });
311
+ }
312
+ integration.filtering = Object.assign(Object.assign({}, (integration.filtering || {})), { integrationFilterConfig: filterItems.length > 1
313
+ ? [
314
+ {
315
+ key: 'file',
316
+ type: 'list_single',
317
+ label: 'File',
318
+ items: filterItems,
319
+ defaultValue: 'all',
320
+ defaultLabel: 'All',
321
+ },
322
+ ]
323
+ : [] });
324
+ }
261
325
  if (!integration.userErrorLifetimeDays) {
262
326
  integration.userErrorLifetimeDays = 30;
263
327
  }
@@ -1,4 +1,4 @@
1
- import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem, UpdateIntegrationRequest } from '../types';
1
+ import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem, UpdateIntegrationRequest, Provider } from '../types';
2
2
  import { JobClient } from './types';
3
3
  import Crowdin from '@crowdin/crowdin-api-client';
4
4
  import { SourceFilesModel } from '@crowdin/crowdin-api-client';
@@ -17,3 +17,9 @@ export declare function getExcludedTargetLanguages({ client, projectId, language
17
17
  languages: string[];
18
18
  }): Promise<string[]>;
19
19
  export declare function filterLanguages(request: UpdateIntegrationRequest, files: SourceFilesModel.File[]): UpdateIntegrationRequest;
20
+ export declare function buildPath(file: TreeItem, files: TreeItem[]): string;
21
+ export declare function getParentPaths(filePath: string): Set<string>;
22
+ export declare function getParentFiles(allFiles: TreeItem[], fileCollection: TreeItem[]): TreeItem[];
23
+ export declare function filterFilesByPatterns(files: TreeItem[], patterns: string[]): TreeItem[];
24
+ export declare function filterFilesByPath(files: TreeItem[], includePatterns?: string[], excludePatterns?: string[]): TreeItem[];
25
+ export declare function updateSyncedData(clientId: string, crowdinId: string, payload: any[], provider?: Provider): Promise<void>;
@@ -8,11 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
13
- const types_1 = require("./types");
15
+ exports.updateSyncedData = exports.filterFilesByPath = exports.filterFilesByPatterns = exports.getParentFiles = exports.getParentPaths = exports.buildPath = exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
16
+ const types_1 = require("../types");
17
+ const types_2 = require("./types");
14
18
  const storage_1 = require("../../../storage");
15
19
  const util_1 = require("../../../util");
20
+ const minimatch_1 = require("minimatch");
21
+ const lodash_uniqby_1 = __importDefault(require("lodash.uniqby"));
16
22
  function skipFilesByRegex(files, skipIntegrationNodes) {
17
23
  if (!Array.isArray(files)) {
18
24
  return [];
@@ -34,7 +40,7 @@ exports.skipFilesByRegex = skipFilesByRegex;
34
40
  function expandFilesTree(nodes, req, integration, job) {
35
41
  var _a;
36
42
  return __awaiter(this, void 0, void 0, function* () {
37
- if (job && types_1.JobStatus.CANCELED === ((_a = (yield job.get())) === null || _a === void 0 ? void 0 : _a.status)) {
43
+ if (job && types_2.JobStatus.CANCELED === ((_a = (yield job.get())) === null || _a === void 0 ? void 0 : _a.status)) {
38
44
  throw new Error('Job canceled');
39
45
  }
40
46
  const files = nodes.filter((file) => file.nodeType === undefined || file.nodeType === '1');
@@ -121,3 +127,73 @@ function filterLanguages(request, files) {
121
127
  return result;
122
128
  }
123
129
  exports.filterLanguages = filterLanguages;
130
+ function buildPath(file, files) {
131
+ const filePath = `/${file.name}`;
132
+ if (!file.parentId) {
133
+ return filePath;
134
+ }
135
+ const parent = files.find((f) => f.id === file.parentId);
136
+ if (!parent) {
137
+ return filePath;
138
+ }
139
+ const parentPath = buildPath(parent, files);
140
+ return `${parentPath}${filePath}`;
141
+ }
142
+ exports.buildPath = buildPath;
143
+ function getParentPaths(filePath) {
144
+ const parentPaths = new Set();
145
+ const parts = filePath.split('/');
146
+ let currentPath = '';
147
+ for (let i = 1; i < parts.length - 1; i++) {
148
+ currentPath += '/' + parts[i];
149
+ parentPaths.add(currentPath);
150
+ }
151
+ return parentPaths;
152
+ }
153
+ exports.getParentPaths = getParentPaths;
154
+ function getParentFiles(allFiles, fileCollection) {
155
+ const parentPaths = new Set();
156
+ fileCollection
157
+ .filter((file) => !!file.path)
158
+ .forEach((file) => {
159
+ const paths = getParentPaths(file.path);
160
+ paths.forEach((path) => parentPaths.add(path));
161
+ });
162
+ return allFiles.filter((file) => file.path && parentPaths.has(file.path));
163
+ }
164
+ exports.getParentFiles = getParentFiles;
165
+ function filterFilesByPatterns(files, patterns) {
166
+ return files.filter((file) => patterns.some((pattern) => (0, minimatch_1.minimatch)(file.path || '', pattern.trim())));
167
+ }
168
+ exports.filterFilesByPatterns = filterFilesByPatterns;
169
+ function filterFilesByPath(files, includePatterns, excludePatterns) {
170
+ const filesWithPaths = files.map((file) => (Object.assign(Object.assign({}, file), { path: buildPath(file, files) })));
171
+ let filteredFiles = [...filesWithPaths];
172
+ if (includePatterns === null || includePatterns === void 0 ? void 0 : includePatterns.length) {
173
+ const includedFiles = filterFilesByPatterns(filteredFiles, includePatterns);
174
+ const parentFiles = getParentFiles(filesWithPaths, includedFiles);
175
+ filteredFiles = [...new Set([...includedFiles, ...parentFiles])];
176
+ }
177
+ if (excludePatterns === null || excludePatterns === void 0 ? void 0 : excludePatterns.length) {
178
+ const excludedFiles = filterFilesByPatterns(filteredFiles, excludePatterns);
179
+ const remainingFiles = filteredFiles.filter((file) => !excludedFiles.includes(file));
180
+ const parentFiles = getParentFiles(filesWithPaths, remainingFiles);
181
+ filteredFiles = [...new Set([...remainingFiles, ...parentFiles])];
182
+ }
183
+ return filteredFiles;
184
+ }
185
+ exports.filterFilesByPath = filterFilesByPath;
186
+ function updateSyncedData(clientId, crowdinId, payload, provider = types_1.Provider.INTEGRATION) {
187
+ return __awaiter(this, void 0, void 0, function* () {
188
+ const existingSyncedData = yield (0, storage_1.getStorage)().getSyncedData(clientId, crowdinId, provider);
189
+ if (existingSyncedData) {
190
+ const existingFiles = JSON.parse(existingSyncedData.files);
191
+ const mergedFiles = (0, lodash_uniqby_1.default)([...existingFiles, ...payload], 'id');
192
+ yield (0, storage_1.getStorage)().updateSyncedData(JSON.stringify(mergedFiles), clientId, crowdinId, provider);
193
+ }
194
+ else {
195
+ yield (0, storage_1.getStorage)().saveSyncedData(JSON.stringify(payload), clientId, crowdinId, provider);
196
+ }
197
+ });
198
+ }
199
+ exports.updateSyncedData = updateSyncedData;
@@ -113,3 +113,11 @@ export interface UnsyncedFiles {
113
113
  files: string;
114
114
  }
115
115
  export type GetUnsyncedFiles = Pick<UnsyncedFiles, 'integrationId' | 'crowdinId'>;
116
+ export interface IntegrationSyncedData {
117
+ id: number;
118
+ integrationId: string;
119
+ crowdinId: string;
120
+ type: string;
121
+ updatedAt: string;
122
+ files?: any;
123
+ }