@localazy/strapi-plugin 0.1.10 → 0.1.11-rc.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 (42) hide show
  1. package/.eslintrc +6 -2
  2. package/admin/src/modules/@common/models/supported-content-type-fields.js +8 -0
  3. package/admin/src/modules/plugin-settings/components/Tree/index.js +2 -0
  4. package/admin/src/modules/plugin-settings/utils/get-filtered-models-schemas.js +4 -4
  5. package/admin/src/modules/plugin-settings/utils/get-models-tree.js +3 -1
  6. package/package.json +1 -1
  7. package/server/bootstrap.js +15 -1
  8. package/server/controllers/localazy-transfer-controller.js +41 -46
  9. package/server/functions/should-set-downloaded-property.js +16 -0
  10. package/server/models/supported-content-type-fields.js +8 -0
  11. package/server/services/index.js +2 -0
  12. package/server/services/strapi-i18n-service.js +4 -3
  13. package/server/services/strapi-localazy-i18n-service.js +104 -0
  14. package/server/tests/fixtures/parsed-localazy-entry-to-create-entry-cookbook.js +5761 -0
  15. package/server/tests/fixtures/parsed-localazy-entry-to-create-entry-restaurant-simple-menu.js +536 -0
  16. package/server/tests/fixtures/parsed-localazy-entry-to-update-entry.js +35157 -0
  17. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-cookbook-empty-recipes.js +202 -0
  18. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-cookbook.js +2700 -0
  19. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-restaurant-simple-flat-simple.js +47 -0
  20. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-restaurant-simple-flat-with-media.js +169 -0
  21. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-restaurant-simple-menu-empty.js +3003 -0
  22. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-restaurant-simple-menu-with-media.js +2057 -0
  23. package/server/tests/fixtures/populate-create-update-entry-with-base-entry-sample-json.js +46 -0
  24. package/server/tests/fixtures/{all-models-with-multimedia.js → strapi-plugin-all-models.js} +717 -540
  25. package/server/utils/__tests__/get-models-tree.test.js +1 -1
  26. package/server/utils/__tests__/parsed-localazy-entry-to-create-entry.test.js +90 -93
  27. package/server/utils/__tests__/parsed-localazy-entry-to-update-entry.test.js +417 -101
  28. package/server/utils/__tests__/populate-create-update-entry-with-base-entry.test.js +852 -0
  29. package/server/utils/get-full-populate-object.js +56 -0
  30. package/server/utils/get-models-tree.js +3 -1
  31. package/server/utils/model-utils.js +38 -0
  32. package/server/utils/parsed-localazy-entry-to-create-entry.js +1 -18
  33. package/server/utils/parsed-localazy-entry-to-update-entry.js +38 -3
  34. package/server/utils/populate-create-update-entry-with-base-entry.js +222 -0
  35. package/server/utils/transfer-setup-utils.js +14 -0
  36. package/server/tests/fixtures/restaurant-model.js +0 -230
  37. package/server/tests/fixtures/restaurants-entries.js +0 -152
  38. package/server/tests/fixtures/strapi-book-with-multimedia-components-cs.js +0 -31
  39. package/server/tests/fixtures/stringified-book-cs.js +0 -3
  40. package/server/tests/fixtures/stringified-book-with-multimedia-components-cs.js +0 -3
  41. package/server/tests/fixtures/stringified-restaurant-cs.js +0 -3
  42. package/server/tests/fixtures/stringified-restaurant-de.js +0 -5
