register-custom-entity-fields.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { CustomFieldType } from '@vendure/common/lib/shared-types';
  2. import { assertNever } from '@vendure/common/lib/shared-utils';
  3. import { Column, ColumnOptions, ColumnType, ConnectionOptions } from 'typeorm';
  4. import { DateUtils } from 'typeorm/util/DateUtils';
  5. import { CustomFields } from '../config/custom-field/custom-field-types';
  6. import { Logger } from '../config/logger/vendure-logger';
  7. import { VendureConfig } from '../config/vendure-config';
  8. import {
  9. CustomAddressFields,
  10. CustomCollectionFields,
  11. CustomCollectionFieldsTranslation,
  12. CustomCustomerFields,
  13. CustomFacetFields,
  14. CustomFacetFieldsTranslation,
  15. CustomFacetValueFields,
  16. CustomFacetValueFieldsTranslation,
  17. CustomGlobalSettingsFields,
  18. CustomOrderFields,
  19. CustomOrderLineFields,
  20. CustomProductFields,
  21. CustomProductFieldsTranslation,
  22. CustomProductOptionFields,
  23. CustomProductOptionFieldsTranslation,
  24. CustomProductOptionGroupFields,
  25. CustomProductOptionGroupFieldsTranslation,
  26. CustomProductVariantFields,
  27. CustomProductVariantFieldsTranslation,
  28. CustomUserFields,
  29. } from './custom-entity-fields';
  30. /**
  31. * The maximum length of the "length" argument of a MySQL varchar column.
  32. */
  33. const MAX_STRING_LENGTH = 65535;
  34. /**
  35. * Dynamically add columns to the custom field entity based on the CustomFields config.
  36. */
  37. function registerCustomFieldsForEntity(
  38. config: VendureConfig,
  39. entityName: keyof CustomFields,
  40. // tslint:disable-next-line:callable-types
  41. ctor: { new (): any },
  42. translation = false,
  43. ) {
  44. const customFields = config.customFields && config.customFields[entityName];
  45. const dbEngine = config.dbConnectionOptions.type;
  46. if (customFields) {
  47. for (const customField of customFields) {
  48. const { name, type, defaultValue, nullable } = customField;
  49. const registerColumn = () => {
  50. const options: ColumnOptions = {
  51. type: getColumnType(dbEngine, type),
  52. default:
  53. type === 'datetime' ? formatDefaultDatetime(dbEngine, defaultValue) : defaultValue,
  54. name,
  55. nullable: nullable === false ? false : true,
  56. };
  57. if (customField.type === 'string') {
  58. const length = customField.length || 255;
  59. if (MAX_STRING_LENGTH < length) {
  60. throw new Error(
  61. `ERROR: The "length" property of the custom field "${
  62. customField.name
  63. }" is greater than the maximum allowed value of ${MAX_STRING_LENGTH}`,
  64. );
  65. }
  66. options.length = length;
  67. }
  68. if (
  69. customField.type === 'datetime' &&
  70. options.precision == null &&
  71. // Setting precision on an sqlite datetime will cause
  72. // spurious migration commands. See https://github.com/typeorm/typeorm/issues/2333
  73. dbEngine !== 'sqljs' &&
  74. dbEngine !== 'sqlite'
  75. ) {
  76. options.precision = 6;
  77. }
  78. Column(options)(new ctor(), name);
  79. };
  80. if (translation) {
  81. if (type === 'localeString') {
  82. registerColumn();
  83. }
  84. } else {
  85. if (type !== 'localeString') {
  86. registerColumn();
  87. }
  88. }
  89. }
  90. }
  91. }
  92. function formatDefaultDatetime(dbEngine: ConnectionOptions['type'], datetime: any): Date | string {
  93. if (!datetime) {
  94. return datetime;
  95. }
  96. switch (dbEngine) {
  97. case 'sqlite':
  98. case 'sqljs':
  99. return DateUtils.mixedDateToUtcDatetimeString(datetime);
  100. case 'mysql':
  101. case 'postgres':
  102. default:
  103. return DateUtils.mixedDateToUtcDatetimeString(datetime);
  104. // return DateUtils.mixedDateToDate(datetime, true, true);
  105. }
  106. }
  107. function getColumnType(dbEngine: ConnectionOptions['type'], type: CustomFieldType): ColumnType {
  108. switch (type) {
  109. case 'string':
  110. case 'localeString':
  111. return 'varchar';
  112. case 'boolean':
  113. switch (dbEngine) {
  114. case 'mysql':
  115. return 'tinyint';
  116. case 'postgres':
  117. return 'bool';
  118. case 'sqlite':
  119. case 'sqljs':
  120. default:
  121. return 'boolean';
  122. }
  123. case 'int':
  124. return 'int';
  125. case 'float':
  126. return 'double precision';
  127. case 'datetime':
  128. switch (dbEngine) {
  129. case 'postgres':
  130. return 'timestamp';
  131. case 'mysql':
  132. case 'sqlite':
  133. case 'sqljs':
  134. default:
  135. return 'datetime';
  136. }
  137. default:
  138. assertNever(type);
  139. }
  140. return 'varchar';
  141. }
  142. /**
  143. * Dynamically registers any custom fields with TypeORM. This function should be run at the bootstrap
  144. * stage of the app lifecycle, before the AppModule is initialized.
  145. */
  146. export function registerCustomEntityFields(config: VendureConfig) {
  147. registerCustomFieldsForEntity(config, 'Address', CustomAddressFields);
  148. registerCustomFieldsForEntity(config, 'Collection', CustomCollectionFields);
  149. registerCustomFieldsForEntity(config, 'Collection', CustomCollectionFieldsTranslation, true);
  150. registerCustomFieldsForEntity(config, 'Customer', CustomCustomerFields);
  151. registerCustomFieldsForEntity(config, 'Facet', CustomFacetFields);
  152. registerCustomFieldsForEntity(config, 'Facet', CustomFacetFieldsTranslation, true);
  153. registerCustomFieldsForEntity(config, 'FacetValue', CustomFacetValueFields);
  154. registerCustomFieldsForEntity(config, 'FacetValue', CustomFacetValueFieldsTranslation, true);
  155. registerCustomFieldsForEntity(config, 'Order', CustomOrderFields);
  156. registerCustomFieldsForEntity(config, 'OrderLine', CustomOrderLineFields);
  157. registerCustomFieldsForEntity(config, 'Product', CustomProductFields);
  158. registerCustomFieldsForEntity(config, 'Product', CustomProductFieldsTranslation, true);
  159. registerCustomFieldsForEntity(config, 'ProductOption', CustomProductOptionFields);
  160. registerCustomFieldsForEntity(config, 'ProductOption', CustomProductOptionFieldsTranslation, true);
  161. registerCustomFieldsForEntity(config, 'ProductOptionGroup', CustomProductOptionGroupFields);
  162. registerCustomFieldsForEntity(
  163. config,
  164. 'ProductOptionGroup',
  165. CustomProductOptionGroupFieldsTranslation,
  166. true,
  167. );
  168. registerCustomFieldsForEntity(config, 'ProductVariant', CustomProductVariantFields);
  169. registerCustomFieldsForEntity(config, 'ProductVariant', CustomProductVariantFieldsTranslation, true);
  170. registerCustomFieldsForEntity(config, 'User', CustomUserFields);
  171. registerCustomFieldsForEntity(config, 'GlobalSettings', CustomGlobalSettingsFields);
  172. }