MetadataStorage.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { ValidationSchemaToMetadataTransformer } from '../validation-schema/ValidationSchemaToMetadataTransformer';
  2. import { getGlobal } from '../utils';
  3. /**
  4. * Storage all metadatas.
  5. */
  6. export class MetadataStorage {
  7. constructor() {
  8. // -------------------------------------------------------------------------
  9. // Private properties
  10. // -------------------------------------------------------------------------
  11. this.validationMetadatas = new Map();
  12. this.constraintMetadatas = new Map();
  13. }
  14. get hasValidationMetaData() {
  15. return !!this.validationMetadatas.size;
  16. }
  17. // -------------------------------------------------------------------------
  18. // Public Methods
  19. // -------------------------------------------------------------------------
  20. /**
  21. * Adds a new validation metadata.
  22. */
  23. addValidationSchema(schema) {
  24. const validationMetadatas = new ValidationSchemaToMetadataTransformer().transform(schema);
  25. validationMetadatas.forEach(validationMetadata => this.addValidationMetadata(validationMetadata));
  26. }
  27. /**
  28. * Adds a new validation metadata.
  29. */
  30. addValidationMetadata(metadata) {
  31. const existingMetadata = this.validationMetadatas.get(metadata.target);
  32. if (existingMetadata) {
  33. existingMetadata.push(metadata);
  34. }
  35. else {
  36. this.validationMetadatas.set(metadata.target, [metadata]);
  37. }
  38. }
  39. /**
  40. * Adds a new constraint metadata.
  41. */
  42. addConstraintMetadata(metadata) {
  43. const existingMetadata = this.constraintMetadatas.get(metadata.target);
  44. if (existingMetadata) {
  45. existingMetadata.push(metadata);
  46. }
  47. else {
  48. this.constraintMetadatas.set(metadata.target, [metadata]);
  49. }
  50. }
  51. /**
  52. * Groups metadata by their property names.
  53. */
  54. groupByPropertyName(metadata) {
  55. const grouped = {};
  56. metadata.forEach(metadata => {
  57. if (!grouped[metadata.propertyName])
  58. grouped[metadata.propertyName] = [];
  59. grouped[metadata.propertyName].push(metadata);
  60. });
  61. return grouped;
  62. }
  63. /**
  64. * Gets all validation metadatas for the given object with the given groups.
  65. */
  66. getTargetValidationMetadatas(targetConstructor, targetSchema, always, strictGroups, groups) {
  67. const includeMetadataBecauseOfAlwaysOption = (metadata) => {
  68. // `metadata.always` overrides global default.
  69. if (typeof metadata.always !== 'undefined')
  70. return metadata.always;
  71. // `metadata.groups` overrides global default.
  72. if (metadata.groups && metadata.groups.length)
  73. return false;
  74. // Use global default.
  75. return always;
  76. };
  77. const excludeMetadataBecauseOfStrictGroupsOption = (metadata) => {
  78. if (strictGroups) {
  79. // Validation is not using groups.
  80. if (!groups || !groups.length) {
  81. // `metadata.groups` has at least one group.
  82. if (metadata.groups && metadata.groups.length)
  83. return true;
  84. }
  85. }
  86. return false;
  87. };
  88. // get directly related to a target metadatas
  89. const filteredForOriginalMetadatasSearch = this.validationMetadatas.get(targetConstructor) || [];
  90. const originalMetadatas = filteredForOriginalMetadatasSearch.filter(metadata => {
  91. if (metadata.target !== targetConstructor && metadata.target !== targetSchema)
  92. return false;
  93. if (includeMetadataBecauseOfAlwaysOption(metadata))
  94. return true;
  95. if (excludeMetadataBecauseOfStrictGroupsOption(metadata))
  96. return false;
  97. if (groups && groups.length > 0)
  98. return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1);
  99. return true;
  100. });
  101. // get metadatas for inherited classes
  102. const filteredForInheritedMetadatasSearch = [];
  103. for (const [key, value] of this.validationMetadatas.entries()) {
  104. if (targetConstructor.prototype instanceof key) {
  105. filteredForInheritedMetadatasSearch.push(...value);
  106. }
  107. }
  108. const inheritedMetadatas = filteredForInheritedMetadatasSearch.filter(metadata => {
  109. // if target is a string it's means we validate against a schema, and there is no inheritance support for schemas
  110. if (typeof metadata.target === 'string')
  111. return false;
  112. if (metadata.target === targetConstructor)
  113. return false;
  114. if (metadata.target instanceof Function && !(targetConstructor.prototype instanceof metadata.target))
  115. return false;
  116. if (includeMetadataBecauseOfAlwaysOption(metadata))
  117. return true;
  118. if (excludeMetadataBecauseOfStrictGroupsOption(metadata))
  119. return false;
  120. if (groups && groups.length > 0)
  121. return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1);
  122. return true;
  123. });
  124. // filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas
  125. const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => {
  126. return !originalMetadatas.find(originalMetadata => {
  127. return (originalMetadata.propertyName === inheritedMetadata.propertyName &&
  128. originalMetadata.type === inheritedMetadata.type);
  129. });
  130. });
  131. return originalMetadatas.concat(uniqueInheritedMetadatas);
  132. }
  133. /**
  134. * Gets all validator constraints for the given object.
  135. */
  136. getTargetValidatorConstraints(target) {
  137. return this.constraintMetadatas.get(target) || [];
  138. }
  139. }
  140. /**
  141. * Gets metadata storage.
  142. * Metadata storage follows the best practices and stores metadata in a global variable.
  143. */
  144. export function getMetadataStorage() {
  145. const global = getGlobal();
  146. if (!global.classValidatorMetadataStorage) {
  147. global.classValidatorMetadataStorage = new MetadataStorage();
  148. }
  149. return global.classValidatorMetadataStorage;
  150. }
  151. //# sourceMappingURL=MetadataStorage.js.map