@@ -0,0 +1,56 @@
1
+ // is taken from
2
+ // https://github.com/Barelydead/strapi-plugin-populate-deep
3
+ // based on the issue
4
+ // https://github.com/strapi/strapi/issues/11836
5
+
6
+ const isEmpty = require("lodash/isEmpty");
7
+ const merge = require("lodash/merge");
8
+
9
+ const getModelPopulationAttributes = (model) => {
10
+ if (model.uid === "plugin::upload.file") {
11
+ const { related, ...attributes } = model.attributes;
12
+ return attributes;
13
+ }
14
+
15
+ return model.attributes;
16
+ };
17
+
18
+ const getFullPopulateObject = (modelUid, maxDepth = 20) => {
19
+ if (maxDepth <= 1) {
20
+ return true;
21
+ }
22
+ if (modelUid === "admin::user") {
23
+ return undefined;
24
+ }
25
+
26
+ const populate = {};
27
+ const model = strapi.getModel(modelUid);
28
+ for (const [key, value] of Object.entries(
29
+ getModelPopulationAttributes(model)
30
+ )) {
31
+ if (value) {
32
+ if (value.type === "component") {
33
+ populate[key] = getFullPopulateObject(value.component, maxDepth - 1);
34
+ } else if (value.type === "dynamiczone") {
35
+ const dynamicPopulate = value.components.reduce((prev, cur) => {
36
+ const curPopulate = getFullPopulateObject(cur, maxDepth - 1);
37
+ return curPopulate === true ? prev : merge(prev, curPopulate);
38
+ }, {});
39
+ populate[key] = isEmpty(dynamicPopulate) ? true : dynamicPopulate;
40
+ } else if (value.type === "relation") {
41
+ const relationPopulate = getFullPopulateObject(
42
+ value.target,
43
+ maxDepth - 1
44
+ );
45
+ if (relationPopulate) {
46
+ populate[key] = relationPopulate;
47
+ }
48
+ } else if (value.type === "media") {
49
+ populate[key] = true;
50
+ }
51
+ }
52
+ }
53
+ return isEmpty(populate) ? true : { populate };
54
+ };
55
+
56
+ module.exports = getFullPopulateObject;
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const SUPPORTED_CONTENT_TYPE_FIELDS = require("../models/supported-content-type-fields");
4
+
3
5
  const getModelTree = (attributes, models) => {
4
6
  const currentModelTree = {};
5
7
  for (const attribute in attributes) {
@@ -12,7 +14,7 @@ const getModelTree = (attributes, models) => {
12
14
  ...getModelTree(nestedModel.__schema__.attributes, models),
13
15
  };
14
16
  } else if (
15
- ["string", "text", "richtext"].includes(attributes[attribute].type)
17
+ SUPPORTED_CONTENT_TYPE_FIELDS.includes(attributes[attribute].type)
16
18
  ) {
17
19
  currentModelTree[attribute] = false;
18
20
  } else {
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ const isEmpty = require("lodash/isEmpty");
4
+
5
+ const getAttribute = (model, attribute) => {
6
+ if (isEmpty(model)) {
7
+ return undefined;
8
+ }
9
+
10
+ const attributeObj = model.attributes[attribute];
11
+ return attributeObj;
12
+ };
13
+
14
+ const isComponent = (attributeObj) => {
15
+ return attributeObj.type === "component";
16
+ };
17
+
18
+ const isRepeatable = (attributeObj) => {
19
+ return isComponent(attributeObj) && !!attributeObj.repeatable;
20
+ };
21
+
22
+ const isRelation = (attributeObj) => {
23
+ return attributeObj.type === "relation"
24
+ && attributeObj.target !== "plugin::upload.file";
25
+ };
26
+
27
+ const findModel = (models, uid) => {
28
+ const model = models.find((model) => model.uid === uid);
29
+ return model;
30
+ };
31
+
32
+ module.exports = {
33
+ getAttribute,
34
+ isComponent,
35
+ isRepeatable,
36
+ isRelation,
37
+ findModel,
38
+ };
@@ -3,24 +3,7 @@
3
3
  const set = require("lodash/set");
4
4
  const get = require("lodash/get");
5
5
  const resetArrayKeysDeep = require("./reset-array-keys-deep");
6
-
7
- const getAttribute = (model, attribute) => {
8
- const attributeObj = model.attributes[attribute];
9
- return attributeObj;
10
- };
11
-
12
- const isComponent = (attributeObj) => {
13
- return attributeObj.type === "component";
14
- };
15
-
16
- const isRepeatable = (attributeObj) => {
17
- return isComponent(attributeObj) && !!attributeObj.repeatable;
18
- };
19
-
20
- const findModel = (models, uid) => {
21
- const model = models.find((model) => model.uid === uid);
22
- return model;
23
- };
6
+ const { getAttribute, isComponent, isRepeatable, findModel } = require("./model-utils");
24
7
 
25
8
  const parsedLocalazyEntryToCreateEntry = (
26
9
  models,
@@ -3,13 +3,16 @@
3
3
  const merge = require("lodash/merge");
4
4
  const parsedLocalazyEntryToCreateEntry = require("./parsed-localazy-entry-to-create-entry");
5
5
  const omitDeep = require("./omit-deep");
6
+ const populateCreateUpdateEntryWithBaseEntry = require("./populate-create-update-entry-with-base-entry");
6
7
 
7
- const parsedLocalazyEntryToUpdateEntry = (
8
+ const parsedLocalazyEntryToUpdateEntry = async (
8
9
  allModels,
9
10
  localazyEntry,
11
+ fullyPopulatedLocalizedEntry,
10
12
  currentEntry,
11
13
  baseEntry,
12
- uid
14
+ uid,
15
+ locale
13
16
  ) => {
14
17
  const createEntry = parsedLocalazyEntryToCreateEntry(
15
18
  allModels,
@@ -25,11 +28,43 @@ const parsedLocalazyEntryToUpdateEntry = (
25
28
  "locale",
26
29
  "localizations",
27
30
  "createdAt",
31
+ "createdBy",
28
32
  "updatedAt",
33
+ "updatedBy",
29
34
  "publishedAt",
30
35
  ]);
31
36
 
32
- return updateEntry;
37
+ const filteredBaseEntry = omitDeep(baseEntry, [
38
+ // "__component", // do not omit the __component
39
+ "locale",
40
+ "localizations",
41
+ "createdAt",
42
+ "createdBy",
43
+ "updatedAt",
44
+ "updatedBy",
45
+ "publishedAt",
46
+ ]);
47
+ const filteredFullyPopulatedLocalizedEntry = omitDeep(fullyPopulatedLocalizedEntry, [
48
+ // "__component", // do not omit the __component
49
+ "locale",
50
+ "localizations",
51
+ "createdAt",
52
+ "createdBy",
53
+ "updatedAt",
54
+ "updatedBy",
55
+ "publishedAt",
56
+ ]);
57
+ const populatedEntry = await populateCreateUpdateEntryWithBaseEntry(
58
+ allModels,
59
+ updateEntry,
60
+ filteredBaseEntry,
61
+ filteredFullyPopulatedLocalizedEntry,
62
+ uid,
63
+ locale
64
+ );
65
+
66
+ // return updateEntry;
67
+ return populatedEntry;
33
68
  };
34
69
 
35
70
  module.exports = parsedLocalazyEntryToUpdateEntry;
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+
3
+ const cloneDeep = require("lodash/cloneDeep");
4
+ const set = require("lodash/set");
5
+ const get = require("lodash/get");
6
+ const isEmpty = require("lodash/isEmpty");
7
+ const {
8
+ getAttribute,
9
+ isComponent,
10
+ isRepeatable,
11
+ isRelation,
12
+ findModel
13
+ } = require("./model-utils");
14
+
15
+ const doesExistInPopulatedLocalizedEntry = (val) => {
16
+ if (Array.isArray(val)) {
17
+ // is array
18
+ return !!val.length;
19
+ }
20
+
21
+ if (typeof val === "object") {
22
+ // is object
23
+ return !isEmpty(val);
24
+ }
25
+
26
+ if (val !== Object(val)) {
27
+ // is primitive
28
+ return !!val;
29
+ }
30
+ }
31
+
32
+ const getNewPrefix = (objectKey, prefix) => {
33
+ return prefix ? `${prefix}.${objectKey}` : objectKey;
34
+ };
35
+
36
+ const populateCreateUpdateEntryWithBaseEntry = async (
37
+ models,
38
+ createUpdateEntry,
39
+ baseEntry,
40
+ populatedLocalizedEntry,
41
+ uid,
42
+ locale
43
+ ) => {
44
+ const populatedEntry = cloneDeep(createUpdateEntry);
45
+
46
+ const populateEntry = async (
47
+ partialBaseEntry,
48
+ model,
49
+ objectKey = "",
50
+ prefix = "",
51
+ component = "",
52
+ isRepeatableComponent = false
53
+ ) => {
54
+ if (Array.isArray(partialBaseEntry)) {
55
+ // is array
56
+ const attribute = getAttribute(model, objectKey);
57
+
58
+ if (typeof attribute !== 'undefined') {
59
+ const newPrefix = getNewPrefix(objectKey, prefix);
60
+ const inCreateUpdateEntry = get(createUpdateEntry, newPrefix);
61
+ // TODO: decide whether add || inCreateUpdateEntry === null to the condition
62
+ const isMissingInCreateUpdateEntry = !!(typeof inCreateUpdateEntry === 'undefined');
63
+ if (isMissingInCreateUpdateEntry) {
64
+ const populatedLocalizedEntryVal = get(populatedLocalizedEntry, newPrefix);
65
+ if (doesExistInPopulatedLocalizedEntry(populatedLocalizedEntryVal)) {
66
+ set(populatedEntry, newPrefix, populatedLocalizedEntryVal);
67
+ } else {
68
+ set(populatedEntry, newPrefix, partialBaseEntry);
69
+ }
70
+ } else {
71
+ for (const [objectKey, value] of Object.entries(entry)) {
72
+ await populateEntry(
73
+ value,
74
+ model,
75
+ objectKey,
76
+ newPrefix
77
+ );
78
+ }
79
+ }
80
+ } else if (isRepeatableComponent) {
81
+ Object.entries(partialBaseEntry).forEach(async ([partialBaseEntryItemIndex, partialBaseEntryItem]) => {
82
+ const newPrefix = `${prefix}.${partialBaseEntryItemIndex}`;
83
+ await populateEntry(
84
+ partialBaseEntryItem,
85
+ model,
86
+ partialBaseEntryItemIndex,
87
+ newPrefix,
88
+ );
89
+ });
90
+ }
91
+
92
+ } else if (typeof partialBaseEntry === "object") {
93
+ // is object
94
+ if (partialBaseEntry === null) {
95
+ partialBaseEntry = {};
96
+ }
97
+
98
+ for (const [objectKey, value] of Object.entries(partialBaseEntry)) {
99
+ const attribute = getAttribute(model, objectKey);
100
+ if (typeof attribute === 'undefined') {
101
+ // skip...
102
+ continue;
103
+ } else if (isComponent(attribute)) {
104
+ // is component
105
+ const component = attribute.component;
106
+ const componentModel = findModel(models, component);
107
+
108
+ const newPrefix = getNewPrefix(objectKey, prefix);
109
+ const inCreateUpdateEntry = get(createUpdateEntry, newPrefix);
110
+ let isMissingInCreateUpdateEntry;
111
+ if (isRepeatable(attribute)) {
112
+ isMissingInCreateUpdateEntry = !!((typeof inCreateUpdateEntry === 'undefined') || (inCreateUpdateEntry && !inCreateUpdateEntry.length));
113
+ } else {
114
+ isMissingInCreateUpdateEntry = !!(typeof inCreateUpdateEntry === 'undefined' || inCreateUpdateEntry === null);
115
+ }
116
+
117
+ if (isMissingInCreateUpdateEntry) {
118
+ const populatedLocalizedEntryVal = get(populatedLocalizedEntry, newPrefix);
119
+ let valueToSet = value;
120
+ if (doesExistInPopulatedLocalizedEntry(populatedLocalizedEntryVal)) {
121
+ valueToSet = populatedLocalizedEntryVal;
122
+ }
123
+ if (isRepeatable(attribute)) {
124
+ valueToSet = value.map((item) => {
125
+ delete item.id;
126
+ return {
127
+ __component: component,
128
+ ...item,
129
+ };
130
+ });
131
+ } else {
132
+ if (!isEmpty(valueToSet)) {
133
+ delete valueToSet.id;
134
+ valueToSet.__component = component;
135
+ }
136
+ }
137
+ set(populatedEntry, newPrefix, valueToSet);
138
+ }
139
+ await populateEntry(
140
+ value,
141
+ componentModel,
142
+ objectKey,
143
+ newPrefix,
144
+ component,
145
+ isRepeatable(attribute),
146
+ );
147
+ } else if (isRelation(attribute)) {
148
+ const newPrefix = getNewPrefix(objectKey, prefix);
149
+ const populatedLocalizedEntryVal = get(populatedLocalizedEntry, newPrefix);
150
+ if (doesExistInPopulatedLocalizedEntry(populatedLocalizedEntryVal)) {
151
+ // skip...
152
+ continue;
153
+ }
154
+
155
+ const isArray = Array.isArray(value);
156
+ let arraiedValue = value;
157
+ if (!isArray) {
158
+ arraiedValue = [value];
159
+ }
160
+ for (const [index, item] of Object.entries(arraiedValue)) {
161
+ if (!isEmpty(item)) {
162
+ const entityId = item.id;
163
+ if (!!entityId) {
164
+ const entryWithLocalizations = await strapi.entityService.findOne(attribute.target, entityId, {
165
+ populate: ['localizations'],
166
+ });
167
+
168
+ const localizedEntry = entryWithLocalizations.localizations.find((item) => item.locale === locale);
169
+ if (!!localizedEntry && localizedEntry.id) {
170
+ if (!isArray) {
171
+ set(populatedEntry, newPrefix, { id: localizedEntry.id });
172
+ } else {
173
+ set(populatedEntry, `${newPrefix}.${index}`, { id: localizedEntry.id });
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ } else {
180
+ const newPrefix = getNewPrefix(objectKey, prefix);
181
+ const inCreateUpdateEntry = get(createUpdateEntry, newPrefix);
182
+ const isMissingInCreateUpdateEntry = !!(typeof inCreateUpdateEntry === 'undefined' || inCreateUpdateEntry === null);
183
+ if (isMissingInCreateUpdateEntry) {
184
+ const populatedLocalizedEntryVal = get(populatedLocalizedEntry, newPrefix);
185
+ if (doesExistInPopulatedLocalizedEntry(populatedLocalizedEntryVal)) {
186
+ set(populatedEntry, newPrefix, populatedLocalizedEntryVal);
187
+ } else {
188
+ set(populatedEntry, newPrefix, value);
189
+ }
190
+ } else {
191
+ await populateEntry(
192
+ value,
193
+ model,
194
+ objectKey,
195
+ newPrefix
196
+ );
197
+ }
198
+ }
199
+ }
200
+ } else if (partialBaseEntry !== Object(partialBaseEntry)) {
201
+ // is primitive/literal
202
+ const attribute = getAttribute(model, objectKey);
203
+ if (typeof attribute !== 'undefined') {
204
+ const inCreateUpdateEntry = get(createUpdateEntry, prefix);
205
+ const isMissingInCreateUpdateEntry = !!(typeof inCreateUpdateEntry === 'undefined' || inCreateUpdateEntry === null);
206
+ if (isMissingInCreateUpdateEntry) {
207
+ set(populatedEntry, prefix, partialBaseEntry);
208
+ }
209
+ }
210
+ } else {
211
+ // ? TODO: something unknown
212
+ console.info(`Unknown type processed: ${partialBaseEntry}`);
213
+ }
214
+ }
215
+
216
+ const model = findModel(models, uid);
217
+ await populateEntry(baseEntry, model);
218
+
219
+ return populatedEntry;
220
+ };
221
+
222
+ module.exports = populateCreateUpdateEntryWithBaseEntry;
@@ -5,6 +5,19 @@ const findSetupModelByCollectionName = (models, collectionName) => {
5
5
  return models.find((model) => Object.keys(model)[0] === collectionName);
6
6
  };
7
7
 
8
+ const findSetupModelByCollectionUid = (
9
+ contentTransferSetup,
10
+ strapiContentTypesModels,
11
+ collectionUid
12
+ ) => {
13
+ const currentModel = strapiContentTypesModels.find(
14
+ (model) => model.uid === collectionUid
15
+ );
16
+ const modelContentTransferSetup = contentTransferSetup.setup
17
+ .find((model) => Object.keys(model)[0] === currentModel.collectionName);
18
+ return modelContentTransferSetup[currentModel.collectionName];
19
+ }
20
+
8
21
  const getCollectionsNames = (setup) => {
9
22
  return getMainKeysOfNestedArrayObjects(setup);
10
23
  };
@@ -33,6 +46,7 @@ const getPickPaths = (transferSetupModelProps) => {
33
46
 
34
47
  module.exports = {
35
48
  findSetupModelByCollectionName,
49
+ findSetupModelByCollectionUid,
36
50
  getCollectionsNames,
37
51
  isCollectionTransferEnabled,
38
52
  getPickPaths,
@@ -1,230 +0,0 @@
1
- module.exports = {
2
- kind: "collectionType",
3
- collectionName: "restaurants",
4
- info: {
5
- singularName: "restaurant",
6
- pluralName: "restaurants",
7
- displayName: "Restaurant",
8
- description: "",
9
- },
10
- options: {
11
- draftAndPublish: true,
12
- },
13
- pluginOptions: {
14
- i18n: {
15
- localized: true,
16
- },
17
- },
18
- attributes: {
19
- title: {
20
- pluginOptions: {
21
- i18n: {
22
- localized: true,
23
- },
24
- },
25
- type: "string",
26
- required: true,
27
- },
28
- description: {
29
- pluginOptions: {
30
- i18n: {
31
- localized: true,
32
- },
33
- },
34
- type: "text",
35
- },
36
- books: {
37
- type: "relation",
38
- relation: "oneToMany",
39
- target: "api::book.book",
40
- },
41
- recipes: {
42
- type: "component",
43
- repeatable: true,
44
- pluginOptions: {
45
- i18n: {
46
- localized: true,
47
- },
48
- },
49
- component: "food.recipe",
50
- },
51
- descs: {
52
- type: "component",
53
- repeatable: false,
54
- pluginOptions: {
55
- i18n: {
56
- localized: true,
57
- },
58
- },
59
- component: "food.pub-component-1",
60
- },
61
- street: {
62
- pluginOptions: {
63
- i18n: {
64
- localized: true,
65
- },
66
- },
67
- type: "string",
68
- },
69
- city: {
70
- pluginOptions: {
71
- i18n: {
72
- localized: true,
73
- },
74
- },
75
- type: "string",
76
- },
77
- country: {
78
- pluginOptions: {
79
- i18n: {
80
- localized: true,
81
- },
82
- },
83
- type: "string",
84
- },
85
- createdAt: {
86
- type: "datetime",
87
- },
88
- updatedAt: {
89
- type: "datetime",
90
- },
91
- publishedAt: {
92
- type: "datetime",
93
- configurable: false,
94
- writable: true,
95
- visible: false,
96
- },
97
- createdBy: {
98
- type: "relation",
99
- relation: "oneToOne",
100
- target: "admin::user",
101
- configurable: false,
102
- writable: false,
103
- visible: false,
104
- useJoinTable: false,
105
- private: true,
106
- },
107
- updatedBy: {
108
- type: "relation",
109
- relation: "oneToOne",
110
- target: "admin::user",
111
- configurable: false,
112
- writable: false,
113
- visible: false,
114
- useJoinTable: false,
115
- private: true,
116
- },
117
- localizations: {
118
- writable: true,
119
- private: false,
120
- configurable: false,
121
- visible: false,
122
- type: "relation",
123
- relation: "oneToMany",
124
- target: "api::restaurant.restaurant",
125
- },
126
- locale: {
127
- writable: true,
128
- private: false,
129
- configurable: false,
130
- visible: false,
131
- type: "string",
132
- },
133
- },
134
- __schema__: {
135
- collectionName: "restaurants",
136
- info: {
137
- singularName: "restaurant",
138
- pluralName: "restaurants",
139
- displayName: "Restaurant",
140
- description: "",
141
- },
142
- options: {
143
- draftAndPublish: true,
144
- },
145
- pluginOptions: {
146
- i18n: {
147
- localized: true,
148
- },
149
- },
150
- attributes: {
151
- title: {
152
- pluginOptions: {
153
- i18n: {
154
- localized: true,
155
- },
156
- },
157
- type: "string",
158
- required: true,
159
- },
160
- description: {
161
- pluginOptions: {
162
- i18n: {
163
- localized: true,
164
- },
165
- },
166
- type: "text",
167
- },
168
- books: {
169
- type: "relation",
170
- relation: "oneToMany",
171
- target: "api::book.book",
172
- },
173
- recipes: {
174
- type: "component",
175
- repeatable: true,
176
- pluginOptions: {
177
- i18n: {
178
- localized: true,
179
- },
180
- },
181
- component: "food.recipe",
182
- },
183
- descs: {
184
- type: "component",
185
- repeatable: false,
186
- pluginOptions: {
187
- i18n: {
188
- localized: true,
189
- },
190
- },
191
- component: "food.pub-component-1",
192
- },
193
- street: {
194
- pluginOptions: {
195
- i18n: {
196
- localized: true,
197
- },
198
- },
199
- type: "string",
200
- },
201
- city: {
202
- pluginOptions: {
203
- i18n: {
204
- localized: true,
205
- },
206
- },
207
- type: "string",
208
- },
209
- country: {
210
- pluginOptions: {
211
- i18n: {
212
- localized: true,
213
- },
214
- },
215
- type: "string",
216
- },
217
- },
218
- kind: "collectionType",
219
- },
220
- modelType: "contentType",
221
- modelName: "restaurant",
222
- connection: "default",
223
- uid: "api::restaurant.restaurant",
224
- apiName: "restaurant",
225
- globalId: "Restaurant",
226
- actions: {},
227
- lifecycles: {},
228
- singularName: "restaurant",
229
- tableName: "restaurants",
230
- };