@localazy/strapi-plugin 0.1.17 → 0.2.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.
@@ -1,529 +1,62 @@
1
1
  "use strict";
2
2
 
3
- const flattenObject = require("../utils/flatten-object");
4
- const {
5
- getCollectionsNames,
6
- findSetupModelByCollectionName,
7
- findSetupModelByCollectionUid,
8
- isCollectionTransferEnabled,
9
- getPickPathsWithComponents,
10
- } = require("../utils/transfer-setup-utils");
11
- const pickEntries = require("../utils/pick-entries");
12
- const config = require("../config").default;
13
- const {
14
- isoLocalazyToStrapi,
15
- isoStrapiToLocalazy,
16
- } = require("../utils/iso-locales-utils");
17
- const shouldSetDownloadedProperty = require("../functions/should-set-downloaded-property");
18
- const set = require("lodash/set");
19
- const isEmpty = require("lodash/isEmpty");
20
- const omitDeep = require("../utils/omit-deep");
21
- const RequestInitiatorHelper = require('../utils/request-initiator-helper');
22
- const PluginSettingsServiceHelper = require('../services/helpers/plugin-settings-service-helper');
23
3
 
24
- const getFilteredLanguagesCodesForDownload = async (languagesCodes) => {
25
- const pluginSettingsServiceHelper = new PluginSettingsServiceHelper(strapi);
26
- const requestInitiatorHelper = new RequestInitiatorHelper(strapi);
27
- await pluginSettingsServiceHelper.setup();
28
- let localLanguagesCodes;
29
- if (requestInitiatorHelper.isInitiatedByLocalazyWebhook()) {
30
- // called by a webhook
31
- localLanguagesCodes = pluginSettingsServiceHelper.getWebhookLanguagesCodes();
32
- } else if (requestInitiatorHelper.isInitiatedByLocalazyPluginUI()) {
33
- // called by a user from the UI
34
- localLanguagesCodes = pluginSettingsServiceHelper.getUiLanguagesCodes();
35
- } else {
36
- strapi.log.warn("Called by an unknown initiator.");
37
- return languagesCodes;
38
- }
39
-
40
- if (isEmpty(localLanguagesCodes)) {
41
- return languagesCodes;
42
- }
43
-
44
- return languagesCodes.filter((code) => localLanguagesCodes.includes(code));
45
- };
4
+ const { UPLOAD_FINISHED_EVENT, DOWNLOAD_FINISHED_EVENT } = require('../constants/events');
5
+ const jobNotificationServiceFactory = require('../services/helpers/job-notification-service');
46
6
 
