@strapi/content-type-builder 5.56.0 → 5.58.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 (178) hide show
  1. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js +117 -13
  2. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js.map +1 -1
  3. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs +118 -13
  4. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs.map +1 -1
  5. package/dist/admin/components/AIChat/providers/SchemaProvider.js +31 -19
  6. package/dist/admin/components/AIChat/providers/SchemaProvider.js.map +1 -1
  7. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs +31 -19
  8. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs.map +1 -1
  9. package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
  10. package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
  11. package/dist/admin/components/DataManager/DataManagerProvider.js +166 -6
  12. package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
  13. package/dist/admin/components/DataManager/DataManagerProvider.mjs +165 -7
  14. package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
  15. package/dist/admin/components/DataManager/RenameMigrationModal.js +285 -0
  16. package/dist/admin/components/DataManager/RenameMigrationModal.js.map +1 -0
  17. package/dist/admin/components/DataManager/RenameMigrationModal.mjs +257 -0
  18. package/dist/admin/components/DataManager/RenameMigrationModal.mjs.map +1 -0
  19. package/dist/admin/components/DataManager/reducer.js +129 -2
  20. package/dist/admin/components/DataManager/reducer.js.map +1 -1
  21. package/dist/admin/components/DataManager/reducer.mjs +128 -2
  22. package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
  23. package/dist/admin/components/DataManager/utils/cleanData.js +8 -1
  24. package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
  25. package/dist/admin/components/DataManager/utils/cleanData.mjs +8 -1
  26. package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
  27. package/dist/admin/components/DataManager/utils/groupRenameChains.js +86 -0
  28. package/dist/admin/components/DataManager/utils/groupRenameChains.js.map +1 -0
  29. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs +84 -0
  30. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs.map +1 -0
  31. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js +29 -0
  32. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js.map +1 -0
  33. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs +27 -0
  34. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs.map +1 -0
  35. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js +35 -0
  36. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js.map +1 -0
  37. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs +33 -0
  38. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs.map +1 -0
  39. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js +48 -0
  40. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js.map +1 -0
  41. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs +45 -0
  42. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs.map +1 -0
  43. package/dist/admin/components/FormModal/FormModal.js +178 -70
  44. package/dist/admin/components/FormModal/FormModal.js.map +1 -1
  45. package/dist/admin/components/FormModal/FormModal.mjs +179 -71
  46. package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
  47. package/dist/admin/components/FormModal/attributes/validation/common.js +6 -5
  48. package/dist/admin/components/FormModal/attributes/validation/common.js.map +1 -1
  49. package/dist/admin/components/FormModal/attributes/validation/common.mjs +1 -1
  50. package/dist/admin/components/FormModal/attributes/validation/common.mjs.map +1 -1
  51. package/dist/admin/components/FormModal/component/createComponentSchema.js +10 -7
  52. package/dist/admin/components/FormModal/component/createComponentSchema.js.map +1 -1
  53. package/dist/admin/components/FormModal/component/createComponentSchema.mjs +1 -1
  54. package/dist/admin/components/FormModal/component/createComponentSchema.mjs.map +1 -1
  55. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js +24 -21
  56. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js.map +1 -1
  57. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs +1 -1
  58. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs.map +1 -1
  59. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js +3 -2
  60. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js.map +1 -1
  61. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs +1 -1
  62. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs.map +1 -1
  63. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js +73 -0
  64. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js.map +1 -0
  65. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs +70 -0
  66. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs.map +1 -0
  67. package/dist/admin/src/components/AIChat/lib/transforms/schemas/toCTB.d.ts +4 -3
  68. package/dist/admin/src/components/AIChat/lib/types/schema.d.ts +6 -1
  69. package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +28 -2
  70. package/dist/admin/src/components/DataManager/RenameMigrationModal.d.ts +73 -0
  71. package/dist/admin/src/components/DataManager/reducer.d.ts +17 -2
  72. package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +24 -8
  73. package/dist/admin/src/components/DataManager/utils/groupRenameChains.d.ts +31 -0
  74. package/dist/admin/src/components/DataManager/utils/isStorageCompatibleRename.d.ts +10 -0
  75. package/dist/admin/src/components/DataManager/utils/resolveAfterEditRenameConsent.d.ts +24 -0
  76. package/dist/admin/src/components/DataManager/utils/splitChainsByInheritedConsent.d.ts +26 -0
  77. package/dist/admin/src/components/FormModal/utils/getRenameStorageChange.d.ts +40 -0
  78. package/dist/admin/src/types.d.ts +23 -0
  79. package/dist/admin/translations/cs.json.js +0 -2
  80. package/dist/admin/translations/cs.json.js.map +1 -1
  81. package/dist/admin/translations/cs.json.mjs +0 -2
  82. package/dist/admin/translations/cs.json.mjs.map +1 -1
  83. package/dist/admin/translations/de.json.js +0 -2
  84. package/dist/admin/translations/de.json.js.map +1 -1
  85. package/dist/admin/translations/de.json.mjs +0 -2
  86. package/dist/admin/translations/de.json.mjs.map +1 -1
  87. package/dist/admin/translations/en.json.js +12 -2
  88. package/dist/admin/translations/en.json.js.map +1 -1
  89. package/dist/admin/translations/en.json.mjs +12 -2
  90. package/dist/admin/translations/en.json.mjs.map +1 -1
  91. package/dist/admin/translations/ja.json.js +0 -2
  92. package/dist/admin/translations/ja.json.js.map +1 -1
  93. package/dist/admin/translations/ja.json.mjs +0 -2
  94. package/dist/admin/translations/ja.json.mjs.map +1 -1
  95. package/dist/admin/translations/ko.json.js +0 -2
  96. package/dist/admin/translations/ko.json.js.map +1 -1
  97. package/dist/admin/translations/ko.json.mjs +0 -2
  98. package/dist/admin/translations/ko.json.mjs.map +1 -1
  99. package/dist/admin/translations/nl.json.js +0 -2
  100. package/dist/admin/translations/nl.json.js.map +1 -1
  101. package/dist/admin/translations/nl.json.mjs +0 -2
  102. package/dist/admin/translations/nl.json.mjs.map +1 -1
  103. package/dist/admin/translations/pl.json.js +0 -2
  104. package/dist/admin/translations/pl.json.js.map +1 -1
  105. package/dist/admin/translations/pl.json.mjs +0 -2
  106. package/dist/admin/translations/pl.json.mjs.map +1 -1
  107. package/dist/admin/translations/ru.json.js +0 -2
  108. package/dist/admin/translations/ru.json.js.map +1 -1
  109. package/dist/admin/translations/ru.json.mjs +0 -2
  110. package/dist/admin/translations/ru.json.mjs.map +1 -1
  111. package/dist/admin/translations/sk.json.js +0 -2
  112. package/dist/admin/translations/sk.json.js.map +1 -1
  113. package/dist/admin/translations/sk.json.mjs +0 -2
  114. package/dist/admin/translations/sk.json.mjs.map +1 -1
  115. package/dist/server/config.js +24 -3
  116. package/dist/server/config.js.map +1 -1
  117. package/dist/server/config.mjs +21 -3
  118. package/dist/server/config.mjs.map +1 -1
  119. package/dist/server/controllers/schema.js +2 -2
  120. package/dist/server/controllers/schema.js.map +1 -1
  121. package/dist/server/controllers/schema.mjs +1 -1
  122. package/dist/server/controllers/schema.mjs.map +1 -1
  123. package/dist/server/controllers/validation/content-type.js +9 -11
  124. package/dist/server/controllers/validation/content-type.js.map +1 -1
  125. package/dist/server/controllers/validation/content-type.mjs +7 -9
  126. package/dist/server/controllers/validation/content-type.mjs.map +1 -1
  127. package/dist/server/controllers/validation/model-schema.js +2 -3
  128. package/dist/server/controllers/validation/model-schema.js.map +1 -1
  129. package/dist/server/controllers/validation/model-schema.mjs +1 -2
  130. package/dist/server/controllers/validation/model-schema.mjs.map +1 -1
  131. package/dist/server/controllers/validation/relations.js +1 -2
  132. package/dist/server/controllers/validation/relations.js.map +1 -1
  133. package/dist/server/controllers/validation/relations.mjs +1 -2
  134. package/dist/server/controllers/validation/relations.mjs.map +1 -1
  135. package/dist/server/controllers/validation/schema.js +42 -23
  136. package/dist/server/controllers/validation/schema.js.map +1 -1
  137. package/dist/server/controllers/validation/schema.mjs +43 -24
  138. package/dist/server/controllers/validation/schema.mjs.map +1 -1
  139. package/dist/server/index.js +1 -1
  140. package/dist/server/index.js.map +1 -1
  141. package/dist/server/services/content-types.js +1 -2
  142. package/dist/server/services/content-types.js.map +1 -1
  143. package/dist/server/services/content-types.mjs +2 -3
  144. package/dist/server/services/content-types.mjs.map +1 -1
  145. package/dist/server/services/migration-builder/index.js +580 -0
  146. package/dist/server/services/migration-builder/index.js.map +1 -0
  147. package/dist/server/services/migration-builder/index.mjs +573 -0
  148. package/dist/server/services/migration-builder/index.mjs.map +1 -0
  149. package/dist/server/services/schema-builder/content-type-builder.js +31 -1
  150. package/dist/server/services/schema-builder/content-type-builder.js.map +1 -1
  151. package/dist/server/services/schema-builder/content-type-builder.mjs +31 -1
  152. package/dist/server/services/schema-builder/content-type-builder.mjs.map +1 -1
  153. package/dist/server/services/schema-mutation.js +30 -2
  154. package/dist/server/services/schema-mutation.js.map +1 -1
  155. package/dist/server/services/schema-mutation.mjs +10 -2
  156. package/dist/server/services/schema-mutation.mjs.map +1 -1
  157. package/dist/server/services/schema.js +312 -15
  158. package/dist/server/services/schema.js.map +1 -1
  159. package/dist/server/services/schema.mjs +305 -9
  160. package/dist/server/services/schema.mjs.map +1 -1
  161. package/dist/server/src/config.d.ts +23 -2
  162. package/dist/server/src/config.d.ts.map +1 -1
  163. package/dist/server/src/controllers/validation/content-type.d.ts +0 -1
  164. package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -1
  165. package/dist/server/src/controllers/validation/relations.d.ts.map +1 -1
  166. package/dist/server/src/controllers/validation/schema.d.ts +28 -0
  167. package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
  168. package/dist/server/src/index.d.ts +10 -2
  169. package/dist/server/src/index.d.ts.map +1 -1
  170. package/dist/server/src/services/content-types.d.ts.map +1 -1
  171. package/dist/server/src/services/migration-builder/index.d.ts +83 -0
  172. package/dist/server/src/services/migration-builder/index.d.ts.map +1 -0
  173. package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -1
  174. package/dist/server/src/services/schema-mutation.d.ts +4 -2
  175. package/dist/server/src/services/schema-mutation.d.ts.map +1 -1
  176. package/dist/server/src/services/schema.d.ts +19 -0
  177. package/dist/server/src/services/schema.d.ts.map +1 -1
  178. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"file":"content-type.js","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { flatMap, getOr, has, snakeCase } from 'lodash/fp';\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = getOr(\n typeKinds.COLLECTION_TYPE,\n 'contentType.kind',\n data\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has('contentType', data)) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has('components', data) && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has('uid', comp)) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as\n const usedNames = flatMap((ct: Struct.ContentTypeSchema) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n })(strapi.contentTypes as any);\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","getOr","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","flatMap","ct","info","strapi","contentTypes","every","usedName","snakeCase","Object","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":";;;;;;;;;;AAAA;AAmCA;;AAEC,IACD,MAAMA,eAAAA,GAAkB;IACtB,CAACC,mBAAAA,CAAUC,WAAW,GAAG;AACvB,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA;AACD,KAAA;IACD,CAACD,mBAAAA,CAAUE,eAAe,GAAG;AAC3B,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA;AACD;AACH,CAAA;AAEA;;AAEC,IACD,MAAMC,WAAAA,GAAc;AAAIC,IAAAA,GAAAA,uBAAAA;AAAe,IAAA,KAAA;AAAO,IAAA,WAAA;AAAa,IAAA,aAAA;AAAe,IAAA;AAAc,CAAA;AAExF;;IAGA,MAAMC,uBAAAA,GAA0B,CAACC,IAAAA,EAA8B,EAAEC,YAAY,KAAK,EAAE,GAAG,EAAE,GAAA;AACvF,IAAA,MAAMC,IAAAA,GAAqCC,QAAAA,CACzCT,mBAAAA,CAAUE,eAAe,EACzB,kBAAA,EACAI,IAAAA,CAAAA;IAGF,MAAMI,iBAAAA,GAAoBC,yBAAaR,WAAAA,EAAaJ,eAAe,CAACS,IAAAA,CAAK,IAAI,EAAE,EAAE;AAC/EI,QAAAA,SAAAA,EAAWC,qBAAWC;AACxB,KAAA,CAAA,CACGC,KAAK,CAAC;AACLC,QAAAA,WAAAA,EAAaC,UAAIC,MAAM,EAAA,CAAGC,GAAG,CAAC,GAAGC,QAAQ,EAAA;AACzCC,QAAAA,YAAAA,EAAcJ,SAAAA,CACXC,MAAM,EAAA,CACNC,GAAG,CAAC,CAAA,CAAA,CACJG,IAAI,CAACC,eAAAA,CAAgBhB,YACrBe,IAAI,CAACE,iCAAAA,EAAAA,CAAAA,CACLC,WAAW,GACXL,QAAQ,EAAA;AACXM,QAAAA,UAAAA,EAAYT,SAAAA,CACTC,MAAM,EAAA,CACNC,GAAG,CAAC,CAAA,CAAA,CACJG,IAAI,CAACC,eAAAA,CAAgBhB,SAAAA,CAAAA,CAAAA,CACrBe,IAAI,CAACK,+BAAAA,CAAgCpB;AACrCe,SAAAA,IAAI,CAACE,iCAAAA,EAAAA,CAAAA,CACLC,WAAW,EAAA,CACXL,QAAQ;KACb,CAAA,CACCE,IAAI,CACH,mCAAA,EACA,0DAAA,EACA,CAACM,QAAUA,KAAAA,CAAMP,YAAY,KAAKO,KAAAA,CAAMF,UAAU,CAAA;IAGtD,OAAOT,SAAAA,CACJY,MAAM,CAAC;;;;;;;AAONC,QAAAA,WAAAA,EAAapB,iBAAAA,CAAkBU,QAAQ,EAAA,CAAGW,SAAS,CAAC,KAAA,CAAA;QACpDC,UAAAA,EAAYC;AACd,KAAA,CAAA,CACCF,SAAS,EAAA;AACd,CAAA;AAEA;;IAGO,MAAMG,wBAAAA,GAA2B,CAAC5B,IAAAA,GAAAA;IACvC,OAAO6B,uBAAAA,CAAkB9B,wBAAwBC,IAAAA,CAAAA,CAAAA,CAAOA,IAAAA,CAAAA;AAC1D;AAEA;;IAGO,MAAM8B,8BAAAA,GAAiC,CAAC9B,IAAAA,GAAAA;IAC7C,IAAI+B,MAAAA,CAAI,eAAe/B,IAAAA,CAAAA,EAAO;AAC5BgC,QAAAA,iCAAAA,CAAoBhC,KAAKwB,WAAW,CAAA;AACpCS,QAAAA,0CAAAA,CAA6BjC,KAAKwB,WAAW,CAAA;AAC/C,IAAA;IAEA,IAAIO,MAAAA,CAAI,cAAc/B,IAAAA,CAAAA,IAASkC,KAAAA,CAAMC,OAAO,CAACnC,IAAAA,CAAK0B,UAAU,CAAA,EAAG;AAC7D1B,QAAAA,IAAAA,CAAK0B,UAAU,CAACU,OAAO,CAAC,CAACC,IAAAA,GAAAA;YACvB,IAAIN,MAAAA,CAAI,OAAOM,IAAAA,CAAAA,EAAO;gBACpBL,iCAAAA,CAAoBK,IAAAA,CAAAA;AACtB,YAAA;AACF,QAAA,CAAA,CAAA;AACF,IAAA;IAEA,OAAOR,uBAAAA,CAAkB9B,wBAAwBC,IAAAA,EAAM;QAAEC,SAAAA,EAAW;KAAK,CAAA,CAAA,CAAID,IAAAA,CAAAA;AAC/E;AAEA,MAAMkB,iCAAAA,GAAoC,IAAA;AACxC,IAAA,MAAMoB,aAAAA,GAAgBC,gBAAAA,CAAW,SAAA,CAAA,CAAWC,gBAAgB,GAAGC,MAAM;IAErE,OAAO;QACLC,IAAAA,EAAM,0BAAA;AACNC,QAAAA,OAAAA,EAAS,CAAC,mCAAmC,EAAEL,aAAAA,CAAcM,IAAI,CAAC,IAAA,CAAA,CAAA,CAAO;AACzE5B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;YACjB,IAAI,OAAOA,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;AAEA,YAAA,OAAO,CAACiB,gBAAAA,CAAW,SAAA,CAAA,CAAWM,mBAAmB,CAACvB,KAAAA,CAAAA;AACpD,QAAA;AACF,KAAA;AACF,CAAA;AAEA,MAAML,kBAAkB,CAAChB,SAAAA,GAAAA;;IAEvB,MAAM6C,SAAAA,GAAYC,WAAQ,CAACC,EAAAA,GAAAA;QACzB,OAAO;AAACA,YAAAA,EAAAA,CAAGC,IAAI,EAAElC,YAAAA;AAAciC,YAAAA,EAAAA,CAAGC,IAAI,EAAE7B;AAAW,SAAA;AACrD,IAAA,CAAA,CAAA,CAAG8B,OAAOC,YAAY,CAAA;IAEtB,OAAO;QACLT,IAAAA,EAAM,iBAAA;QACNC,OAAAA,EAAS,6EAAA;AACT3B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;;AAEjB,YAAA,IAAIrB,WAAW,OAAO,IAAA;;YAGtB,IAAI,OAAOqB,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;;AAGA,YAAA,OAAOwB,UAAUM,KAAK,CAAC,CAACC,QAAAA,GAAaC,YAAAA,CAAUD,cAAcC,YAAAA,CAAUhC,KAAAA,CAAAA,CAAAA;AACzE,QAAA;AACF,KAAA;AACF,CAAA;AAEA,MAAMD,kCAAkC,CAACpB,SAAAA,GAAAA;AACvC,IAAA,MAAM6C,YAAYS,MAAAA,CAAOC,IAAI,CAACN,MAAAA,CAAOC,YAAY,CAAA,CAAEM,GAAG,CACpD,CAACC,MAAQR,MAAAA,CAAOC,YAAY,CAACO,GAAAA,CAAgC,CAACC,cAAc,CAAA;IAG9E,OAAO;QACLjB,IAAAA,EAAM,iBAAA;QACNC,OAAAA,EAAS,6EAAA;AACT3B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;;AAEjB,YAAA,IAAIrB,WAAW,OAAO,IAAA;;YAGtB,IAAI,OAAOqB,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;;AAGA,YAAA,OAAOwB,UAAUM,KAAK,CAAC,CAACC,QAAAA,GAAaC,YAAAA,CAAUD,cAAcC,YAAAA,CAAUhC,KAAAA,CAAAA,CAAAA;AACzE,QAAA;AACF,KAAA;AACF,CAAA;AAEA;;AAEC,IACD,MAAMsC,UAAAA,GAAajD,SAAAA,CAAIC,MAAM,EAAA,CAAGiD,KAAK,CAAC;AAACnE,IAAAA,mBAAAA,CAAUC,WAAW;AAAED,IAAAA,mBAAAA,CAAUE;AAAgB,CAAA,CAAA;AAEjF,MAAMkE,YAAAA,GAAejC,uBAAAA,CAAkB+B,UAAAA;;;;;;"}
