@crowdin/app-project-module 0.28.10 → 0.29.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.
Files changed (40) hide show
  1. package/out/handlers/crowdin-file-progress.js +1 -1
  2. package/out/handlers/crowdin-update.js +7 -2
  3. package/out/handlers/crowdin-webhook.js +4 -3
  4. package/out/handlers/integration-update.js +5 -0
  5. package/out/handlers/integration-webhook.js +2 -1
  6. package/out/handlers/main.js +1 -0
  7. package/out/handlers/manifest.js +4 -0
  8. package/out/handlers/settings-save.js +11 -0
  9. package/out/handlers/settings.d.ts +4 -0
  10. package/out/handlers/settings.js +22 -0
  11. package/out/handlers/sync-settings-save.d.ts +2 -2
  12. package/out/handlers/sync-settings-save.js +8 -11
  13. package/out/handlers/sync-settings.js +1 -1
  14. package/out/index.js +18 -1
  15. package/out/middlewares/crowdin-client.js +5 -0
  16. package/out/models/index.d.ts +64 -3
  17. package/out/models/index.js +14 -1
  18. package/out/static/js/dependent.js +1 -2
  19. package/out/storage/index.d.ts +8 -5
  20. package/out/storage/mysql.d.ts +4 -1
  21. package/out/storage/mysql.js +35 -1
  22. package/out/storage/postgre.d.ts +4 -1
  23. package/out/storage/postgre.js +35 -1
  24. package/out/storage/sqlite.d.ts +4 -1
  25. package/out/storage/sqlite.js +33 -1
  26. package/out/util/api/api.d.ts +6 -0
  27. package/out/util/api/api.js +452 -0
  28. package/out/util/api/base.d.ts +7 -0
  29. package/out/util/api/base.js +8 -0
  30. package/out/util/api/components.d.ts +227 -0
  31. package/out/util/api/components.js +228 -0
  32. package/out/util/cron.d.ts +2 -1
  33. package/out/util/cron.js +76 -9
  34. package/out/util/defaults.js +26 -3
  35. package/out/util/file-snapshot.d.ts +7 -0
  36. package/out/util/file-snapshot.js +144 -0
  37. package/out/util/webhooks.d.ts +4 -3
  38. package/out/util/webhooks.js +29 -5
  39. package/out/views/main.handlebars +140 -8
  40. package/package.json +5 -1
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createOrUpdateFileSnapshot = exports.getIntegrationSnapshot = exports.getCrowdinSnapshot = exports.getAllNewFiles = exports.getFileDiff = void 0;
13
+ const models_1 = require("../models");
14
+ const index_1 = require("./index");
15
+ const defaults_1 = require("./defaults");
16
+ const storage_1 = require("../storage");
17
+ function getFileDiff(currentFiles, savedFiles) {
18
+ return currentFiles.filter((x) => !savedFiles.some((x2) => x2.id === x.id));
19
+ }
20
+ exports.getFileDiff = getFileDiff;
21
+ function getAllNewFiles(args) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const { config, integration, crowdinApiClient, crowdinId, integrationCredentials, integrationId, projectId, integrationSettings, syncSettings, } = args;
24
+ let currentFileSnapshot = [];
25
+ const fileSnapshotData = yield (0, storage_1.getStorage)().getFilesSnapshot(integrationId, crowdinId, syncSettings.provider);
26
+ const snapshotFiles = (fileSnapshotData === null || fileSnapshotData === void 0 ? void 0 : fileSnapshotData.files) ? JSON.parse(fileSnapshotData.files) : [];
27
+ if (syncSettings.provider === models_1.Provider.CROWDIN) {
28
+ currentFileSnapshot = yield getCrowdinSnapshot(config, integration, crowdinApiClient, projectId, integrationSettings);
29
+ }
30
+ else {
31
+ currentFileSnapshot = yield getIntegrationSnapshot(integration, integrationCredentials, integrationSettings);
32
+ }
33
+ const difference = getFileDiff(currentFileSnapshot, snapshotFiles);
34
+ const onlyFiles = difference.filter((file) => 'type' in file);
35
+ const synFiles = JSON.parse(syncSettings.files);
36
+ if (syncSettings.provider === models_1.Provider.INTEGRATION) {
37
+ if (integrationSettings[`new-${syncSettings.provider}-files`]) {
38
+ return onlyFiles;
39
+ }
40
+ const syncFolders = synFiles.filter((file) => !('type' in file));
41
+ return getNewFoldersFile(syncFolders, difference);
42
+ }
43
+ else {
44
+ const files = {};
45
+ const projectData = yield crowdinApiClient.projectsGroupsApi.getProject(projectId);
46
+ const targetLanguages = projectData.data.targetLanguageIds;
47
+ if (integrationSettings[`new-${syncSettings.provider}-files`]) {
48
+ for (const file of onlyFiles) {
49
+ files[file.id] = targetLanguages;
50
+ }
51
+ }
52
+ else {
53
+ const syncFolders = currentFileSnapshot.filter((file) => !('type' in file) && Object.keys(synFiles).includes(file.id));
54
+ const newFiles = getNewFoldersFile(syncFolders, difference);
55
+ for (const file of newFiles) {
56
+ files[file.id] = targetLanguages;
57
+ }
58
+ }
59
+ return files;
60
+ }
61
+ });
62
+ }
63
+ exports.getAllNewFiles = getAllNewFiles;
64
+ function getNewFoldersFile(folders, snapshotFiles) {
65
+ let files = [];
66
+ for (const folder of folders) {
67
+ const newFiles = snapshotFiles.find((file) => file.parentId === folder.id);
68
+ if (newFiles) {
69
+ files = files.concat(newFiles);
70
+ }
71
+ }
72
+ files = files.filter((file) => 'type' in file);
73
+ return files;
74
+ }
75
+ function getCrowdinSnapshot(config, integration, crowdinApiClient, projectId, integrationSettings) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ let files = [];
78
+ if (integration.getCrowdinFiles) {
79
+ const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinApiClient, projectId);
80
+ files = yield integration.getCrowdinFiles(projectId, crowdinApiClient, rootFolder, integrationSettings);
81
+ }
82
+ return files;
83
+ });
84
+ }
85
+ exports.getCrowdinSnapshot = getCrowdinSnapshot;
86
+ function getTreeItems(integrationData) {
87
+ let files = [];
88
+ if ((0, index_1.isExtendedResultType)(integrationData)) {
89
+ files = integrationData.data;
90
+ }
91
+ else {
92
+ files = integrationData;
93
+ }
94
+ return files;
95
+ }
96
+ function getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, parentFiles) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ for (const file of parentFiles) {
99
+ if (!('type' in file)) {
100
+ let childs = yield integration.getIntegrationFiles(integrationCredentials, integrationSettings, file.id, '', 0);
101
+ childs = getTreeItems(childs);
102
+ if (childs.length > 0) {
103
+ const childFiles = yield getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, childs);
104
+ parentFiles = [...parentFiles, ...childFiles];
105
+ }
106
+ }
107
+ }
108
+ return parentFiles;
109
+ });
110
+ }
111
+ function getIntegrationSnapshot(integration, integrationCredentials, integrationSettings) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ let files = [];
114
+ let integrationData = [];
115
+ integrationData = yield integration.getIntegrationFiles(integrationCredentials, integrationSettings, '', '', 0);
116
+ files = getTreeItems(integrationData);
117
+ if (integration.integrationOneLevelFetching) {
118
+ files = yield getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, files);
119
+ }
120
+ return files;
121
+ });
122
+ }
123
+ exports.getIntegrationSnapshot = getIntegrationSnapshot;
124
+ function createOrUpdateFileSnapshot(config, integration, req, provider) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ let files = [];
127
+ const existingSnapshot = yield (0, storage_1.getStorage)().getFilesSnapshot(req.crowdinContext.clientId, req.crowdinContext.crowdinId, provider);
128
+ if (provider === models_1.Provider.CROWDIN) {
129
+ files = yield getCrowdinSnapshot(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id, req.integrationSettings);
130
+ }
131
+ if (provider === models_1.Provider.INTEGRATION) {
132
+ files = yield getIntegrationSnapshot(integration, req.integrationCredentials, req.integrationSettings);
133
+ }
134
+ if (existingSnapshot) {
135
+ (0, index_1.log)(`Updating file snapshot for provider ${provider} ${JSON.stringify(files, null, 2)}`, config.logger);
136
+ yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(files), req.crowdinContext.clientId, req.crowdinContext.crowdinId, provider);
137
+ }
138
+ else {
139
+ (0, index_1.log)(`Saving file snapshot for provider ${provider} ${JSON.stringify(files, null, 2)}`, config.logger);
140
+ yield (0, storage_1.getStorage)().saveFilesSnapshot(JSON.stringify(files), req.crowdinContext.clientId, req.crowdinContext.crowdinId, provider);
141
+ }
142
+ });
143
+ }
144
+ exports.createOrUpdateFileSnapshot = createOrUpdateFileSnapshot;
@@ -1,5 +1,5 @@
1
1
  import Crowdin from '@crowdin/crowdin-api-client';