47
7
  module.exports = {
48
8
  async upload(ctx) {
9
+ const JobNotificationService = new jobNotificationServiceFactory(strapi.StrapIO);
49
10
  try {
50
- let success = true;
51
- const messageReport = [];
52
-
53
- // Strapi Service
54
- const StrapiService = strapi
55
- .plugin("localazy")
56
- .service("strapiService");
57
-
58
- // Strapi i18n Service
59
- const StrapiI18nService = strapi
60
- .plugin("localazy")
61
- .service("strapiI18nService");
62
-
63
- // Localazy Upload Service
64
- const LocalazyUploadService = strapi
65
- .plugin("localazy")
66
- .service("localazyUploadService");
67
-
68
- // get content transfer setup
69
- const contentTransferSetup = await strapi
11
+ const LocalazyTransferUploadService = strapi
70
12
  .plugin("localazy")
71
- .service("pluginSettingsService")
72
- .getContentTransferSetup();
73
-
74
- if (!contentTransferSetup.has_setup) {
75
- const message = "Content transfer setup is not set up.";
76
- strapi.log.info(message);
77
- messageReport.push(message);
78
- success = false;
79
- ctx.body = {
80
- success,
81
- report: messageReport,
82
- };
83
- return;
84
- }
85
-
86
- const { setup } = contentTransferSetup;
87
- const collectionsNames = getCollectionsNames(setup);
88
- const models = await StrapiService.getModels();
89
- // flatten Strapi content
90
- let flattenContent = {};
91
-
92
- for (const collectionName of collectionsNames) {
93
- const currentModel = models.find(
94
- (model) => model.collectionName === collectionName
95
- );
96
- const modelUid = currentModel.uid;
97
- if (!currentModel) {
98
- strapi.log.warn(`Model with uid ${modelUid} is not found.`);
99
- continue;
100
- }
101
-
102
- const transferSetupModel = findSetupModelByCollectionName(
103
- setup,
104
- collectionName
105
- );
106
-
107
- if (!isCollectionTransferEnabled(setup, collectionName)) {
108
- const message = `Collection ${collectionName} transfer is disabled.`;
109
- messageReport.push(message);
110
- strapi.log.info(message);
111
- continue;
112
- }
113
-
114
- // get only enabled fields paths
115
- const currentTransferSetupModel = transferSetupModel[collectionName];
116
- const pickPaths = getPickPathsWithComponents(currentTransferSetupModel);
117
- if (!pickPaths.length) {
118
- const message = `No fields for collection ${collectionName} transfer are enabled.`;
119
- messageReport.push(message);
120
- strapi.log.warn(message);
121
- continue;
122
- }
123
- const pickPathsWithUid = pickPaths.map(
124
- (pickPath) => `${modelUid}.${pickPath}`
125
- );
126
-
127
- let entries = await strapi.entityService.findMany(modelUid, {
128
- populate: "deep",
129
- });
130
- entries = omitDeep(entries, [
131
- // "__component",
132
- "locale",
133
- "localizations",
134
- "createdAt",
135
- "createdBy",
136
- "updatedAt",
137
- "updatedBy",
138
- "publishedAt",
139
- ]);
140
-
141
- if (!entries) {
142
- strapi.log.info(`No entries found for model ${modelUid}`);
143
- continue;
144
- }
145
-
146
- if (!Array.isArray(entries)) {
147
- entries = [entries];
148
- }
13
+ .service("localazyTransferUploadService");
149
14
 
150
- entries.forEach((entry) => {
151
- const flatten = flattenObject({
152
- [modelUid]: entry,
153
- });
154
- // get only enabled fields; "__component" will be filtered out inside of the function
155
- const pickedFlatten = pickEntries(flatten, pickPathsWithUid);
156
-
157
- flattenContent = {
158
- ...flattenContent,
159
- ...pickedFlatten,
160
- };
161
- });
162
- }
163
-
164
- // get Strapi default language and convert it to Localazy language code
165
- const strapiLocales = await StrapiI18nService.getLocales(ctx);
166
- const defaultLocale = strapiLocales.find((locale) => locale.isDefault);
167
-
168
- const locale = defaultLocale
169
- ? isoStrapiToLocalazy(defaultLocale.code)
170
- : config.LOCALAZY_DEFAULT_LOCALE;
171
-
172
- const chunks = LocalazyUploadService.splitToChunks(flattenContent);
173
- const importFile = LocalazyUploadService.createImportFileRepresentation(
174
- config.LOCALAZY_DEFAULT_FILE_NAME,
175
- config.LOCALAZY_DEFAULT_FILE_PATH,
176
- config.LOCALAZY_DEFAULT_FILE_EXTENSION,
177
- locale,
178
- chunks
179
- );
180
- // Use `deprecate: "file"` if there is one chunk of transferred data only!
181
- const hasMoreTransferFilesChunks = importFile.length > 1;
182
- const uploadConfig = !hasMoreTransferFilesChunks ? { deprecate: "file" } : {};
183
- await LocalazyUploadService.upload(
184
- importFile,
185
- uploadConfig
186
- );
15
+ /**
16
+ * start executing the function after a delay
17
+ * (to let the client receive and subscribe to the messages stream)
18
+ */
19
+ // TODO: let the client send a message to the server to start the upload (that it's subscribed to the stream)
20
+ setTimeout(() => (LocalazyTransferUploadService.upload(JobNotificationService, ctx)), 1000);
187
21
 
188
22
  ctx.body = {
189
- success,
190
- report: messageReport,
23
+ streamIdentifier: JobNotificationService.getStreamIdentifier(),
191
24
  };
25
+
192
26
  } catch (e) {
193
27
  strapi.log.error(e.message);
28
+ await JobNotificationService.emit(UPLOAD_FINISHED_EVENT, {
29
+ success: false,
30
+ message: e.message,
31
+ });
194
32
  ctx.throw(400, e);
195
33
  }
196
34
  },
197
35
 
198
36
  async download(ctx) {
199
- let success = true;
200
- const messageReport = [];
201
-
202
- // Strapi service
203
- const StrapiService = strapi
204
- .plugin("localazy")
205
- .service("strapiService");
206
-
207
- // Strapi i18n Service
208
- const StrapiI18nService = strapi
209
- .plugin("localazy")
210
- .service("strapiI18nService");
211
-
212
- const LocalazyUserService = strapi
213
- .plugin("localazy")
214
- .service("localazyUserService");
215
- const LocalazyPubApiService = strapi
216
- .plugin("localazy")
217
- .service("localazyPubApiService");
218
- const LocalazyDownloadService = strapi
219
- .plugin("localazy")
220
- .service("localazyDownloadService");
221
- const strapiLocalazyI18nService = strapi
222
- .plugin("localazy")
223
- .service("strapiLocalazyI18nService");
224
-
225
- const contentTransferSetup = await strapi
226
- .plugin("localazy")
227
- .service("pluginSettingsService")
228
- .getContentTransferSetup();
229
-
230
- const user = await LocalazyUserService.getUser();
231
-
232
- /**
233
- * Get Strapi File
234
- */
235
- const strapiFile = await LocalazyPubApiService.getStrapiFile(
236
- user.project.id
237
- );
238
- if (!strapiFile) {
239
- success = false;
240
- const message = `File ${config.LOCALAZY_DEFAULT_FILE_NAME} not found`;
241
- strapi.log.error(message);
242
- messageReport.push(message);
243
- ctx.body = {
244
- success,
245
- report: messageReport,
246
- };
247
- return;
248
- }
249
-
250
- /**
251
- * Get Localazy Project (incl. languages)
252
- */
253
- const project = await LocalazyPubApiService.getProject(
254
- user.project.id,
255
- false,
256
- true
257
- );
258
- if (!project) {
259
- success = false;
260
- const message = `Project ${user.project.id} not found`;
261
- strapi.log.error(message);
262
- messageReport.push(message);
263
- ctx.body = {
264
- success,
265
- report: messageReport,
266
- };
267
- return;
268
- }
269
-
270
- /**
271
- * Get Localazy project languages
272
- */
273
- const projectLanguages = project.languages;
274
- const projectSourceLanguageId = project.sourceLanguage;
275
- const sourceLanguage = project.languages.find(
276
- (language) => language.id === projectSourceLanguageId
277
- );
278
-
279
- if (!sourceLanguage) {
280
- success = false;
281
- const message = "Source language not found";
282
- strapi.log.error(message);
283
- messageReport.push(message);
284
- ctx.body = {
285
- success,
286
- report: messageReport,
287
- };
288
- return;
289
- }
37
+ const JobNotificationService = new jobNotificationServiceFactory(strapi.StrapIO);
38
+ try {
39
+ const LocalazyTransferDownloadService = strapi
40
+ .plugin("localazy")
41
+ .service("localazyTransferDownloadService");
290
42
 
291
- const projectSourceLanguageCode = sourceLanguage.code;
292
- const projectLanguagesWithoutSourceLanguage = projectLanguages.filter(
293
- (language) => language.code !== projectSourceLanguageCode
294
- );
43
+ /**
44
+ * start executing the function after a delay
45
+ * (to let the client receive and subscribe to the messages stream)
46
+ */
47
+ // TODO: let the client send a message to the server to start the download (that it's subscribed to the stream)
48
+ setTimeout(() => (LocalazyTransferDownloadService.download(JobNotificationService, ctx)), 1000);
295
49
 
296
- if (!projectLanguagesWithoutSourceLanguage.length) {
297
- const message = "Your Localazy project is not translated to other languages.";
298
- strapi.log.info(message);
299
- messageReport.push(message);
300
50
  ctx.body = {
301
- success,
302
- report: messageReport,
51
+ streamIdentifier: JobNotificationService.getStreamIdentifier(),
303
52
  };
304
- return;
305
- }
306
-
307
- /**
308
- * Get project languages codes
309
- */
310
- let languagesCodes = projectLanguagesWithoutSourceLanguage.map(
311
- (language) => language.code
312
- );
313
- // process filtered languages only / keep all if empty!
314
- languagesCodes = await getFilteredLanguagesCodesForDownload(languagesCodes);
315
-
316
- /**
317
- * Iterate over languages and create the ones that are not present in Strapi
318
- */
319
- const strapiUnsupportedLanguages = []; // do not iterate over these languages later
320
- const strapiLocales = await StrapiI18nService.getLocales(ctx);
321
- for (const isoLocalazy of languagesCodes) {
322
- try {
323
- const isoStrapi = isoLocalazyToStrapi(isoLocalazy);
324
- const strapiLocale = strapiLocales.find(
325
- (locale) => locale.code === isoStrapi
326
- );
327
- // skip creating locale if it already exists
328
- if (strapiLocale) {
329
- strapi.log.info(`The Localazy locale ${isoLocalazy} already exists.`);
330
- continue;
331
- }
332
-
333
- const newLocaleCode = await StrapiI18nService.createStrapiLocale(
334
- ctx,
335
- isoLocalazy
336
- );
337
- messageReport.push(`Created locale ${newLocaleCode}`);
338
- } catch (e) {
339
- strapi.log.error(e.message);
340
- if (e.name === "ValidationError") {
341
- // store unsupported language code
342
- strapiUnsupportedLanguages.push(isoLocalazy);
343
- messageReport.push(`Language ${isoLocalazy} is not supported by Strapi`);
344
- } else {
345
- messageReport.push(e.message);
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * Download all keys for Strapi-supported languages
352
- */
353
- const supportedLanguages = languagesCodes.filter(
354
- (languageCode) => !strapiUnsupportedLanguages.includes(languageCode)
355
- );
356
- const localazyContent = {};
357
- for (const isoLocalazy of supportedLanguages) {
358
- const isoStrapi = isoLocalazyToStrapi(isoLocalazy);
359
- const result = await LocalazyDownloadService.download({
360
- projectId: user.project.id,
361
- fileId: strapiFile.id,
362
- lang: isoStrapi,
53
+ } catch (e) {
54
+ strapi.log.error(e.message);
55
+ await JobNotificationService.emit(DOWNLOAD_FINISHED_EVENT, {
56
+ success: false,
57
+ message: e.message,
363
58
  });
364
- if (!result.success) {
365
- messageReport.push(result.message);
366
- }
367
- const langKeys = result.data;
368
- localazyContent[isoLocalazy] = langKeys;
369
- }
370
-
371
- /**
372
- * Parse Localazy content
373
- */
374
- const parsedLocalazyContent = {};
375
- const strapiContentTypesModels = await StrapiService.getModels();
376
- for (const [isoLocalazy, keys] of Object.entries(localazyContent)) {
377
- const isoStrapi = isoLocalazyToStrapi(isoLocalazy);
378
- if (!isoStrapi) {
379
- messageReport.push(
380
- `Language ${isoLocalazy} is not supported by Strapi`
381
- );
382
- continue;
383
- }
384
-
385
- for (const localazyEntry of keys) {
386
- const key = localazyEntry.key[0];
387
- const value = localazyEntry.value;
388
-
389
- const parsedKey = StrapiI18nService.parseLocalazyKey(key);
390
-
391
- const modelContentTransferSetup = findSetupModelByCollectionUid(
392
- contentTransferSetup,
393
- strapiContentTypesModels,
394
- parsedKey.uid
395
- );
396
-
397
- if (typeof modelContentTransferSetup !== "undefined" && shouldSetDownloadedProperty(modelContentTransferSetup, parsedKey.rest)) {
398
- const parsedKeyRestWithoutComponents = parsedKey.rest;
399
- const setKey = [
400
- isoStrapi,
401
- parsedKey.uid,
402
- parsedKey.id,
403
- ...parsedKeyRestWithoutComponents,
404
- ];
405
-
406
- set(parsedLocalazyContent, setKey, value);
407
- }
408
- }
409
- }
410
-
411
- /**
412
- * Iterate over parsed Localazy content and insert/update content in Strapi
413
- */
414
- for (const [isoStrapi, contentTypes] of Object.entries(
415
- parsedLocalazyContent
416
- )) {
417
- for (const [uid, models] of Object.entries(contentTypes)) {
418
- /**
419
- * Model could not exist anymore in Strapi
420
- */
421
- const strapiContentTypeModel = strapiContentTypesModels.find(
422
- (model) => model.uid === uid
423
- );
424
- if (!strapiContentTypeModel) {
425
- strapi.log.info(
426
- `Model ${uid} not found in Strapi, it has been deleted, skipping...`
427
- );
428
- continue;
429
- }
430
-
431
- for (const [id, translatedModel] of Object.entries(models)) {
432
- try {
433
- /**
434
- * Get original source language entry
435
- */
436
- const baseEntry = await strapi.entityService.findOne(uid, id, {
437
- populate: "deep",
438
- });
439
-
440
- if (isEmpty(baseEntry)) {
441
- const message = `Source language entry ${uid}[${id}] does not exist anymore, skipping...`;
442
- throw new Error(message);
443
- }
444
-
445
- /**
446
- * Get current language entry
447
- */
448
- const baseEntryLocalizations = baseEntry.localizations;
449
-
450
- if (!Array.isArray(baseEntryLocalizations)) {
451
- throw new Error(
452
- `Content type ${uid} does not support localizations, skipping...`
453
- );
454
- }
455
-
456
- const baseEntryCurrentLanguageLocalizationInfo =
457
- baseEntryLocalizations.find(
458
- (localization) => localization.locale === isoStrapi
459
- );
460
-
461
- if (!baseEntryCurrentLanguageLocalizationInfo) {
462
- // create new entry
463
- try {
464
- const createdEntry = await strapiLocalazyI18nService.createEntry(
465
- ctx,
466
- uid,
467
- strapiContentTypesModels,
468
- translatedModel,
469
- baseEntry,
470
- isoStrapi,
471
- );
472
-
473
- messageReport.push(
474
- `Created new entry ${uid}[${createdEntry.id}] in language ${isoStrapi}`
475
- );
476
- strapi.log.info(`Created new entry ${uid}[${createdEntry.id}] in language ${isoStrapi}`);
477
- } catch (e) {
478
- success = false;
479
- strapi.log.error(e.message);
480
- strapi.log.error(JSON.stringify(e.details?.errors || {}));
481
- messageReport.push(
482
- `Cannot create an entry in ${isoStrapi} for ${uid}[${baseEntry.id}]: ${e.message}`
483
- );
484
- }
485
- } else {
486
- // update existing entry
487
- try {
488
- const localizedEntryId =
489
- baseEntryCurrentLanguageLocalizationInfo.id;
490
-
491
- const updatedEntry = await strapiLocalazyI18nService.updateEntry(
492
- uid,
493
- localizedEntryId,
494
- strapiContentTypesModels,
495
- translatedModel,
496
- baseEntry,
497
- isoStrapi,
498
- );
499
-
500
- messageReport.push(
501
- `Updated ${uid}[${updatedEntry.id}] (${isoStrapi})`
502
- );
503
- strapi.log.info(`Updated ${uid}[${updatedEntry.id}] (${isoStrapi})`);
504
- } catch (e) {
505
- success = false;
506
- strapi.log.error(e.message);
507
- strapi.log.error(JSON.stringify(e.details?.errors || {}));
508
- messageReport.push(
509
- `Cannot update an ${uid}[${baseEntryCurrentLanguageLocalizationInfo.id}] (${isoStrapi}): ${e.message}`
510
- );
511
- }
512
- }
513
- } catch (e) {
514
- success = false;
515
- strapi.log.error(e.message);
516
- messageReport.push(
517
- `An error occured while processing download: ${e.message}`
518
- );
519
- }
520
- }
521
- }
59
+ ctx.throw(400, e);
522
60
  }
523
-
524
- ctx.body = {
525
- success,
526
- report: messageReport,
527
- };
528
61
  },
529
62
  };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ const { LOCALAZY_PLUGIN_CHANNEL } = require('../../constants/channels');
4
+ const generateRandomId = require('../../utils/generate-random-id');
5
+
6
+ class JobNotificationService {
7
+ _strapio;
8
+ _channel;
9
+ _jobStreamIdentifier;
10
+ constructor(strapio) {
11
+ this._strapio = strapio;
12
+ this._channel = LOCALAZY_PLUGIN_CHANNEL;
13
+ this._jobStreamIdentifier = generateRandomId();
14
+ }
15
+
16
+ setChannel(channel) {
17
+ this._channel = channel;
18
+ }
19
+
20
+ getStreamIdentifier() {
21
+ return this._jobStreamIdentifier;
22
+ }
23
+
24
+ async emit(event, data) {
25
+ await new Promise((resolve) => setImmediate(resolve));
26
+ this._strapio.emitRaw(this._channel, `${event}:${this._jobStreamIdentifier}`, data);
27
+ }
28
+ }
29
+
30
+ module.exports = JobNotificationService;
@@ -9,6 +9,8 @@ const localazyDownloadService = require("./localazy-download-service");
9
9
  const localazyPubApiService = require("./localazy-pubapi-service");
10
10
  const strapiI18nService = require("./strapi-i18n-service");
11
11
  const strapiLocalazyI18nService = require("./strapi-localazy-i18n-service");
12
+ const localazyTransferUploadService = require("./localazy-transfer-upload-service");
13
+ const localazyTransferDownloadService = require("./localazy-transfer-download-service");
12
14
 
13
15
  module.exports = {
14
16
  localazyUserService,
@@ -20,4 +22,6 @@ module.exports = {
20
22
  localazyPubApiService,
21
23
  strapiI18nService,
22
24
  strapiLocalazyI18nService,
25
+ localazyTransferUploadService,
26
+ localazyTransferDownloadService,
23
27
  };