1
+ {"version":3,"file":"content-type.js","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["import { get, has, snakeCase } from 'lodash';\n/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = get(\n data,\n 'contentType.kind',\n typeKinds.COLLECTION_TYPE\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has(data, 'contentType')) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has(data, 'components') && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has(comp, 'uid')) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n const usedNames = Object.values(strapi.contentTypes).flatMap((ct) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n });\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","get","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","Object","values","strapi","contentTypes","flatMap","ct","info","every","usedName","snakeCase","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":";;;;;;;;;;AAmCA;;AAEC,IACD,MAAMA,eAAAA,GAAkB;IACtB,CAACC,mBAAAA,CAAUC,WAAW,GAAG;AACvB,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA;AACD,KAAA;IACD,CAACD,mBAAAA,CAAUE,eAAe,GAAG;AAC3B,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA,UAAA;AACA,QAAA,WAAA;AACA,QAAA,YAAA;AACA,QAAA;AACD;AACH,CAAA;AAEA;;AAEC,IACD,MAAMC,WAAAA,GAAc;AAAIC,IAAAA,GAAAA,uBAAAA;AAAe,IAAA,KAAA;AAAO,IAAA,WAAA;AAAa,IAAA,aAAA;AAAe,IAAA;AAAc,CAAA;AAExF;;IAGA,MAAMC,uBAAAA,GAA0B,CAACC,IAAAA,EAA8B,EAAEC,YAAY,KAAK,EAAE,GAAG,EAAE,GAAA;AACvF,IAAA,MAAMC,IAAAA,GAAqCC,KAAAA,CACzCH,IAAAA,EACA,kBAAA,EACAN,oBAAUE,eAAe,CAAA;IAG3B,MAAMQ,iBAAAA,GAAoBC,yBAAaR,WAAAA,EAAaJ,eAAe,CAACS,IAAAA,CAAK,IAAI,EAAE,EAAE;AAC/EI,QAAAA,SAAAA,EAAWC,qBAAWC;AACxB,KAAA,CAAA,CACGC,KAAK,CAAC;AACLC,QAAAA,WAAAA,EAAaC,UAAIC,MAAM,EAAA,CAAGC,GAAG,CAAC,GAAGC,QAAQ,EAAA;AACzCC,QAAAA,YAAAA,EAAcJ,SAAAA,CACXC,MAAM,EAAA,CACNC,GAAG,CAAC,CAAA,CAAA,CACJG,IAAI,CAACC,eAAAA,CAAgBhB,YACrBe,IAAI,CAACE,iCAAAA,EAAAA,CAAAA,CACLC,WAAW,GACXL,QAAQ,EAAA;AACXM,QAAAA,UAAAA,EAAYT,SAAAA,CACTC,MAAM,EAAA,CACNC,GAAG,CAAC,CAAA,CAAA,CACJG,IAAI,CAACC,eAAAA,CAAgBhB,SAAAA,CAAAA,CAAAA,CACrBe,IAAI,CAACK,+BAAAA,CAAgCpB;AACrCe,SAAAA,IAAI,CAACE,iCAAAA,EAAAA,CAAAA,CACLC,WAAW,EAAA,CACXL,QAAQ;KACb,CAAA,CACCE,IAAI,CACH,mCAAA,EACA,0DAAA,EACA,CAACM,QAAUA,KAAAA,CAAMP,YAAY,KAAKO,KAAAA,CAAMF,UAAU,CAAA;IAGtD,OAAOT,SAAAA,CACJY,MAAM,CAAC;;;;;;;AAONC,QAAAA,WAAAA,EAAapB,iBAAAA,CAAkBU,QAAQ,EAAA,CAAGW,SAAS,CAAC,KAAA,CAAA;QACpDC,UAAAA,EAAYC;AACd,KAAA,CAAA,CACCF,SAAS,EAAA;AACd,CAAA;AAEA;;IAGO,MAAMG,wBAAAA,GAA2B,CAAC5B,IAAAA,GAAAA;IACvC,OAAO6B,uBAAAA,CAAkB9B,wBAAwBC,IAAAA,CAAAA,CAAAA,CAAOA,IAAAA,CAAAA;AAC1D;AAEA;;IAGO,MAAM8B,8BAAAA,GAAiC,CAAC9B,IAAAA,GAAAA;IAC7C,IAAI+B,KAAAA,CAAI/B,MAAM,aAAA,CAAA,EAAgB;AAC5BgC,QAAAA,iCAAAA,CAAoBhC,KAAKwB,WAAW,CAAA;AACpCS,QAAAA,0CAAAA,CAA6BjC,KAAKwB,WAAW,CAAA;AAC/C,IAAA;IAEA,IAAIO,KAAAA,CAAI/B,MAAM,YAAA,CAAA,IAAiBkC,KAAAA,CAAMC,OAAO,CAACnC,IAAAA,CAAK0B,UAAU,CAAA,EAAG;AAC7D1B,QAAAA,IAAAA,CAAK0B,UAAU,CAACU,OAAO,CAAC,CAACC,IAAAA,GAAAA;YACvB,IAAIN,KAAAA,CAAIM,MAAM,KAAA,CAAA,EAAQ;gBACpBL,iCAAAA,CAAoBK,IAAAA,CAAAA;AACtB,YAAA;AACF,QAAA,CAAA,CAAA;AACF,IAAA;IAEA,OAAOR,uBAAAA,CAAkB9B,wBAAwBC,IAAAA,EAAM;QAAEC,SAAAA,EAAW;KAAK,CAAA,CAAA,CAAID,IAAAA,CAAAA;AAC/E;AAEA,MAAMkB,iCAAAA,GAAoC,IAAA;AACxC,IAAA,MAAMoB,aAAAA,GAAgBC,gBAAAA,CAAW,SAAA,CAAA,CAAWC,gBAAgB,GAAGC,MAAM;IAErE,OAAO;QACLC,IAAAA,EAAM,0BAAA;AACNC,QAAAA,OAAAA,EAAS,CAAC,mCAAmC,EAAEL,aAAAA,CAAcM,IAAI,CAAC,IAAA,CAAA,CAAA,CAAO;AACzE5B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;YACjB,IAAI,OAAOA,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;AAEA,YAAA,OAAO,CAACiB,gBAAAA,CAAW,SAAA,CAAA,CAAWM,mBAAmB,CAACvB,KAAAA,CAAAA;AACpD,QAAA;AACF,KAAA;AACF,CAAA;AAEA,MAAML,kBAAkB,CAAChB,SAAAA,GAAAA;IACvB,MAAM6C,SAAAA,GAAYC,OAAOC,MAAM,CAACC,OAAOC,YAAY,CAAA,CAAEC,OAAO,CAAC,CAACC,EAAAA,GAAAA;QAC5D,OAAO;AAACA,YAAAA,EAAAA,CAAGC,IAAI,EAAEtC,YAAAA;AAAcqC,YAAAA,EAAAA,CAAGC,IAAI,EAAEjC;AAAW,SAAA;AACrD,IAAA,CAAA,CAAA;IAEA,OAAO;QACLsB,IAAAA,EAAM,iBAAA;QACNC,OAAAA,EAAS,6EAAA;AACT3B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;;AAEjB,YAAA,IAAIrB,WAAW,OAAO,IAAA;;YAGtB,IAAI,OAAOqB,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;;AAGA,YAAA,OAAOwB,UAAUQ,KAAK,CAAC,CAACC,QAAAA,GAAaC,WAAAA,CAAUD,cAAcC,WAAAA,CAAUlC,KAAAA,CAAAA,CAAAA;AACzE,QAAA;AACF,KAAA;AACF,CAAA;AAEA,MAAMD,kCAAkC,CAACpB,SAAAA,GAAAA;AACvC,IAAA,MAAM6C,YAAYC,MAAAA,CAAOU,IAAI,CAACR,MAAAA,CAAOC,YAAY,CAAA,CAAEQ,GAAG,CACpD,CAACC,MAAQV,MAAAA,CAAOC,YAAY,CAACS,GAAAA,CAAgC,CAACC,cAAc,CAAA;IAG9E,OAAO;QACLlB,IAAAA,EAAM,iBAAA;QACNC,OAAAA,EAAS,6EAAA;AACT3B,QAAAA,IAAAA,CAAAA,CAAKM,KAAc,EAAA;;AAEjB,YAAA,IAAIrB,WAAW,OAAO,IAAA;;YAGtB,IAAI,OAAOqB,UAAU,QAAA,EAAU;gBAC7B,OAAO,IAAA;AACT,YAAA;;AAGA,YAAA,OAAOwB,UAAUQ,KAAK,CAAC,CAACC,QAAAA,GAAaC,WAAAA,CAAUD,cAAcC,WAAAA,CAAUlC,KAAAA,CAAAA,CAAAA;AACzE,QAAA;AACF,KAAA;AACF,CAAA;AAEA;;AAEC,IACD,MAAMuC,UAAAA,GAAalD,SAAAA,CAAIC,MAAM,EAAA,CAAGkD,KAAK,CAAC;AAACpE,IAAAA,mBAAAA,CAAUC,WAAW;AAAED,IAAAA,mBAAAA,CAAUE;AAAgB,CAAA,CAAA;AAEjF,MAAMmE,YAAAA,GAAelC,uBAAAA,CAAkBgC,UAAAA;;;;;;"}
@@ -1,4 +1,4 @@
1
- import { has, getOr, flatMap, snakeCase } from 'lodash/fp';
1
+ import { has, get, snakeCase } from 'lodash';
2
2
  import { yup, validateYupSchema } from '@strapi/utils';
