@localazy/strapi-plugin 0.1.12 → 0.1.13-rc.1

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 (32) hide show
  1. package/.eslintrc +6 -3
  2. package/.github/workflows/.build.yml +1 -1
  3. package/admin/src/index.js +26 -1
  4. package/admin/src/modules/@common/components/LanguagesSelector/index.js +69 -0
  5. package/admin/src/modules/@common/locale/en.js +1 -0
  6. package/admin/src/modules/localazy-download/locale/en.js +3 -0
  7. package/admin/src/modules/plugin-settings/components/ContentTransferSetup/index.js +1 -2
  8. package/admin/src/modules/plugin-settings/components/GlobalSettings/index.js +269 -0
  9. package/admin/src/modules/plugin-settings/locale/en.js +38 -3
  10. package/admin/src/modules/plugin-settings/services/strapi-users-service.js +26 -0
  11. package/admin/src/pages/Download/index.js +49 -2
  12. package/admin/src/pages/PluginSettings/index.js +27 -11
  13. package/admin/src/utils/createAxiosInstance.js +2 -0
  14. package/package.json +24 -25
  15. package/server/bootstrap.js +50 -9
  16. package/server/controllers/localazy-transfer-controller.js +42 -4
  17. package/server/controllers/strapi-controller.js +3 -0
  18. package/server/lifecycles/deep-populate-hook.js +11 -0
  19. package/server/lifecycles/deprecate-event-entry-in-localazy-hook.js +69 -0
  20. package/server/lifecycles/upload-event-entry-to-localazy-hook.js +31 -0
  21. package/server/middlewares/index.js +5 -1
  22. package/server/middlewares/verify-webhook.js +77 -0
  23. package/server/routes/index.js +2 -0
  24. package/server/routes/localazy-public-transfer.js +20 -0
  25. package/server/routes/strapi.js +8 -0
  26. package/server/services/helpers/plugin-settings-service-helper.js +52 -0
  27. package/server/services/localazy-pubapi-service.js +6 -0
  28. package/server/services/plugin-settings-service.js +7 -1
  29. package/server/services/strapi-i18n-service.js +1 -1
  30. package/server/utils/get-picked-flatten-keys-for-hook-entry.js +270 -0
  31. package/server/utils/request-initiator-helper.js +36 -0
  32. package/admin/src/.eslintrc +0 -3
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+
3
+ const { isoStrapiToLocalazy } = require("./iso-locales-utils");
4
+ const config = require("../config").default;
5
+ const omitDeep = require("./omit-deep");
6
+ const flattenObject = require("./flatten-object");
7
+ const pickEntries = require("./pick-entries");
8
+ const {
9
+ findSetupModelByCollectionName,
10
+ getPickPaths
11
+ } = require("./transfer-setup-utils");
12
+ const PluginSettingsServiceHelper = require('../services/helpers/plugin-settings-service-helper');
13
+
14
+ /**
15
+ *
16
+ * @param {*} contentTransferSetup
17
+ * @param {*} eventModel
18
+ * @returns `undefined` if such model is not model eligible for transfer or `boolean` setup
19
+ */
20
+ const shouldProcessEntry = (contentTransferSetup, eventModel) => {
21
+ let key;
22
+ const modelContentTransferSetup = contentTransferSetup.setup
23
+ .find((model) => {
24
+ key = Object.keys(model)[0];
25
+ return key === eventModel;
26
+ });
27
+
28
+ // might be undefined as it's called on any Content-Type
29
+ if (!modelContentTransferSetup) {
30
+ return undefined;
31
+ }
32
+ return !!modelContentTransferSetup[key].__model__;
33
+ }
34
+
35
+ const getEventEntries = async (event) => {
36
+ switch (event.action) {
37
+ case 'afterCreate':
38
+ case 'afterUpdate': {
39
+ const entry = [await strapi.entityService.findOne(
40
+ event.model.uid,
41
+ event.result.id,
42
+ {
43
+ populate: "deep",
44
+ })];
45
+ return entry;
46
+ }
47
+ case "beforeDelete": {
48
+ const entry = [await strapi.entityService.findOne(
49
+ event.model.uid,
50
+ event.params.where.id,
51
+ {
52
+ populate: "deep",
53
+ })];
54
+ return entry;
55
+ }
56
+ case "beforeDeleteMany": {
57
+ /**
58
+ * Strapi sets the locale to the default one, so passing the ids won't help (won't fetch the entries in the correct locale)
59
+ * And therefore because of Strapi being Strapi, we need to get one id from the filter and fetch it's language
60
+ * And then pass the locale parameter to the findMany method
61
+ */
62
+ // const entry = await strapi.entityService.findOne(
63
+ // event.model.uid,
64
+ // event.params.where.$and[0].id.$in[0],
65
+ // {
66
+ // populate: "deep",
67
+ // });
68
+ // const locale = entry.locale;
69
+ // const entries = await strapi.entityService.findMany(
70
+ // event.model.uid,
71
+ // {
72
+ // filters: {
73
+ // id: event.params.where.$and[0].id.$in,
74
+ // },
75
+ // locale,
76
+ // populate: "deep",
77
+ // });
78
+
79
+ /**
80
+ * If deletion of other than default locale is requested, `entries` will be an empty array
81
+ * If deletion of default locale is requested, `entries` will contain requested entries, and we can proceed with the deprecation process
82
+ */
83
+ const entries = await strapi.entityService.findMany(
84
+ event.model.uid,
85
+ {
86
+ filters: event.params.where,
87
+ populate: "deep",
88
+ });
89
+
90
+ if (entries.length === 0) {
91
+ strapi.log.info(`Localazy Plugin: No entries in source language for ${event.action} found; the operation won't proceed`);
92
+ }
93
+ return entries;
94
+ }
95
+ default: {
96
+ throw new Error("Unhandled event action");
97
+ }
98
+ }
99
+ }
100
+
101
+ const shouldSkipAction = async (event) => {
102
+ const pluginSettingsServiceHelper = new PluginSettingsServiceHelper(strapi);
103
+ await pluginSettingsServiceHelper.setup();
104
+
105
+ switch (event.action) {
106
+ case 'afterCreate': {
107
+ if (!pluginSettingsServiceHelper.shouldAllowAutomatedCreatedTrigger()) {
108
+ strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because automated created trigger is disabled.`)
109
+ return true;
110
+ }
111
+ return false;
112
+ }
113
+ case 'afterUpdate': {
114
+ if (!pluginSettingsServiceHelper.shouldAllowAutomatedUpdatedTrigger()) {
115
+ strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because automated update trigger is disabled.`)
116
+ return true;
117
+ }
118
+ return false;
119
+ }
120
+ case "beforeDelete":
121
+ case "beforeDeleteMany": {
122
+ if (!pluginSettingsServiceHelper.shouldAllowDeprecateOnDeletion()) {
123
+ strapi.log.info(`Localazy Plugin: Skipping ${event.action} hook because deprecate on deletion is disabled.`)
124
+ return true;
125
+ }
126
+ return false;
127
+ }
128
+ default: {
129
+ throw new Error("Unhandled event action");
130
+ }
131
+ }
132
+ }
133
+
134
+ module.exports = async (event) => {
135
+ // Docs: https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#hook-event-object
136
+ // this should respect the content transfer setup
137
+ // there read it and compare with the model property
138
+ const LocalazyUserService = strapi
139
+ .plugin("localazy")
140
+ .service("localazyUserService");
141
+ const LocalazyPubApiService = strapi
142
+ .plugin("localazy")
143
+ .service("localazyPubApiService");
144
+ const StrapiI18nService = strapi
145
+ .plugin("localazy")
146
+ .service("strapiI18nService");
147
+ const contentTransferSetup = await strapi
148
+ .plugin("localazy")
149
+ .service("pluginSettingsService")
150
+ .getContentTransferSetup();
151
+
152
+ const eventAction = event.action;
153
+ strapi.log.info(`${eventAction} triggered`);
154
+
155
+ // Content Transfer Setup not available yet; break execution
156
+ if (!contentTransferSetup.has_setup) {
157
+ strapi.log.warn("Localazy Plugin: Content Transfer Setup is not set up; the operation won't proceed");
158
+ return;
159
+ }
160
+
161
+ // entry model not supposed to be processed; break execution
162
+ const eventModel = event.model.tableName;
163
+ const shouldProcess = shouldProcessEntry(contentTransferSetup, eventModel);
164
+ if (typeof shouldProcess === 'undefined') {
165
+ return;
166
+ }
167
+ if (!shouldProcess) {
168
+ strapi.log.info("Localazy Plugin: The entry collection transfer is disabled");
169
+ return;
170
+ }
171
+
172
+ // if project not connected; break execution
173
+ const user = await LocalazyUserService.getUser();
174
+ const { accessToken } = user;
175
+ if (!accessToken) {
176
+ strapi.log.error("Localazy Plugin: Localazy user is not logged in; the operation won't proceed");
177
+ return;
178
+ }
179
+
180
+ if (await shouldSkipAction(event)) {
181
+ strapi.log.info("Localazy Plugin: Automated action skipped");
182
+ return;
183
+ }
184
+
185
+ const project = await LocalazyPubApiService.getProject(
186
+ user.project.id,
187
+ false,
188
+ true
189
+ );
190
+ const projectLanguages = project.languages;
191
+ const projectSourceLanguageId = project.sourceLanguage;
192
+ const sourceLanguage = projectLanguages.find(
193
+ (language) => language.id === projectSourceLanguageId
194
+ );
195
+ const projectSourceLanguageCode = sourceLanguage.code;
196
+
197
+ // Strapi default locale not compatible with project source language
198
+ const strapiLocales = await StrapiI18nService.getLocales();
199
+ const defaultLocale = strapiLocales.find((locale) => locale.isDefault);
200
+
201
+ const strapiDefaultLocale = defaultLocale
202
+ ? isoStrapiToLocalazy(defaultLocale.code)
203
+ : config.LOCALAZY_DEFAULT_LOCALE;
204
+ if (strapiDefaultLocale !== projectSourceLanguageCode) {
205
+ strapi.log.warn("Localazy Plugin: Source locales do not match; the operation won't proceed");
206
+ return;
207
+ }
208
+
209
+ /**
210
+ * Prepare the data structure and upload it to Localazy
211
+ */
212
+ const modelUid = event.model.uid;
213
+ const entries = await getEventEntries(event);
214
+
215
+ if (entries.length === 0) {
216
+ return;
217
+ }
218
+
219
+ // ? TODO: will always be valid?
220
+ const eventEntryLocale = entries[0].locale;
221
+
222
+ // event entry not in source language; break execution
223
+ if (isoStrapiToLocalazy(eventEntryLocale) !== projectSourceLanguageCode) {
224
+ strapi.log.info("Localazy Plugin: Entry language is not in Localazy source language; the operation won't proceed");
225
+ return;
226
+ }
227
+
228
+ const reducedEntries = omitDeep(entries, [
229
+ // "__component",
230
+ "locale",
231
+ "localizations",
232
+ "createdAt",
233
+ "createdBy",
234
+ "updatedAt",
235
+ "updatedBy",
236
+ "publishedAt",
237
+ ]);
238
+
239
+ const transferSetupModel = findSetupModelByCollectionName(
240
+ contentTransferSetup.setup,
241
+ eventModel
242
+ );
243
+ const currentTransferSetupModel = transferSetupModel[eventModel];
244
+ const pickPaths = getPickPaths(currentTransferSetupModel);
245
+ if (!pickPaths.length) {
246
+ strapi.log.warn(`No fields for collection ${eventModel} transfer are enabled.`);
247
+ return;
248
+ }
249
+ const pickPathsWithUid = pickPaths.map(
250
+ (pickPath) => `${modelUid}.${pickPath}`
251
+ );
252
+
253
+ // flatten Strapi content
254
+ let flatten = {};
255
+ for (const reducedEntry of reducedEntries) {
256
+ flatten = {
257
+ ...flatten,
258
+ ...flattenObject({
259
+ [modelUid]: reducedEntry,
260
+ }),
261
+ };
262
+ }
263
+ // get only enabled fields; "__component" will be filtered out inside of the function
264
+ const pickedFlatten = pickEntries(flatten, pickPathsWithUid, currentTransferSetupModel);
265
+
266
+ return {
267
+ pickedFlatten,
268
+ eventEntryLocale,
269
+ };
270
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ class RequestInitiatorHelper {
4
+ constructor(strapi) {
5
+ this.strapi = strapi;
6
+ this.ctx = strapi.requestContext.get();
7
+ }
8
+
9
+ isInitiatedByLocalazyWebhook() {
10
+ if (typeof this.ctx !== 'undefined') {
11
+ const ctxHeaders = this.ctx.headers;
12
+ const xLocalazyHmac = ctxHeaders["x-localazy-hmac"];
13
+ const xLocalazyTimestamp = ctxHeaders["x-localazy-timestamp"];
14
+ if (!!xLocalazyHmac && !!xLocalazyTimestamp) {
15
+ return true;
16
+ }
17
+ }
18
+ return false;
19
+ }
20
+
21
+ isInitiatedByLocalazyPluginUI() {
22
+ if (typeof this.ctx !== 'undefined') {
23
+ const ctxHeaders = this.ctx.headers;
24
+ const xLocalazyInitiatedBy = ctxHeaders["x-localazy-initiated-by"];
25
+ if (!xLocalazyInitiatedBy) {
26
+ return false;
27
+ }
28
+ return xLocalazyInitiatedBy === "strapi-plugin-localazy";
29
+ }
30
+ return false;
31
+ }
32
+
33
+
34
+ }
35
+
36
+ module.exports = RequestInitiatorHelper;
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "@strapi-community/admin"
3
- }