2
- import { Config, CrowdinContextInfo, IntegrationLogic, Payload, UpdateIntegrationRequest, WebhookUrlParams } from '../models';
2
+ import { Config, CrowdinContextInfo, IntegrationLogic, IntegrationSyncSettings, Payload, Provider, TreeItem, UpdateIntegrationRequest, WebhookUrlParams } from '../models';
3
3
  import { WebhooksModel } from '@crowdin/crowdin-api-client/out/webhooks';
4
4
  import { Request } from 'express';
5
5
  export declare function encodedUrlParam(config: Config, integration: IntegrationLogic, crowdinContext: CrowdinContextInfo): string;
@@ -14,7 +14,7 @@ export declare function updateCrowdinWebhooks(config: Config, client: Crowdin, p
14
14
  export declare function unregisterCrowdinWebhooks(config: Config, client: Crowdin, projectId: number, webhook: WebhooksModel.Webhook): Promise<void>;
15
15
  export declare function unregisterAllCrowdinWebhooks(config: Config, integration: IntegrationLogic, crowdinId: string): Promise<void>;
16
16
  export declare function filterSyncFiles(events: Payload[], syncFileSettings: UpdateIntegrationRequest): UpdateIntegrationRequest;
17
- export declare function prepareWebhookData(config: Config, integration: IntegrationLogic, webhookUrlParam: string, provider: string): Promise<{
17
+ export declare function prepareWebhookData(config: Config, integration: IntegrationLogic, webhookUrlParam: string, provider: Provider): Promise<{
18
18
  projectId: number;
19
19
  crowdinClient: {
20
20
  client: Crowdin;
@@ -23,7 +23,8 @@ export declare function prepareWebhookData(config: Config, integration: Integrat
23
23
  preparedIntegrationCredentials: any;
24
24
  rootFolder: import("@crowdin/crowdin-api-client").SourceFilesModel.Directory | undefined;
25
25
  appSettings: any;
26
- syncSettings: import("../models").IntegrationSyncSettings | null | undefined;
26
+ syncSettings: IntegrationSyncSettings | null;
27
+ newFiles: TreeItem[] | UpdateIntegrationRequest;
27
28
  }>;
28
29
  export declare function updateCrowdinFromWebhookRequest(integration: IntegrationLogic, webhookData: any, req: Request): Promise<void | import("../models").ExtendedResult<void>>;
29
30
  export declare function listenQueueMessage(config: Config, integration: IntegrationLogic, queueUrl: string, queueName: string): Promise<void>;
@@ -44,6 +44,7 @@ const connection_1 = require("./connection");
44
44
  const storage_1 = require("../storage");
45
45
  const defaults_1 = require("./defaults");
46
46
  const util_1 = require("../util");
47
+ const file_snapshot_1 = require("./file-snapshot");
47
48
  const HookEvents = {
48
49
  ALL: ['file.translated', 'file.approved'],
49
50
  TRANSLATED: ['file.translated'],
@@ -227,10 +228,12 @@ function filterSyncFiles(events, syncFileSettings) {
227
228
  }
228
229
  exports.filterSyncFiles = filterSyncFiles;
229
230
  function prepareWebhookData(config, integration, webhookUrlParam, provider) {
231
+ var _a;
230
232
  return __awaiter(this, void 0, void 0, function* () {
231
233
  let rootFolder = undefined;
232
234
  let appSettings = null;
233
235
  let syncSettings = null;
236
+ let newFiles = [];
234
237
  const { projectId, crowdinId, clientId } = decodedUrlParam(config, webhookUrlParam);
235
238
  const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
236
239
  const crowdinClient = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
@@ -240,11 +243,32 @@ function prepareWebhookData(config, integration, webhookUrlParam, provider) {
240
243
  appSettings = JSON.parse(integrationCredentials.config);
241
244
  const isWebhookSync = +appSettings.syncType === models_1.SyncType.WEBHOOKS;
242
245
  if (isWebhookSync) {
243
- syncSettings = yield (0, storage_1.getStorage)().getSyncSettings(clientId, crowdinId, 'schedule', provider);
246
+ syncSettings = (yield (0, storage_1.getStorage)().getSyncSettings(clientId, crowdinId, 'schedule', provider));
244
247
  rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient.client, projectId);
248
+ if ((_a = integration.syncNewElements) === null || _a === void 0 ? void 0 : _a[provider]) {
249
+ newFiles = yield (0, file_snapshot_1.getAllNewFiles)({
250
+ config,
251
+ integration,
252
+ crowdinApiClient: crowdinClient.client,
253
+ crowdinId,
254
+ integrationCredentials: preparedIntegrationCredentials,
255
+ integrationId: clientId,
256
+ projectId,
257
+ integrationSettings: appSettings,
258
+ syncSettings,
259
+ });
260
+ }
245
261
  }
246
262
  }
247
- return { projectId, crowdinClient, preparedIntegrationCredentials, rootFolder, appSettings, syncSettings };
263
+ return {
264
+ projectId,
265
+ crowdinClient,
266
+ preparedIntegrationCredentials,
267
+ rootFolder,
268
+ appSettings,
269
+ syncSettings,
270
+ newFiles,
271
+ };
248
272
  });
249
273
  }
250
274
  exports.prepareWebhookData = prepareWebhookData;
@@ -252,9 +276,9 @@ function updateCrowdinFromWebhookRequest(integration, webhookData, req) {
252
276
  var _a, _b;
253
277
  return __awaiter(this, void 0, void 0, function* () {
254
278
  let filesToSync = [];
255
- const { projectId, crowdinClient, preparedIntegrationCredentials, rootFolder, appSettings, syncSettings } = webhookData;
279
+ const { projectId, crowdinClient, preparedIntegrationCredentials, rootFolder, appSettings, syncSettings, newFiles, } = webhookData;
256
280
  if ((_a = integration.webhooks) === null || _a === void 0 ? void 0 : _a.integrationWebhookInterceptor) {
257
- filesToSync = yield ((_b = integration.webhooks) === null || _b === void 0 ? void 0 : _b.integrationWebhookInterceptor(projectId, crowdinClient.client, preparedIntegrationCredentials, rootFolder, appSettings, syncSettings, req));
281
+ filesToSync = yield ((_b = integration.webhooks) === null || _b === void 0 ? void 0 : _b.integrationWebhookInterceptor(projectId, crowdinClient.client, preparedIntegrationCredentials, rootFolder, appSettings, syncSettings, req, newFiles));
258
282
  }
259
283
  return yield integration.updateCrowdin(projectId, crowdinClient.client, preparedIntegrationCredentials, filesToSync, rootFolder, appSettings);
260
284
  });
@@ -296,7 +320,7 @@ function consumer(channel, config, integration) {
296
320
  const data = JSON.parse(msg.content.toString());
297
321
  const urlParam = (_a = integration.webhooks) === null || _a === void 0 ? void 0 : _a.urlParam;
298
322
  const webhookUrlParam = data.query[urlParam];
299
- const webhookData = yield prepareWebhookData(config, integration, webhookUrlParam, 'integration');
323
+ const webhookData = yield prepareWebhookData(config, integration, webhookUrlParam, models_1.Provider.INTEGRATION);
300
324
  yield updateCrowdinFromWebhookRequest(integration, webhookData, data);
301
325
  }
302
326
  catch (e) {
@@ -34,6 +34,12 @@
34
34
  {{/if}}
35
35
  </div>
36
36
  <crowdin-simple-integration
37
+ {{#if syncNewElements.crowdin}}
38
+ skip-crowdin-auto-schedule
39
+ {{/if}}
40
+ {{#if syncNewElements.integration}}
41
+ skip-integration-auto-schedule
42
+ {{/if}}
37
43
  {{#or withCronSync.crowdin webhooks.crowdin}}
38
44
  crowdin-schedule="true"
39
45
  {{/or}}
@@ -170,7 +176,12 @@
170
176
  <crowdin-p>{{label}}</crowdin-p>
171
177
  {{/if}}
172
178
  {{/if}}
173
- <div style="padding: 8px"></div>
179
+ <div
180
+ style="padding: 8px"
181
+ {{#if dependencySettings}}
182
+ data-dependency="{{dependencySettings}}"
183
+ {{/if}}
184
+ ></div>
174
185
  {{/each}}
175
186
  </div>
176
187
  <div slot="footer">
@@ -178,6 +189,36 @@
178
189
  </div>
179
190
  </crowdin-modal>
180
191
  {{/if}}
192
+ {{#or syncNewElements.crowdin syncNewElements.integration}}
193
+ <crowdin-modal
194
+ id="confirm-schedule-modal"
195
+ modal-width="50"
196
+ modal-title="Synchronization options"
197
+ close-button-title="Close"
198
+ close-button="true"
199
+ body-overflow-unset
200
+ >
201
+ <crowdin-checkbox
202
+ id="selected-files"
203
+ name="selected-files"
204
+ label="Selected files"
205
+ class="hydrated"
206
+ onchange="onChangeAutoSynchronizationOptions(this)"
207
+ >
208
+ </crowdin-checkbox>
209
+ <crowdin-checkbox
210
+ id="new-files"
211
+ name="new-files"
212
+ label="New files"
213
+ class="hydrated"
214
+ onchange="onChangeAutoSynchronizationOptions(this)"
215
+ >
216
+ </crowdin-checkbox>
217
+ <div slot="footer">
218
+ <crowdin-button outlined id="save-schedule-sync" onclick="saveScheduleSync()">Save</crowdin-button>
219
+ </div>
220
+ </crowdin-modal>
221
+ {{/or}}
181
222
  </body>
182
223
  <script type="text/javascript">
183
224
  document.body.addEventListener('refreshFilesList', (e) => {
@@ -267,7 +308,7 @@
267
308
  function getIntegrationData(hardReload = false, parentId = '', search = '', page = 0) {
268
309
  appComponent.setAttribute('is-integration-loading', true);
269
310
  checkOrigin()
270
- .then(restParams => fetch(`api/integration/data${restParams}&parent_id=${parentId}&search=${search}&page=${page}`))
311
+ .then(restParams => fetch(`api/integration/data${restParams}&parent_id=${encodeURIComponent(parentId)}&search=${encodeURIComponent(search)}&page=${page}`))
271
312
  .then(checkResponse)
272
313
  .then((res) => {
273
314
  const files = res.data;
@@ -317,9 +358,18 @@
317
358
  .then(checkResponse)
318
359
  .then((res) => {
319
360
  if (provider === 'crowdin') {
320
- appComponent.setCrowdinScheduleSync(res);
361
+ {{#if syncNewElements.crowdin}}
362
+ appComponent.setCrowdinScheduleSync(res, true, true);
363
+ {{else}}
364
+ appComponent.setCrowdinScheduleSync(res);
365
+ {{/if}}
321
366
  } else {
322
- appComponent.setIntegrationScheduleSync(res);
367
+ {{#if syncNewElements.integration}}
368
+ appComponent.setIntegrationScheduleSync(res, true, true);
369
+ {{else}}
370
+ appComponent.setIntegrationScheduleSync(res);
371
+ {{/if}}
372
+
323
373
  }
324
374
  })
325
375
  .catch(e => catchRejection(e, 'Can\'t fetch file progress'));
@@ -521,19 +571,91 @@
521
571
  {{/if}}
522
572
 
523
573
  {{#or withCronSync webhooks}}
574
+ const scheduleModal = document.getElementById('confirm-schedule-modal');
575
+ let syncData;
576
+
524
577
  document.body.addEventListener('integrationScheduleSync', setIntegrationScheduleSync);
525
578
  document.body.addEventListener('crowdinScheduleSync', setCrowdinScheduleSync);
526
579
  document.body.addEventListener('crowdinDisableSync', disableCrowdinSync);
527
580
  document.body.addEventListener('integrationDisableSync', disableIntegrationSync);
528
581
 
582
+ async function saveScheduleSync() {
583
+ const newFile = scheduleModal.querySelector('#new-files').checked || false;
584
+ const selectedFiles = scheduleModal.querySelector('#selected-files').checked || false;
585
+
586
+ const type = scheduleModal.getAttribute('data-type');
587
+
588
+ if (type === 'crowdin') {
589
+ appComponent.setCrowdinScheduleSync(syncData, newFile, selectedFiles);
590
+ const syncedFiles = await appComponent.getCrowdinScheduleSync(true);
591
+ appComponent.setAttribute('is-crowdin-loading', true);
592
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
593
+ } else if (type === 'integration') {
594
+ appComponent.setIntegrationScheduleSync(syncData, newFile, selectedFiles);
595
+ const syncedFiles = await appComponent.getIntegrationScheduleSync(true);
596
+ appComponent.setAttribute('is-integration-loading', true);
597
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
598
+ }
599
+
600
+ scheduleModal.close();
601
+ }
602
+
603
+ function openScheduleModal(type) {
604
+ const newFile = scheduleModal.querySelector('#new-files')
605
+ const selectedFiles = scheduleModal.querySelector('#selected-files');
606
+
607
+ newFile.checked = false;
608
+ newFile.value = false;
609
+ selectedFiles.checked = true;
610
+ selectedFiles.value = true;
611
+ scheduleModal.querySelector('#save-schedule-sync').setAttribute('disabled', false);
612
+ scheduleModal.setAttribute('data-type', type);
613
+ scheduleModal.open();
614
+ }
615
+
616
+ function onChangeAutoSynchronizationOptions() {
617
+ const newFiles = document.getElementById('new-files').checked || false;
618
+ const selectedFiles = document.getElementById('selected-files').checked || false;
619
+ const buttonSaveScheduleSync = document.getElementById('save-schedule-sync');
620
+
621
+ if (newFiles || selectedFiles) {
622
+ buttonSaveScheduleSync.removeAttribute('disabled');
623
+ } else {
624
+ buttonSaveScheduleSync.setAttribute('disabled', true);
625
+ }
626
+ }
627
+
628
+ async function hasFolder(selectedFiles) {
629
+ let isFolder;
630
+ if (Array.isArray(selectedFiles)) {
631
+ isFolder = selectedFiles.find((file) => file.node_type === folderType || file.node_type === branchType);
632
+ } else {
633
+ const files = await appComponent.getCrowdinFilesData();
634
+ const folders = files.filter((file) => file.node_type === folderType || file.node_type === branchType);
635
+ isFolder = folders.find((folder) => selectedFiles.hasOwnProperty(folder.id));
636
+ }
637
+
638
+ return isFolder !== undefined;
639
+ }
640
+
529
641
  async function setIntegrationScheduleSync(e) {
530
642
  if (e.detail.length === 0) {
531
643
  showToast('Select templates which will be pushed to Crowdin');
532
644
  return;
533
645
  }
534
- appComponent.setAttribute('is-integration-loading', true);
535
- const syncedFiles = await appComponent.getIntegrationScheduleSync();
536
646
 
647
+ {{#if syncNewElements.integration}}
648
+ syncData = e.detail;
649
+ const isFolder = await hasFolder(e.detail);
650
+ if (isFolder) {
651
+ openScheduleModal('integration');
652
+ return;
653
+ }
654
+
655
+ appComponent.setIntegrationScheduleSync(e.detail);
656
+ {{/if}}
657
+ const syncedFiles = await appComponent.getIntegrationScheduleSync();
658
+ appComponent.setAttribute('is-integration-loading', true);
537
659
  updateSyncSettings(syncedFiles, 'schedule', 'integration');
538
660
  }
539
661
 
@@ -542,9 +664,19 @@
542
664
  showToast('Select templates which will be pushed to Crowdin');
543
665
  return;
544
666
  }
545
- appComponent.setAttribute('is-crowdin-loading', true);
546
- const syncedFiles = await appComponent.getCrowdinScheduleSync();
547
667
 
668
+ {{#if syncNewElements.crowdin}}
669
+ syncData = e.detail;
670
+ const isFolder = await hasFolder(e.detail);
671
+ if (isFolder) {
672
+ openScheduleModal('crowdin');
673
+ return;
674
+ }
675
+
676
+ appComponent.setCrowdinScheduleSync(e.detail);
677
+ {{/if}}
678
+ const syncedFiles = await appComponent.getCrowdinScheduleSync();
679
+ appComponent.setAttribute('is-crowdin-loading', true);
548
680
  updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
549
681
  }
550
682
 
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.28.10",
3
+ "version": "0.29.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc -p ./ && cp -R views out && cp -R static out && cp logo.png out && rollup -c rollup.config.mjs",
9
+ "build-main": "tsc -p ./ && cp -R views out && cp -R static out && cp logo.png out",
9
10
  "lint": "eslint --fix \"{src,tests}/**/*.{js,ts}\"",
10
11
  "prettier": "prettier --config .prettierrc src/**/*.ts --write",
11
12
  "lint-ci": "eslint \"{src,tests}/**/*.{js,ts}\"",
@@ -22,7 +23,9 @@
22
23
  "mysql2": "^2.3.3",
23
24
  "node-cron": "^3.0.2",
24
25
  "pg": "^8.10.0",
26
+ "redoc-express": "^2.1.0",
25
27
  "sqlite3": "^5.1.6",
28
+ "swagger-jsdoc": "^6.2.8",
26
29
  "uuid": "^8.3.2"
27
30
  },
28
31
  "devDependencies": {
@@ -48,6 +51,7 @@
48
51
  "@types/jest": "^29.5.2",
49
52
  "@types/node": "^12.20.55",
50
53
  "@types/node-cron": "^3.0.7",
54
+ "@types/swagger-jsdoc": "^6.0.1",
51
55
  "@typescript-eslint/eslint-plugin": "^2.3.1",
52
56
  "@typescript-eslint/parser": "^2.3.1",
53
57
  "eslint": "^6.4.0",