3
3
  import { getService } from '../../utils/index.mjs';
4
4
  import { typeKinds, modelTypes, DEFAULT_TYPES } from '../../services/constants.mjs';
@@ -6,7 +6,6 @@ import { createSchema } from './model-schema.mjs';
6
6
  import { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform.mjs';
7
7
  import { nestedComponentSchema } from './component.mjs';
8
8
 
9
- /* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format
10
9
  /**
11
10
  * Allowed relation per type kind
12
11
  */ const VALID_RELATIONS = {
@@ -41,7 +40,7 @@ import { nestedComponentSchema } from './component.mjs';
41
40
  /**
42
41
  * Returns a yup schema to validate a content type payload
43
42
  */ const createContentTypeSchema = (data, { isEdition = false } = {})=>{
44
- const kind = getOr(typeKinds.COLLECTION_TYPE, 'contentType.kind', data);
43
+ const kind = get(data, 'contentType.kind', typeKinds.COLLECTION_TYPE);
45
44
  const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
46
45
  modelType: modelTypes.CONTENT_TYPE
47
46
  }).shape({
@@ -69,13 +68,13 @@ import { nestedComponentSchema } from './component.mjs';
69
68
  /**
70
69
  * Validator for content type edition
71
70
  */ const validateUpdateContentTypeInput = (data)=>{
72
- if (has('contentType', data)) {
71
+ if (has(data, 'contentType')) {
73
72
  removeEmptyDefaults(data.contentType);
74
73
  removeDeletedUIDTargetFields(data.contentType);
75
74
  }
76
- if (has('components', data) && Array.isArray(data.components)) {
75
+ if (has(data, 'components') && Array.isArray(data.components)) {
77
76
  data.components.forEach((comp)=>{
78
- if (has('uid', comp)) {
77
+ if (has(comp, 'uid')) {
79
78
  removeEmptyDefaults(comp);
80
79
  }
81
80
  });
@@ -98,13 +97,12 @@ const forbiddenContentTypeNameValidator = ()=>{
98
97
  };
99
98
  };
100
99
  const nameIsAvailable = (isEdition)=>{
101
- // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as
102
- const usedNames = flatMap((ct)=>{
100
+ const usedNames = Object.values(strapi.contentTypes).flatMap((ct)=>{
103
101
  return [
104
102
  ct.info?.singularName,
105
103
  ct.info?.pluralName
106
104
  ];
107
- })(strapi.contentTypes);
105
+ });
108
106
  return {
109
107
  name: 'nameAlreadyUsed',
110
108
  message: 'contentType: name `${value}` is already being used by another content type.',
@@ -1 +1 @@
1
- {"version":3,"file":"content-type.mjs","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { flatMap, getOr, has, snakeCase } from 'lodash/fp';\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = getOr(\n typeKinds.COLLECTION_TYPE,\n 'contentType.kind',\n data\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has('contentType', data)) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has('components', data) && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has('uid', comp)) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n // TODO TS: if strapi.contentTypes (ie, ContentTypes) works as an ArrayLike and is used like this, we may want to ensure it is typed so that it can be without using as\n const usedNames = flatMap((ct: Struct.ContentTypeSchema) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n })(strapi.contentTypes as any);\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","getOr","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","flatMap","ct","info","strapi","contentTypes","every","usedName","snakeCase","Object","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
1
+ {"version":3,"file":"content-type.mjs","sources":["../../../../server/src/controllers/validation/content-type.ts"],"sourcesContent":["import { get, has, snakeCase } from 'lodash';\n/* eslint-disable no-template-curly-in-string */ // yup templates need to be in this format\n\nimport { yup, validateYupSchema } from '@strapi/utils';\n\nimport type { Struct, Internal, UID } from '@strapi/types';\n\nimport { getService } from '../../utils';\nimport { modelTypes, DEFAULT_TYPES, typeKinds } from '../../services/constants';\nimport { createSchema } from './model-schema';\nimport { removeEmptyDefaults, removeDeletedUIDTargetFields } from './data-transform';\nimport { nestedComponentSchema } from './component';\n\n// Input flattens some fields of the \"info\" into the root type\nexport type CreateContentTypeInput = {\n uid?: UID.ContentType;\n contentType?: Partial<Struct.ContentTypeSchema> & Partial<Struct.ContentTypeSchemaInfo>;\n components?: Array<\n Partial<Struct.ComponentSchema> &\n Partial<Struct.SchemaInfo> & { tmpUID?: Internal.UID.Component }\n >;\n singularName: Struct.ContentTypeSchemaInfo['singularName'];\n attributes: Struct.SchemaAttributes & Record<string, any>;\n kind: Struct.ContentTypeKind;\n collectionName?: Struct.CollectionTypeSchema['collectionName'];\n pluralName: Struct.ContentTypeSchemaInfo['pluralName'];\n displayName: Struct.ContentTypeSchemaInfo['displayName'];\n description?: Struct.ContentTypeSchemaInfo['description'];\n options?: Struct.SchemaOptions;\n draftAndPublish?: Struct.SchemaOptions['draftAndPublish'];\n pluginOptions?: Struct.ContentTypeSchema['pluginOptions'];\n config?: object;\n plugin?: string;\n};\n\n/**\n * Allowed relation per type kind\n */\nconst VALID_RELATIONS = {\n [typeKinds.SINGLE_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n [typeKinds.COLLECTION_TYPE]: [\n 'oneToOne',\n 'oneToMany',\n 'manyToOne',\n 'manyToMany',\n 'morphOne',\n 'morphMany',\n 'morphToOne',\n 'morphToMany',\n ],\n} as const;\n\n/**\n * Allowed types\n */\nconst VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone', 'customField'];\n\n/**\n * Returns a yup schema to validate a content type payload\n */\nconst createContentTypeSchema = (data: CreateContentTypeInput, { isEdition = false } = {}) => {\n const kind: keyof typeof VALID_RELATIONS = get(\n data,\n 'contentType.kind',\n typeKinds.COLLECTION_TYPE\n );\n\n const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {\n modelType: modelTypes.CONTENT_TYPE,\n })\n .shape({\n displayName: yup.string().min(1).required(),\n singularName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n pluralName: yup\n .string()\n .min(1)\n .test(nameIsAvailable(isEdition))\n .test(nameIsNotExistingCollectionName(isEdition)) // TODO: v5: require singularName to not match a collection name\n .test(forbiddenContentTypeNameValidator())\n .isKebabCase()\n .required(),\n })\n .test(\n 'singularName-not-equal-pluralName',\n '${path}: singularName and pluralName should be different',\n (value) => value.singularName !== value.pluralName\n );\n\n return yup\n .object({\n // FIXME .noUnknown(false) will strip off the unwanted properties without throwing an error\n // Why not having .noUnknown() ? Because we want to be able to add options relatable to EE features\n // without having any reference to them in CE.\n // Why not handle an \"options\" object in the content-type ? The admin panel needs lots of rework\n // to be able to send this options object instead of top-level attributes.\n // @nathan-pichon 20/02/2023\n contentType: contentTypeSchema.required().noUnknown(false),\n components: nestedComponentSchema,\n })\n .noUnknown();\n};\n\n/**\n * Validator for content type creation\n */\nexport const validateContentTypeInput = (data: CreateContentTypeInput) => {\n return validateYupSchema(createContentTypeSchema(data))(data);\n};\n\n/**\n * Validator for content type edition\n */\nexport const validateUpdateContentTypeInput = (data: CreateContentTypeInput) => {\n if (has(data, 'contentType')) {\n removeEmptyDefaults(data.contentType);\n removeDeletedUIDTargetFields(data.contentType as Struct.ContentTypeSchema);\n }\n\n if (has(data, 'components') && Array.isArray(data.components)) {\n data.components.forEach((comp) => {\n if (has(comp, 'uid')) {\n removeEmptyDefaults(comp as Struct.ComponentSchema);\n }\n });\n }\n\n return validateYupSchema(createContentTypeSchema(data, { isEdition: true }))(data);\n};\n\nconst forbiddenContentTypeNameValidator = () => {\n const reservedNames = getService('builder').getReservedNames().models;\n\n return {\n name: 'forbiddenContentTypeName',\n message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,\n test(value: unknown) {\n if (typeof value !== 'string') {\n return true;\n }\n\n return !getService('builder').isReservedModelName(value);\n },\n };\n};\n\nconst nameIsAvailable = (isEdition: boolean) => {\n const usedNames = Object.values(strapi.contentTypes).flatMap((ct) => {\n return [ct.info?.singularName, ct.info?.pluralName];\n });\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\nconst nameIsNotExistingCollectionName = (isEdition: boolean) => {\n const usedNames = Object.keys(strapi.contentTypes).map(\n (key) => strapi.contentTypes[key as Internal.UID.ContentType].collectionName\n ) as string[];\n\n return {\n name: 'nameAlreadyUsed',\n message: 'contentType: name `${value}` is already being used by another content type.',\n test(value: unknown) {\n // don't check on edition\n if (isEdition) return true;\n\n // ignore if not a string (will be caught in another validator)\n if (typeof value !== 'string') {\n return true;\n }\n\n // compare snake case to check the actual column names that will be used in the database\n return usedNames.every((usedName) => snakeCase(usedName) !== snakeCase(value));\n },\n };\n};\n\n/**\n * Validates type kind\n */\nconst kindSchema = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]);\n\nexport const validateKind = validateYupSchema(kindSchema);\n"],"names":["VALID_RELATIONS","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","VALID_TYPES","DEFAULT_TYPES","createContentTypeSchema","data","isEdition","kind","get","contentTypeSchema","createSchema","modelType","modelTypes","CONTENT_TYPE","shape","displayName","yup","string","min","required","singularName","test","nameIsAvailable","forbiddenContentTypeNameValidator","isKebabCase","pluralName","nameIsNotExistingCollectionName","value","object","contentType","noUnknown","components","nestedComponentSchema","validateContentTypeInput","validateYupSchema","validateUpdateContentTypeInput","has","removeEmptyDefaults","removeDeletedUIDTargetFields","Array","isArray","forEach","comp","reservedNames","getService","getReservedNames","models","name","message","join","isReservedModelName","usedNames","Object","values","strapi","contentTypes","flatMap","ct","info","every","usedName","snakeCase","keys","map","key","collectionName","kindSchema","oneOf","validateKind"],"mappings":"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"}
@@ -2,7 +2,6 @@
2
2
 
3
3
  var utils = require('@strapi/utils');
4
4
  var _ = require('lodash');
5
- var fp = require('lodash/fp');
6
5
  var constants = require('../../services/constants.js');
7
6
  var index = require('../../utils/index.js');
8
7
  var common = require('./common.js');
@@ -58,10 +57,10 @@ const createAttributesValidator = ({ types: types$1, modelType, relations: relat
58
57
  });
59
58
  };
60
59
  const isConflictingKey = (key, attributes)=>{
61
- const snakeCaseKey = fp.snakeCase(key);
60
+ const snakeCaseKey = _.snakeCase(key);
62
61
  return Object.keys(attributes).some((existingKey)=>{
63
62
  if (existingKey === key) return false; // don't compare against itself
64
- return fp.snakeCase(existingKey) === snakeCaseKey;
63
+ return _.snakeCase(existingKey) === snakeCaseKey;
65
64
  });
66
65
  };
67
66
  const isForbiddenKey = (key)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"model-schema.js","sources":["../../../../server/src/controllers/validation/model-schema.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport _ from 'lodash';\nimport { snakeCase } from 'lodash/fp';\nimport { modelTypes, typeKinds } from '../../services/constants';\nimport { getService } from '../../utils';\nimport { isValidKey, isValidCollectionName } from './common';\nimport { getTypeValidator } from './types';\nimport { getRelationValidator } from './relations';\n\ntype ModelTypeInput = (typeof modelTypes)[keyof typeof modelTypes];\n\ntype CreateAttributesInput = {\n types: ReadonlyArray<string>;\n relations: ReadonlyArray<string>;\n modelType?: ModelTypeInput;\n};\n\nexport const createSchema = (\n types: CreateAttributesInput['types'],\n relations: CreateAttributesInput['relations'],\n { modelType }: { modelType?: ModelTypeInput } = {}\n) => {\n const shape = {\n description: yup.string(),\n options: yup.object(),\n pluginOptions: yup.object(),\n collectionName: yup.string().nullable().test(isValidCollectionName),\n attributes: createAttributesValidator({ types, relations, modelType }),\n draftAndPublish: yup.boolean(),\n } as any;\n\n if (modelType === modelTypes.CONTENT_TYPE) {\n shape.kind = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();\n }\n\n return yup.object(shape).noUnknown();\n};\n\nconst createAttributesValidator = ({ types, modelType, relations }: CreateAttributesInput) => {\n return yup.lazy((attributes) => {\n return yup\n .object()\n .shape(\n _.mapValues(attributes, (attribute, key) => {\n if (isForbiddenKey(key)) {\n return forbiddenValidator();\n }\n\n if (isConflictingKey(key, attributes)) {\n return conflictingKeysValidator(key);\n }\n\n if (attribute.type === 'relation') {\n return getRelationValidator(attribute, relations).test(isValidKey(key));\n }\n\n if (_.has(attribute, 'type')) {\n return getTypeValidator(attribute, { types, modelType, attributes }).test(\n isValidKey(key)\n );\n }\n\n return typeOrRelationValidator;\n })\n )\n .required('attributes.required');\n });\n};\n\nconst isConflictingKey = (key: string, attributes: Record<string, any>) => {\n const snakeCaseKey = snakeCase(key);\n\n return Object.keys(attributes).some((existingKey) => {\n if (existingKey === key) return false; // don't compare against itself\n return snakeCase(existingKey) === snakeCaseKey;\n });\n};\n\nconst isForbiddenKey = (key: string) => {\n return getService('builder').isReservedAttributeName(key);\n};\n\nconst forbiddenValidator = () => {\n const reservedNames = [...getService('builder').getReservedNames().attributes];\n\n return yup.mixed().test({\n name: 'forbiddenKeys',\n message: `Attribute keys cannot be one of ${reservedNames.join(', ')}`,\n test: () => false,\n });\n};\n\nconst conflictingKeysValidator = (key: string) => {\n return yup.mixed().test({\n name: 'conflictingKeys',\n message: `Attribute ${key} conflicts with an existing key`,\n test: () => false,\n });\n};\n\nconst typeOrRelationValidator = yup.object().test({\n name: 'mustHaveTypeOrTarget',\n message: 'Attribute must have either a type or a target',\n test: () => false,\n});\n"],"names":["createSchema","types","relations","modelType","shape","description","yup","string","options","object","pluginOptions","collectionName","nullable","test","isValidCollectionName","attributes","createAttributesValidator","draftAndPublish","boolean","modelTypes","CONTENT_TYPE","kind","oneOf","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","noUnknown","lazy","_","mapValues","attribute","key","isForbiddenKey","forbiddenValidator","isConflictingKey","conflictingKeysValidator","type","getRelationValidator","isValidKey","has","getTypeValidator","typeOrRelationValidator","required","snakeCaseKey","snakeCase","Object","keys","some","existingKey","getService","isReservedAttributeName","reservedNames","getReservedNames","mixed","name","message","join"],"mappings":"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"}
1
+ {"version":3,"file":"model-schema.js","sources":["../../../../server/src/controllers/validation/model-schema.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport _, { snakeCase } from 'lodash';\n\nimport { modelTypes, typeKinds } from '../../services/constants';\nimport { getService } from '../../utils';\nimport { isValidKey, isValidCollectionName } from './common';\nimport { getTypeValidator } from './types';\nimport { getRelationValidator } from './relations';\n\ntype ModelTypeInput = (typeof modelTypes)[keyof typeof modelTypes];\n\ntype CreateAttributesInput = {\n types: ReadonlyArray<string>;\n relations: ReadonlyArray<string>;\n modelType?: ModelTypeInput;\n};\n\nexport const createSchema = (\n types: CreateAttributesInput['types'],\n relations: CreateAttributesInput['relations'],\n { modelType }: { modelType?: ModelTypeInput } = {}\n) => {\n const shape = {\n description: yup.string(),\n options: yup.object(),\n pluginOptions: yup.object(),\n collectionName: yup.string().nullable().test(isValidCollectionName),\n attributes: createAttributesValidator({ types, relations, modelType }),\n draftAndPublish: yup.boolean(),\n } as any;\n\n if (modelType === modelTypes.CONTENT_TYPE) {\n shape.kind = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();\n }\n\n return yup.object(shape).noUnknown();\n};\n\nconst createAttributesValidator = ({ types, modelType, relations }: CreateAttributesInput) => {\n return yup.lazy((attributes) => {\n return yup\n .object()\n .shape(\n _.mapValues(attributes, (attribute, key) => {\n if (isForbiddenKey(key)) {\n return forbiddenValidator();\n }\n\n if (isConflictingKey(key, attributes)) {\n return conflictingKeysValidator(key);\n }\n\n if (attribute.type === 'relation') {\n return getRelationValidator(attribute, relations).test(isValidKey(key));\n }\n\n if (_.has(attribute, 'type')) {\n return getTypeValidator(attribute, { types, modelType, attributes }).test(\n isValidKey(key)\n );\n }\n\n return typeOrRelationValidator;\n })\n )\n .required('attributes.required');\n });\n};\n\nconst isConflictingKey = (key: string, attributes: Record<string, any>) => {\n const snakeCaseKey = snakeCase(key);\n\n return Object.keys(attributes).some((existingKey) => {\n if (existingKey === key) return false; // don't compare against itself\n return snakeCase(existingKey) === snakeCaseKey;\n });\n};\n\nconst isForbiddenKey = (key: string) => {\n return getService('builder').isReservedAttributeName(key);\n};\n\nconst forbiddenValidator = () => {\n const reservedNames = [...getService('builder').getReservedNames().attributes];\n\n return yup.mixed().test({\n name: 'forbiddenKeys',\n message: `Attribute keys cannot be one of ${reservedNames.join(', ')}`,\n test: () => false,\n });\n};\n\nconst conflictingKeysValidator = (key: string) => {\n return yup.mixed().test({\n name: 'conflictingKeys',\n message: `Attribute ${key} conflicts with an existing key`,\n test: () => false,\n });\n};\n\nconst typeOrRelationValidator = yup.object().test({\n name: 'mustHaveTypeOrTarget',\n message: 'Attribute must have either a type or a target',\n test: () => false,\n});\n"],"names":["createSchema","types","relations","modelType","shape","description","yup","string","options","object","pluginOptions","collectionName","nullable","test","isValidCollectionName","attributes","createAttributesValidator","draftAndPublish","boolean","modelTypes","CONTENT_TYPE","kind","oneOf","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","noUnknown","lazy","_","mapValues","attribute","key","isForbiddenKey","forbiddenValidator","isConflictingKey","conflictingKeysValidator","type","getRelationValidator","isValidKey","has","getTypeValidator","typeOrRelationValidator","required","snakeCaseKey","snakeCase","Object","keys","some","existingKey","getService","isReservedAttributeName","reservedNames","getReservedNames","mixed","name","message","join"],"mappings":";;;;;;;;;;;;;;AAiBO,MAAMA,YAAAA,GAAe,CAC1BC,KAAAA,EACAC,SAAAA,EACA,EAAEC,SAAS,EAAkC,GAAG,EAAE,GAAA;AAElD,IAAA,MAAMC,KAAAA,GAAQ;AACZC,QAAAA,WAAAA,EAAaC,UAAIC,MAAM,EAAA;AACvBC,QAAAA,OAAAA,EAASF,UAAIG,MAAM,EAAA;AACnBC,QAAAA,aAAAA,EAAeJ,UAAIG,MAAM,EAAA;AACzBE,QAAAA,cAAAA,EAAgBL,UAAIC,MAAM,EAAA,CAAGK,QAAQ,EAAA,CAAGC,IAAI,CAACC,4BAAAA,CAAAA;AAC7CC,QAAAA,UAAAA,EAAYC,yBAAAA,CAA0B;AAAEf,YAAAA,KAAAA;AAAOC,YAAAA,SAAAA;AAAWC,YAAAA;AAAU,SAAA,CAAA;AACpEc,QAAAA,eAAAA,EAAiBX,UAAIY,OAAO;AAC9B,KAAA;IAEA,IAAIf,SAAAA,KAAcgB,oBAAAA,CAAWC,YAAY,EAAE;AACzChB,QAAAA,KAAAA,CAAMiB,IAAI,GAAGf,SAAAA,CAAIC,MAAM,EAAA,CAAGe,KAAK,CAAC;AAACC,YAAAA,mBAAAA,CAAUC,WAAW;AAAED,YAAAA,mBAAAA,CAAUE;AAAgB,SAAA,CAAA,CAAEb,QAAQ,EAAA;AAC9F,IAAA;AAEA,IAAA,OAAON,SAAAA,CAAIG,MAAM,CAACL,KAAAA,CAAAA,CAAOsB,SAAS,EAAA;AACpC;AAEA,MAAMV,yBAAAA,GAA4B,CAAC,SAAEf,OAAK,EAAEE,SAAS,aAAED,WAAS,EAAyB,GAAA;IACvF,OAAOI,SAAAA,CAAIqB,IAAI,CAAC,CAACZ,UAAAA,GAAAA;QACf,OAAOT,SAAAA,CACJG,MAAM,EAAA,CACNL,KAAK,CACJwB,mBAAEC,SAAS,CAACd,UAAAA,EAAY,CAACe,SAAAA,EAAWC,GAAAA,GAAAA;AAClC,YAAA,IAAIC,eAAeD,GAAAA,CAAAA,EAAM;gBACvB,OAAOE,kBAAAA,EAAAA;AACT,YAAA;YAEA,IAAIC,gBAAAA,CAAiBH,KAAKhB,UAAAA,CAAAA,EAAa;AACrC,gBAAA,OAAOoB,wBAAAA,CAAyBJ,GAAAA,CAAAA;AAClC,YAAA;YAEA,IAAID,SAAAA,CAAUM,IAAI,KAAK,UAAA,EAAY;AACjC,gBAAA,OAAOC,8BAAAA,CAAqBP,SAAAA,EAAW5B,WAAAA,CAAAA,CAAWW,IAAI,CAACyB,iBAAAA,CAAWP,GAAAA,CAAAA,CAAAA;AACpE,YAAA;AAEA,YAAA,IAAIH,kBAAAA,CAAEW,GAAG,CAACT,SAAAA,EAAW,MAAA,CAAA,EAAS;AAC5B,gBAAA,OAAOU,uBAAiBV,SAAAA,EAAW;AAAE7B,2BAAAA,OAAAA;AAAOE,oBAAAA,SAAAA;AAAWY,oBAAAA;iBAAW,CAAA,CAAGF,IAAI,CACvEyB,iBAAAA,CAAWP,GAAAA,CAAAA,CAAAA;AAEf,YAAA;YAEA,OAAOU,uBAAAA;AACT,QAAA,CAAA,CAAA,CAAA,CAEDC,QAAQ,CAAC,qBAAA,CAAA;AACd,IAAA,CAAA,CAAA;AACF,CAAA;AAEA,MAAMR,gBAAAA,GAAmB,CAACH,GAAAA,EAAahB,UAAAA,GAAAA;AACrC,IAAA,MAAM4B,eAAeC,WAAAA,CAAUb,GAAAA,CAAAA;AAE/B,IAAA,OAAOc,OAAOC,IAAI,CAAC/B,UAAAA,CAAAA,CAAYgC,IAAI,CAAC,CAACC,WAAAA,GAAAA;AACnC,QAAA,IAAIA,WAAAA,KAAgBjB,GAAAA,EAAK,OAAO,KAAA,CAAA;AAChC,QAAA,OAAOa,YAAUI,WAAAA,CAAAA,KAAiBL,YAAAA;AACpC,IAAA,CAAA,CAAA;AACF,CAAA;AAEA,MAAMX,iBAAiB,CAACD,GAAAA,GAAAA;IACtB,OAAOkB,gBAAAA,CAAW,SAAA,CAAA,CAAWC,uBAAuB,CAACnB,GAAAA,CAAAA;AACvD,CAAA;AAEA,MAAME,kBAAAA,GAAqB,IAAA;AACzB,IAAA,MAAMkB,aAAAA,GAAgB;WAAIF,gBAAAA,CAAW,SAAA,CAAA,CAAWG,gBAAgB,EAAA,CAAGrC;AAAW,KAAA;AAE9E,IAAA,OAAOT,SAAAA,CAAI+C,KAAK,EAAA,CAAGxC,IAAI,CAAC;QACtByC,IAAAA,EAAM,eAAA;AACNC,QAAAA,OAAAA,EAAS,CAAC,gCAAgC,EAAEJ,aAAAA,CAAcK,IAAI,CAAC,IAAA,CAAA,CAAA,CAAO;AACtE3C,QAAAA,IAAAA,EAAM,IAAM;AACd,KAAA,CAAA;AACF,CAAA;AAEA,MAAMsB,2BAA2B,CAACJ,GAAAA,GAAAA;AAChC,IAAA,OAAOzB,SAAAA,CAAI+C,KAAK,EAAA,CAAGxC,IAAI,CAAC;QACtByC,IAAAA,EAAM,iBAAA;AACNC,QAAAA,OAAAA,EAAS,CAAC,UAAU,EAAExB,GAAAA,CAAI,+BAA+B,CAAC;AAC1DlB,QAAAA,IAAAA,EAAM,IAAM;AACd,KAAA,CAAA;AACF,CAAA;AAEA,MAAM4B,uBAAAA,GAA0BnC,SAAAA,CAAIG,MAAM,EAAA,CAAGI,IAAI,CAAC;IAChDyC,IAAAA,EAAM,sBAAA;IACNC,OAAAA,EAAS,+CAAA;AACT1C,IAAAA,IAAAA,EAAM,IAAM;AACd,CAAA,CAAA;;;;"}
@@ -1,6 +1,5 @@
1
1
  import { yup } from '@strapi/utils';
2
- import _ from 'lodash';
3
- import { snakeCase } from 'lodash/fp';
2
+ import _, { snakeCase } from 'lodash';
4
3
  import { modelTypes, typeKinds } from '../../services/constants.mjs';
5
4
  import { getService } from '../../utils/index.mjs';
6
5
  import { isValidCollectionName, isValidKey } from './common.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"model-schema.mjs","sources":["../../../../server/src/controllers/validation/model-schema.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport _ from 'lodash';\nimport { snakeCase } from 'lodash/fp';\nimport { modelTypes, typeKinds } from '../../services/constants';\nimport { getService } from '../../utils';\nimport { isValidKey, isValidCollectionName } from './common';\nimport { getTypeValidator } from './types';\nimport { getRelationValidator } from './relations';\n\ntype ModelTypeInput = (typeof modelTypes)[keyof typeof modelTypes];\n\ntype CreateAttributesInput = {\n types: ReadonlyArray<string>;\n relations: ReadonlyArray<string>;\n modelType?: ModelTypeInput;\n};\n\nexport const createSchema = (\n types: CreateAttributesInput['types'],\n relations: CreateAttributesInput['relations'],\n { modelType }: { modelType?: ModelTypeInput } = {}\n) => {\n const shape = {\n description: yup.string(),\n options: yup.object(),\n pluginOptions: yup.object(),\n collectionName: yup.string().nullable().test(isValidCollectionName),\n attributes: createAttributesValidator({ types, relations, modelType }),\n draftAndPublish: yup.boolean(),\n } as any;\n\n if (modelType === modelTypes.CONTENT_TYPE) {\n shape.kind = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();\n }\n\n return yup.object(shape).noUnknown();\n};\n\nconst createAttributesValidator = ({ types, modelType, relations }: CreateAttributesInput) => {\n return yup.lazy((attributes) => {\n return yup\n .object()\n .shape(\n _.mapValues(attributes, (attribute, key) => {\n if (isForbiddenKey(key)) {\n return forbiddenValidator();\n }\n\n if (isConflictingKey(key, attributes)) {\n return conflictingKeysValidator(key);\n }\n\n if (attribute.type === 'relation') {\n return getRelationValidator(attribute, relations).test(isValidKey(key));\n }\n\n if (_.has(attribute, 'type')) {\n return getTypeValidator(attribute, { types, modelType, attributes }).test(\n isValidKey(key)\n );\n }\n\n return typeOrRelationValidator;\n })\n )\n .required('attributes.required');\n });\n};\n\nconst isConflictingKey = (key: string, attributes: Record<string, any>) => {\n const snakeCaseKey = snakeCase(key);\n\n return Object.keys(attributes).some((existingKey) => {\n if (existingKey === key) return false; // don't compare against itself\n return snakeCase(existingKey) === snakeCaseKey;\n });\n};\n\nconst isForbiddenKey = (key: string) => {\n return getService('builder').isReservedAttributeName(key);\n};\n\nconst forbiddenValidator = () => {\n const reservedNames = [...getService('builder').getReservedNames().attributes];\n\n return yup.mixed().test({\n name: 'forbiddenKeys',\n message: `Attribute keys cannot be one of ${reservedNames.join(', ')}`,\n test: () => false,\n });\n};\n\nconst conflictingKeysValidator = (key: string) => {\n return yup.mixed().test({\n name: 'conflictingKeys',\n message: `Attribute ${key} conflicts with an existing key`,\n test: () => false,\n });\n};\n\nconst typeOrRelationValidator = yup.object().test({\n name: 'mustHaveTypeOrTarget',\n message: 'Attribute must have either a type or a target',\n test: () => false,\n});\n"],"names":["createSchema","types","relations","modelType","shape","description","yup","string","options","object","pluginOptions","collectionName","nullable","test","isValidCollectionName","attributes","createAttributesValidator","draftAndPublish","boolean","modelTypes","CONTENT_TYPE","kind","oneOf","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","noUnknown","lazy","_","mapValues","attribute","key","isForbiddenKey","forbiddenValidator","isConflictingKey","conflictingKeysValidator","type","getRelationValidator","isValidKey","has","getTypeValidator","typeOrRelationValidator","required","snakeCaseKey","snakeCase","Object","keys","some","existingKey","getService","isReservedAttributeName","reservedNames","getReservedNames","mixed","name","message","join"],"mappings":"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"}
1
+ {"version":3,"file":"model-schema.mjs","sources":["../../../../server/src/controllers/validation/model-schema.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport _, { snakeCase } from 'lodash';\n\nimport { modelTypes, typeKinds } from '../../services/constants';\nimport { getService } from '../../utils';\nimport { isValidKey, isValidCollectionName } from './common';\nimport { getTypeValidator } from './types';\nimport { getRelationValidator } from './relations';\n\ntype ModelTypeInput = (typeof modelTypes)[keyof typeof modelTypes];\n\ntype CreateAttributesInput = {\n types: ReadonlyArray<string>;\n relations: ReadonlyArray<string>;\n modelType?: ModelTypeInput;\n};\n\nexport const createSchema = (\n types: CreateAttributesInput['types'],\n relations: CreateAttributesInput['relations'],\n { modelType }: { modelType?: ModelTypeInput } = {}\n) => {\n const shape = {\n description: yup.string(),\n options: yup.object(),\n pluginOptions: yup.object(),\n collectionName: yup.string().nullable().test(isValidCollectionName),\n attributes: createAttributesValidator({ types, relations, modelType }),\n draftAndPublish: yup.boolean(),\n } as any;\n\n if (modelType === modelTypes.CONTENT_TYPE) {\n shape.kind = yup.string().oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE]).nullable();\n }\n\n return yup.object(shape).noUnknown();\n};\n\nconst createAttributesValidator = ({ types, modelType, relations }: CreateAttributesInput) => {\n return yup.lazy((attributes) => {\n return yup\n .object()\n .shape(\n _.mapValues(attributes, (attribute, key) => {\n if (isForbiddenKey(key)) {\n return forbiddenValidator();\n }\n\n if (isConflictingKey(key, attributes)) {\n return conflictingKeysValidator(key);\n }\n\n if (attribute.type === 'relation') {\n return getRelationValidator(attribute, relations).test(isValidKey(key));\n }\n\n if (_.has(attribute, 'type')) {\n return getTypeValidator(attribute, { types, modelType, attributes }).test(\n isValidKey(key)\n );\n }\n\n return typeOrRelationValidator;\n })\n )\n .required('attributes.required');\n });\n};\n\nconst isConflictingKey = (key: string, attributes: Record<string, any>) => {\n const snakeCaseKey = snakeCase(key);\n\n return Object.keys(attributes).some((existingKey) => {\n if (existingKey === key) return false; // don't compare against itself\n return snakeCase(existingKey) === snakeCaseKey;\n });\n};\n\nconst isForbiddenKey = (key: string) => {\n return getService('builder').isReservedAttributeName(key);\n};\n\nconst forbiddenValidator = () => {\n const reservedNames = [...getService('builder').getReservedNames().attributes];\n\n return yup.mixed().test({\n name: 'forbiddenKeys',\n message: `Attribute keys cannot be one of ${reservedNames.join(', ')}`,\n test: () => false,\n });\n};\n\nconst conflictingKeysValidator = (key: string) => {\n return yup.mixed().test({\n name: 'conflictingKeys',\n message: `Attribute ${key} conflicts with an existing key`,\n test: () => false,\n });\n};\n\nconst typeOrRelationValidator = yup.object().test({\n name: 'mustHaveTypeOrTarget',\n message: 'Attribute must have either a type or a target',\n test: () => false,\n});\n"],"names":["createSchema","types","relations","modelType","shape","description","yup","string","options","object","pluginOptions","collectionName","nullable","test","isValidCollectionName","attributes","createAttributesValidator","draftAndPublish","boolean","modelTypes","CONTENT_TYPE","kind","oneOf","typeKinds","SINGLE_TYPE","COLLECTION_TYPE","noUnknown","lazy","_","mapValues","attribute","key","isForbiddenKey","forbiddenValidator","isConflictingKey","conflictingKeysValidator","type","getRelationValidator","isValidKey","has","getTypeValidator","typeOrRelationValidator","required","snakeCaseKey","snakeCase","Object","keys","some","existingKey","getService","isReservedAttributeName","reservedNames","getReservedNames","mixed","name","message","join"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var fp = require('lodash/fp');
4
3
  var utils = require('@strapi/utils');
5
4
  var constants = require('../../services/constants.js');
6
5
  var common = require('./common.js');
@@ -15,7 +14,7 @@ const isValidRelation = (validNatures)=>function isValidRelationValue(value) {
15
14
  return true;
16
15
  }
17
16
  if (this.parent.target === constants.coreUids.STRAPI_USER) {
18
- if (!validNatures.includes(value) || !fp.isUndefined(this.parent.targetAttribute)) {
17
+ if (!validNatures.includes(value) || this.parent.targetAttribute !== undefined) {
19
18
  return this.createError({
20
19
  path: this.path,
21
20
  message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`
@@ -1 +1 @@
1
- {"version":3,"file":"relations.js","sources":["../../../../server/src/controllers/validation/relations.ts"],"sourcesContent":["import { isUndefined } from 'lodash/fp';\nimport { yup } from '@strapi/utils';\nimport type { TestContext, TestFunction } from 'yup';\nimport type { Schema, UID } from '@strapi/types';\nimport { typeKinds, coreUids } from '../../services/constants';\nimport { isValidName } from './common';\n\nconst STRAPI_USER_RELATIONS = ['oneToOne', 'oneToMany'];\n\nconst isValidRelation = (validNatures: ReadonlyArray<string>): TestFunction<string | undefined> =>\n function isValidRelationValue(this: TestContext, value) {\n // NOTE: In case of an undefined value, delegate the check to .required()\n if (value === undefined) {\n return true;\n }\n\n if (this.parent.target === coreUids.STRAPI_USER) {\n if (!validNatures.includes(value) || !isUndefined(this.parent.targetAttribute)) {\n return this.createError({\n path: this.path,\n message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`,\n });\n }\n }\n\n return validNatures.includes(value)\n ? true\n : this.createError({\n path: this.path,\n message: `must be one of the following values: ${validNatures.join(', ')}`,\n });\n };\n\nexport const getRelationValidator = (\n attribute: Schema.Attribute.Relation,\n allowedRelations: ReadonlyArray<string>\n) => {\n const contentTypesUIDs = Object.keys(strapi.contentTypes)\n .filter((key) => strapi.contentTypes[key as UID.ContentType].kind === typeKinds.COLLECTION_TYPE)\n .filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER)\n .concat(['__self__', '__contentType__']);\n\n const base = {\n type: yup.string().oneOf(['relation']).required(),\n relation: yup.string().test('isValidRelation', isValidRelation(allowedRelations)).required(),\n configurable: yup.boolean().nullable(),\n required: yup.boolean(),\n private: yup.boolean().nullable(),\n pluginOptions: yup.object(),\n };\n\n switch (attribute.relation) {\n case 'oneToOne':\n case 'oneToMany':\n case 'manyToOne':\n case 'manyToMany':\n case 'morphOne':\n case 'morphMany': {\n return yup.object({\n ...base,\n target: yup.string().oneOf(contentTypesUIDs).required(),\n targetAttribute: yup.string().test(isValidName).nullable(),\n });\n }\n case 'morphToOne':\n case 'morphToMany':\n default: {\n return yup.object({ ...base });\n }\n }\n};\n"],"names":["STRAPI_USER_RELATIONS","isValidRelation","validNatures","isValidRelationValue","value","undefined","parent","target","coreUids","STRAPI_USER","includes","isUndefined","targetAttribute","createError","path","message","join","getRelationValidator","attribute","allowedRelations","contentTypesUIDs","Object","keys","strapi","contentTypes","filter","key","kind","typeKinds","COLLECTION_TYPE","startsWith","PREFIX","concat","base","type","yup","string","oneOf","required","relation","test","configurable","boolean","nullable","private","pluginOptions","object","isValidName"],"mappings":"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"}
1
+ {"version":3,"file":"relations.js","sources":["../../../../server/src/controllers/validation/relations.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport type { TestContext, TestFunction } from 'yup';\nimport type { Schema, UID } from '@strapi/types';\nimport { typeKinds, coreUids } from '../../services/constants';\nimport { isValidName } from './common';\n\nconst STRAPI_USER_RELATIONS = ['oneToOne', 'oneToMany'];\n\nconst isValidRelation = (validNatures: ReadonlyArray<string>): TestFunction<string | undefined> =>\n function isValidRelationValue(this: TestContext, value) {\n // NOTE: In case of an undefined value, delegate the check to .required()\n if (value === undefined) {\n return true;\n }\n\n if (this.parent.target === coreUids.STRAPI_USER) {\n if (!validNatures.includes(value) || this.parent.targetAttribute !== undefined) {\n return this.createError({\n path: this.path,\n message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`,\n });\n }\n }\n\n return validNatures.includes(value)\n ? true\n : this.createError({\n path: this.path,\n message: `must be one of the following values: ${validNatures.join(', ')}`,\n });\n };\n\nexport const getRelationValidator = (\n attribute: Schema.Attribute.Relation,\n allowedRelations: ReadonlyArray<string>\n) => {\n const contentTypesUIDs = Object.keys(strapi.contentTypes)\n .filter((key) => strapi.contentTypes[key as UID.ContentType].kind === typeKinds.COLLECTION_TYPE)\n .filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER)\n .concat(['__self__', '__contentType__']);\n\n const base = {\n type: yup.string().oneOf(['relation']).required(),\n relation: yup.string().test('isValidRelation', isValidRelation(allowedRelations)).required(),\n configurable: yup.boolean().nullable(),\n required: yup.boolean(),\n private: yup.boolean().nullable(),\n pluginOptions: yup.object(),\n };\n\n switch (attribute.relation) {\n case 'oneToOne':\n case 'oneToMany':\n case 'manyToOne':\n case 'manyToMany':\n case 'morphOne':\n case 'morphMany': {\n return yup.object({\n ...base,\n target: yup.string().oneOf(contentTypesUIDs).required(),\n targetAttribute: yup.string().test(isValidName).nullable(),\n });\n }\n case 'morphToOne':\n case 'morphToMany':\n default: {\n return yup.object({ ...base });\n }\n }\n};\n"],"names":["STRAPI_USER_RELATIONS","isValidRelation","validNatures","isValidRelationValue","value","undefined","parent","target","coreUids","STRAPI_USER","includes","targetAttribute","createError","path","message","join","getRelationValidator","attribute","allowedRelations","contentTypesUIDs","Object","keys","strapi","contentTypes","filter","key","kind","typeKinds","COLLECTION_TYPE","startsWith","PREFIX","concat","base","type","yup","string","oneOf","required","relation","test","configurable","boolean","nullable","private","pluginOptions","object","isValidName"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { isUndefined } from 'lodash/fp';
2
1
  import { yup } from '@strapi/utils';
3
2
  import { typeKinds, coreUids } from '../../services/constants.mjs';
4
3
  import { isValidName } from './common.mjs';
@@ -13,7 +12,7 @@ const isValidRelation = (validNatures)=>function isValidRelationValue(value) {
13
12
  return true;
14
13
  }
15
14
  if (this.parent.target === coreUids.STRAPI_USER) {
16
- if (!validNatures.includes(value) || !isUndefined(this.parent.targetAttribute)) {
15
+ if (!validNatures.includes(value) || this.parent.targetAttribute !== undefined) {
17
16
  return this.createError({
18
17
  path: this.path,
19
18
  message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`
@@ -1 +1 @@
1
- {"version":3,"file":"relations.mjs","sources":["../../../../server/src/controllers/validation/relations.ts"],"sourcesContent":["import { isUndefined } from 'lodash/fp';\nimport { yup } from '@strapi/utils';\nimport type { TestContext, TestFunction } from 'yup';\nimport type { Schema, UID } from '@strapi/types';\nimport { typeKinds, coreUids } from '../../services/constants';\nimport { isValidName } from './common';\n\nconst STRAPI_USER_RELATIONS = ['oneToOne', 'oneToMany'];\n\nconst isValidRelation = (validNatures: ReadonlyArray<string>): TestFunction<string | undefined> =>\n function isValidRelationValue(this: TestContext, value) {\n // NOTE: In case of an undefined value, delegate the check to .required()\n if (value === undefined) {\n return true;\n }\n\n if (this.parent.target === coreUids.STRAPI_USER) {\n if (!validNatures.includes(value) || !isUndefined(this.parent.targetAttribute)) {\n return this.createError({\n path: this.path,\n message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`,\n });\n }\n }\n\n return validNatures.includes(value)\n ? true\n : this.createError({\n path: this.path,\n message: `must be one of the following values: ${validNatures.join(', ')}`,\n });\n };\n\nexport const getRelationValidator = (\n attribute: Schema.Attribute.Relation,\n allowedRelations: ReadonlyArray<string>\n) => {\n const contentTypesUIDs = Object.keys(strapi.contentTypes)\n .filter((key) => strapi.contentTypes[key as UID.ContentType].kind === typeKinds.COLLECTION_TYPE)\n .filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER)\n .concat(['__self__', '__contentType__']);\n\n const base = {\n type: yup.string().oneOf(['relation']).required(),\n relation: yup.string().test('isValidRelation', isValidRelation(allowedRelations)).required(),\n configurable: yup.boolean().nullable(),\n required: yup.boolean(),\n private: yup.boolean().nullable(),\n pluginOptions: yup.object(),\n };\n\n switch (attribute.relation) {\n case 'oneToOne':\n case 'oneToMany':\n case 'manyToOne':\n case 'manyToMany':\n case 'morphOne':\n case 'morphMany': {\n return yup.object({\n ...base,\n target: yup.string().oneOf(contentTypesUIDs).required(),\n targetAttribute: yup.string().test(isValidName).nullable(),\n });\n }\n case 'morphToOne':\n case 'morphToMany':\n default: {\n return yup.object({ ...base });\n }\n }\n};\n"],"names":["STRAPI_USER_RELATIONS","isValidRelation","validNatures","isValidRelationValue","value","undefined","parent","target","coreUids","STRAPI_USER","includes","isUndefined","targetAttribute","createError","path","message","join","getRelationValidator","attribute","allowedRelations","contentTypesUIDs","Object","keys","strapi","contentTypes","filter","key","kind","typeKinds","COLLECTION_TYPE","startsWith","PREFIX","concat","base","type","yup","string","oneOf","required","relation","test","configurable","boolean","nullable","private","pluginOptions","object","isValidName"],"mappings":"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"}
1
+ {"version":3,"file":"relations.mjs","sources":["../../../../server/src/controllers/validation/relations.ts"],"sourcesContent":["import { yup } from '@strapi/utils';\nimport type { TestContext, TestFunction } from 'yup';\nimport type { Schema, UID } from '@strapi/types';\nimport { typeKinds, coreUids } from '../../services/constants';\nimport { isValidName } from './common';\n\nconst STRAPI_USER_RELATIONS = ['oneToOne', 'oneToMany'];\n\nconst isValidRelation = (validNatures: ReadonlyArray<string>): TestFunction<string | undefined> =>\n function isValidRelationValue(this: TestContext, value) {\n // NOTE: In case of an undefined value, delegate the check to .required()\n if (value === undefined) {\n return true;\n }\n\n if (this.parent.target === coreUids.STRAPI_USER) {\n if (!validNatures.includes(value) || this.parent.targetAttribute !== undefined) {\n return this.createError({\n path: this.path,\n message: `must be one of the following values: ${STRAPI_USER_RELATIONS.join(', ')}`,\n });\n }\n }\n\n return validNatures.includes(value)\n ? true\n : this.createError({\n path: this.path,\n message: `must be one of the following values: ${validNatures.join(', ')}`,\n });\n };\n\nexport const getRelationValidator = (\n attribute: Schema.Attribute.Relation,\n allowedRelations: ReadonlyArray<string>\n) => {\n const contentTypesUIDs = Object.keys(strapi.contentTypes)\n .filter((key) => strapi.contentTypes[key as UID.ContentType].kind === typeKinds.COLLECTION_TYPE)\n .filter((key) => !key.startsWith(coreUids.PREFIX) || key === coreUids.STRAPI_USER)\n .concat(['__self__', '__contentType__']);\n\n const base = {\n type: yup.string().oneOf(['relation']).required(),\n relation: yup.string().test('isValidRelation', isValidRelation(allowedRelations)).required(),\n configurable: yup.boolean().nullable(),\n required: yup.boolean(),\n private: yup.boolean().nullable(),\n pluginOptions: yup.object(),\n };\n\n switch (attribute.relation) {\n case 'oneToOne':\n case 'oneToMany':\n case 'manyToOne':\n case 'manyToMany':\n case 'morphOne':\n case 'morphMany': {\n return yup.object({\n ...base,\n target: yup.string().oneOf(contentTypesUIDs).required(),\n targetAttribute: yup.string().test(isValidName).nullable(),\n });\n }\n case 'morphToOne':\n case 'morphToMany':\n default: {\n return yup.object({ ...base });\n }\n }\n};\n"],"names":["STRAPI_USER_RELATIONS","isValidRelation","validNatures","isValidRelationValue","value","undefined","parent","target","coreUids","STRAPI_USER","includes","targetAttribute","createError","path","message","join","getRelationValidator","attribute","allowedRelations","contentTypesUIDs","Object","keys","strapi","contentTypes","filter","key","kind","typeKinds","COLLECTION_TYPE","startsWith","PREFIX","concat","base","type","yup","string","oneOf","required","relation","test","configurable","boolean","nullable","private","pluginOptions","object","isValidName"],"mappings":"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"}
@@ -2,9 +2,10 @@
2
2
 
3
3
  var z = require('zod/v4');
4
4
  var utils = require('@strapi/utils');
5
- var fp = require('lodash/fp');
5
+ var _ = require('lodash');
6
6
  var builder = require('../../services/builder.js');
7
7
  var constants = require('../../services/constants.js');
8
+ var attributes = require('../../utils/attributes.js');
8
9
  var common = require('./common.js');
9
10
  var contentStructure = require('./content-structure.js');
10
11
 
@@ -29,7 +30,7 @@ function _interopNamespace(e) {
29
30
  var z__namespace = /*#__PURE__*/_interopNamespace(z);
30
31
 
31
32
  const uniqueAttributeName = (attributes, ctx)=>{
32
- const names = new Set(attributes.map((attribute)=>fp.snakeCase(attribute.name)));
33
+ const names = new Set(attributes.map((attribute)=>_.snakeCase(attribute.name)));
33
34
  if (names.size !== attributes.length) {
34
35
  ctx.addIssue({
35
36
  code: 'custom',
@@ -77,20 +78,19 @@ const verifySingularAndPluralNames = (obj, ctx)=>{
77
78
  });
78
79
  }
79
80
  };
81
+ // The payload carries the full attribute list and the saved schema is built from
82
+ // it, so the names that will be written are the payload attributes that are not
83
+ // deleted. The rename hops do not decide what is saved. The one exception is
84
+ // non-configurable attributes: the builder keeps them from the current schema
85
+ // whatever the payload says, so they are always part of the saved schema.
80
86
  const getEffectiveAttributeNames = (contentType)=>{
81
- if (contentType.action === 'create') {
82
- return (contentType.attributes ?? []).filter((attribute)=>attribute.action !== 'delete').map((attribute)=>attribute.name);
83
- }
84
- if (contentType.action !== 'update' || !contentType.uid) {
85
- return [];
86
- }
87
- const existingContentType = strapi.contentTypes[contentType.uid];
88
- const names = new Set(existingContentType ? Object.keys(existingContentType.attributes) : []);
89
- for (const attribute of contentType.attributes ?? []){
90
- if (attribute.action === 'delete') {
91
- names.delete(attribute.name);
92
- } else if (attribute.action === 'create') {
93
- names.add(attribute.name);
87
+ const names = new Set((contentType.attributes ?? []).filter((attribute)=>attribute.action !== 'delete').map((attribute)=>attribute.name));
88
+ if (contentType.action === 'update' && contentType.uid) {
89
+ const existingAttributes = strapi.contentTypes[contentType.uid]?.attributes ?? {};
90
+ for (const [name, attribute] of Object.entries(existingAttributes)){
91
+ if (!attributes.isConfigurable(attribute)) {
92
+ names.add(name);
93
+ }
94
94
  }
95
95
  }
96
96
  return [
@@ -113,7 +113,7 @@ const verifyDraftAndPublishReservedAttributes = (contentType, ctx)=>{
113
113
  }
114
114
  };
115
115
  const maxLengthGreaterThanMinLength = (value, ctx)=>{
116
- if (!fp.isNil(value.maxLength) && !fp.isNil(value.minLength) && fp.isNumber(value.maxLength) && fp.isNumber(value.minLength)) {
116
+ if (value.maxLength != null && value.minLength != null && _.isNumber(value.maxLength) && _.isNumber(value.minLength)) {
117
117
  if (value.maxLength < value.minLength) {
118
118
  ctx.addIssue({
119
119
  code: 'custom',
@@ -126,7 +126,7 @@ const maxLengthGreaterThanMinLength = (value, ctx)=>{
126
126
  }
127
127
  };
128
128
  const maxGreaterThanMin = (value, ctx)=>{
129
- if (!fp.isNil(value.max) && !fp.isNil(value.min) && fp.isNumber(value.max) && fp.isNumber(value.min)) {
129
+ if (value.max != null && value.min != null && _.isNumber(value.max) && _.isNumber(value.min)) {
130
130
  if (value.max < value.min) {
131
131
  ctx.addIssue({
132
132
  code: 'custom',
@@ -142,11 +142,11 @@ const checkUserTarget = (value, ctx)=>{
142
142
  if (value.type !== 'relation') {
143
143
  return;
144
144
  }
145
- if (fp.isUndefined(value.target) || fp.isUndefined(value.relation)) {
145
+ if (value.target === undefined || value.relation === undefined) {
146
146
  return;
147
147
  }
148
148
  const { target, relation, targetAttribute } = value;
149
- if (target === constants.coreUids.STRAPI_USER && (!STRAPI_USER_RELATIONS.includes(relation) || !fp.isUndefined(targetAttribute))) {
149
+ if (target === constants.coreUids.STRAPI_USER && (!STRAPI_USER_RELATIONS.includes(relation) || targetAttribute !== undefined)) {
150
150
  ctx.addIssue({
151
151
  code: 'custom',
152
152
  path: [
@@ -157,7 +157,7 @@ const checkUserTarget = (value, ctx)=>{
157
157
  }
158
158
  };
159
159
  const uidRefinement = (value, ctx)=>{
160
- if (!fp.isNil(value.targetField) && !fp.isNil(value.default)) {
160
+ if (value.targetField != null && value.default != null) {
161
161
  ctx.addIssue({
162
162
  code: 'custom',
163
163
  message: 'Cannot define a default UID if the targetField is set',
@@ -168,7 +168,7 @@ const uidRefinement = (value, ctx)=>{
168
168
  }
169
169
  };
170
170
  const enumRefinement = (value, ctx)=>{
171
- if (value.type === 'enumeration' && !fp.isNil(value.default) && !fp.isNil(value.enum)) {
171
+ if (value.type === 'enumeration' && value.default != null && value.enum != null) {
172
172
  if (value.default === '' || !value.enum.some((v)=>v === value.default)) {
173
173
  ctx.addIssue({
174
174
  code: 'custom',
@@ -348,7 +348,7 @@ const jsonSchema = basePropertiesSchema.extend({
348
348
  if (value === undefined) {
349
349
  return true;
350
350
  }
351
- if (fp.isNumber(value) || fp.isNull(value) || fp.isObject(value) || fp.isArray(value)) {
351
+ if (_.isNumber(value) || value === null || _.isObject(value) || Array.isArray(value)) {
352
352
  return true;
353
353
  }
354
354
  try {
@@ -547,6 +547,23 @@ const updateAttributeSchema = (meta)=>z__namespace.object({
547
547
  name: z__namespace.string(),
548
548
  properties: attributePropertiesSchema(meta)
549
549
  });
550
+ // Ordered list of attribute rename hops performed by the user for a given
551
+ // content-type / component, used to generate a data-preserving rename migration.
552
+ // The order is significant: the migration replays each hop verbatim.
553
+ // Names end up in generated migration code, so they are held to the attribute
554
+ // name rules. The new name is held to the same reserved-name rules as a newly
555
+ // created attribute. The old name only has to be a name an attribute can
556
+ // legally carry today: `status` is reserved only while draft and publish is
557
+ // enabled, and renaming it away is exactly how a type gets to enable it.
558
+ const renameHopNameSchema = z__namespace.string().min(1).max(64).regex(common.NAME_REGEX);
559
+ const renameHopOldNameSchema = renameHopNameSchema.refine((value)=>!utils.contentTypes.isReservedAttributeName(value, {
560
+ draftAndPublish: false
561
+ }), 'Attribute name is reserved');
562
+ const renameHopNewNameSchema = renameHopNameSchema.refine((value)=>!builder.isReservedAttributeName(value), 'Attribute name is reserved');
563
+ const renamesSchema = z__namespace.array(z__namespace.object({
564
+ oldName: renameHopOldNameSchema,
565
+ newName: renameHopNewNameSchema
566
+ }).refine((hop)=>hop.oldName !== hop.newName, 'A rename must change the attribute name')).max(200).optional();
550
567
  const deleteAttributeSchema = z__namespace.object({
551
568
  action: z__namespace.literal('delete'),
552
569
  name: z__namespace.string()
@@ -576,6 +593,7 @@ const createComponentSchema = baseComponentSchema.extend({
576
593
  const updateComponentSchema = baseComponentSchema.extend({
577
594
  action: z__namespace.literal('update'),
578
595
  category: categorySchema.optional(),
596
+ renames: renamesSchema,
579
597
  attributes: z__namespace.array(z__namespace.discriminatedUnion('action', [
580
598
  createAttributeSchema({
581
599
  modelType: 'component'
@@ -625,7 +643,8 @@ const createCollectionTypeSchema = baseCreateContentTypeSchema.extend({
625
643
  })).superRefine(uniqueAttributeName).superRefine(verifyUidTargetField)
626
644
  });
627
645
  const baseUpdateContentTypeSchema = baseContentTypeSchema.extend({
628
- action: z__namespace.literal('update')
646
+ action: z__namespace.literal('update'),
647
+ renames: renamesSchema
629
648
  });
630
649
  const updateSingleTypeSchema = baseUpdateContentTypeSchema.extend({
631
650
  kind: z__namespace.literal(constants.typeKinds.SINGLE_TYPE).optional(),