@dynamatix/cat-shared 0.0.17 → 0.0.19

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.
@@ -1,11 +1,32 @@
1
1
  import AuditConfigModel from '../models/audit-config.model.js';
2
2
  import AuditLog from '../models/audit.model.js';
3
+ import ValueReferenceMap from '../models/value-reference-map.model.js';
3
4
  import { getContext } from '../services/request-context.service.js';
5
+ import mongoose from 'mongoose';
4
6
 
5
7
  let onAuditLogCreated = null;
6
8
 
9
+ // Generic resolver: fetch display values using ValueReferenceMap
10
+ async function resolveAuditValues(field, oldValue, newValue) {
11
+ const map = await ValueReferenceMap.findOne({ field });
12
+ if (!map || (!oldValue && !newValue)) return { oldValue, newValue };
13
+
14
+ const Model = mongoose.models[map.model];
15
+ if (!Model) return { oldValue, newValue };
16
+
17
+ const [oldDoc, newDoc] = await Promise.all([
18
+ oldValue ? Model.findById(oldValue).lean() : null,
19
+ newValue ? Model.findById(newValue).lean() : null
20
+ ]);
21
+
22
+ return {
23
+ oldValue: oldDoc?.[map.displayField] || oldValue,
24
+ newValue: newDoc?.[map.displayField] || newValue
25
+ };
26
+ }
27
+
7
28
  function applyAuditMiddleware(schema, collectionName) {
8
- // Handle create (after save)
29
+ // Handle creation audit
9
30
  schema.post('save', async function (doc) {
10
31
  const auditConfig = await AuditConfigModel.findOne({ collectionName });
11
32
  if (!auditConfig?.trackCreation) return;
@@ -30,13 +51,13 @@ function applyAuditMiddleware(schema, collectionName) {
30
51
  }
31
52
  });
32
53
 
33
- // Capture original doc before update
54
+ // Capture the original doc before update
34
55
  schema.pre(['findOneAndUpdate', 'findByIdAndUpdate'], async function (next) {
35
56
  this._originalDoc = await this.model.findOne(this.getQuery()).lean();
36
57
  next();
37
58
  });
38
59
 
39
- // Handle updates (after successful update)
60
+ // Handle update audits
40
61
  schema.post(['findOneAndUpdate', 'findByIdAndUpdate'], async function (result) {
41
62
  if (!result || !this._originalDoc) return;
42
63
 
@@ -50,16 +71,23 @@ function applyAuditMiddleware(schema, collectionName) {
50
71
  const contextId = context?.contextId;
51
72
 
52
73
  for (const field of auditConfig.fields) {
53
- if (update?.$set?.hasOwnProperty(field) || update?.hasOwnProperty(field)) {
74
+ const hasChanged =
75
+ update?.$set?.hasOwnProperty(field) || update?.hasOwnProperty(field);
76
+
77
+ if (hasChanged) {
54
78
  const newValue = update?.$set?.[field] ?? update?.[field];
55
79
  const oldValue = this._originalDoc[field];
56
80
 
57
81
  if (oldValue !== newValue) {
82
+ // Resolve human-readable values
83
+ const { oldValue: resolvedOld, newValue: resolvedNew } =
84
+ await resolveAuditValues(field, oldValue, newValue);
85
+
58
86
  logs.push({
59
87
  name: `${collectionName}.${field}`,
60
88
  recordId: result._id,
61
- oldValue,
62
- newValue,
89
+ oldValue: resolvedOld,
90
+ newValue: resolvedNew,
63
91
  createdBy: userId,
64
92
  contextId
65
93
  });
@@ -0,0 +1,158 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const formfieldSchema = new mongoose.Schema({
4
+ fieldName: {
5
+ type: String,
6
+ required: true,
7
+ trim: true
8
+ },
9
+ isEditable:{
10
+ type: Boolean,
11
+ default: true
12
+ },
13
+ size: {
14
+ type: Number,
15
+ default: 6,
16
+ enum: [3,4,6,8,10,12],
17
+ },
18
+ layout:{
19
+ type: String,
20
+ enum: ['horizontal', 'vertical'],
21
+ },
22
+ label: {
23
+ type: String,
24
+ required: true,
25
+ trim: true
26
+ },
27
+ dataType: {
28
+ type: String,
29
+ required: true,
30
+ enum: ['string', 'number', 'boolean', 'date', 'array', 'object', 'select', 'radio', 'list'] // Added common form field types
31
+ },
32
+ isRequired: {
33
+ type: Boolean,
34
+ default: false
35
+ },
36
+ helpText: {
37
+ type: String,
38
+ default: null
39
+ },
40
+ requiredErrorMessage:{
41
+ type: String,
42
+ default: 'Field is required'
43
+ },
44
+ defaultValue: {
45
+ type: mongoose.Schema.Types.Mixed
46
+ },
47
+ validationRules: {
48
+ type: mongoose.Schema.Types.Mixed,
49
+ default: {}
50
+ },
51
+ visibilityCondition: {
52
+ type: mongoose.Schema.Types.Mixed,
53
+ default: null
54
+ },
55
+ options: {
56
+ type: [mongoose.Schema.Types.Mixed],
57
+ default: []
58
+ },
59
+ section: {
60
+ type: String,
61
+ default: null
62
+ },
63
+ list:{
64
+ type: {
65
+ name: {
66
+ type: String,
67
+ required: true
68
+ },
69
+ label: {
70
+ type: String,
71
+ required: true
72
+ },
73
+ value: {
74
+ type: String,
75
+ required: true
76
+ },
77
+ optionFilterLogic: {
78
+ type: mongoose.Schema.Types.Mixed,
79
+ default: null
80
+ }
81
+ },
82
+ default: null
83
+ },
84
+ isSyncEnabled: { // Fixed typo (was isSyncEnabed)
85
+ type: Boolean,
86
+ default: true
87
+ },
88
+ dataElement: { //<collectionName>.<fieldName>
89
+ type: String,
90
+ default: null
91
+ },
92
+ syncTargetDataElement: { //<collectionName>.<fieldName> of Apprivo
93
+ type: String,
94
+ default: null
95
+ },
96
+ order: {
97
+ type: Number,
98
+ default: 0
99
+ },
100
+ maxLength: {
101
+ type: Number,
102
+ default: null,
103
+ min: 0
104
+ }
105
+ }, { timestamps: true });
106
+
107
+ const formConfigurationSchema = new mongoose.Schema({
108
+ formName: {
109
+ type: String,
110
+ required: true,
111
+ trim: true,
112
+ unique: true // Consider making form names unique
113
+ },
114
+ apiPath: {
115
+ type: String,
116
+ required: true,
117
+ trim: true
118
+ },
119
+ visibilityCondition: {
120
+ type: mongoose.Schema.Types.Mixed,
121
+ default: null
122
+ },
123
+ sections: [{
124
+ sectionName: {
125
+ type: String,
126
+ required: true,
127
+ trim: true
128
+ },
129
+ sectionLabel: {
130
+ type: String,
131
+ required: true,
132
+ trim: true
133
+ },
134
+ fields: [{
135
+ type: [formfieldSchema]
136
+ }],
137
+ order: {
138
+ type: Number,
139
+ default: 0
140
+ },
141
+ visibilityCondition: { // Added section-level visibility
142
+ type: mongoose.Schema.Types.Mixed,
143
+ default: null
144
+ }
145
+ }],
146
+ isActive: { // Consider adding status flag
147
+ type: Boolean,
148
+ default: true
149
+ },
150
+ version: { // Consider adding versioning
151
+ type: Number,
152
+ default: 1
153
+ }
154
+ }, { timestamps: true });
155
+
156
+ const FormConfiguration = mongoose.model('FormConfiguration', formConfigurationSchema);
157
+
158
+ module.exports = FormConfiguration;
package/models/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default as AuditConfigModel } from './audit-config.model.js';
2
- export { default as AuditModel } from './audit.model.js';
2
+ export { default as AuditModel } from './audit.model.js';
3
+ export { default as ValueReferenceMapModel } from './value-reference-map.model.js';
@@ -0,0 +1,12 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const valueReferenceMapSchema = new mongoose.Schema({
4
+ field: String, // e.g. 'documentTypeId', 'createdBy', 'status'
5
+ model: String, // e.g. 'DocumentType', 'User', 'ApplicationStatus'
6
+ displayField: String, // e.g. 'name', 'fullName', 'label'
7
+ });
8
+
9
+ valueReferenceMapSchema.index({ field: 1 }, { unique: true });
10
+
11
+ const ValueReferenceMapModel = mongoose.model('ValueReferenceMap', valueReferenceMapSchema);
12
+ export default ValueReferenceMapModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamatix/cat-shared",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -13,5 +13,9 @@
13
13
  "./models": "./models/index.js",
14
14
  "./middlewares": "./middlewares/index.js",
15
15
  "./services": "./services/index.js"
16
+ },
17
+ "dependencies": {
18
+ "dotenv": "^16.4.7",
19
+ "mongoose": "^8.13.1"
16
20
  }
17
21
  }
@@ -0,0 +1,66 @@
1
+ import mongoose, { model } from 'mongoose';
2
+ import dotenv from 'dotenv';
3
+ import ValueReferenceMapModel from '../models/value-reference-map.model.js';
4
+
5
+ dotenv.config();
6
+
7
+ const MONGO_URI = 'mongodb+srv://qa-user:vw4sIy3lZpyTPoX0@gatehouse-testing.e08r2.mongodb.net/gatehouse-qa?retryWrites=true&w=majority';
8
+
9
+ // Grouped definition (DRY)
10
+ const groupedMappings = [
11
+ {
12
+ model: 'DocumentType',
13
+ displayField: 'name',
14
+ fields: ['documentTypeId']
15
+ },
16
+ {
17
+ model: 'User',
18
+ displayField: 'fullName',
19
+ fields: ['createdBy', 'updatedBy', 'ownerId','documentOwnerId']
20
+ },
21
+ {
22
+ model: 'Applicant',
23
+ displayField: 'firstName',
24
+ fields: ['documentOwnerId']
25
+ }
26
+ ];
27
+
28
+ // Flatten the mappings
29
+ const referenceMappings = groupedMappings.flatMap(group =>
30
+ group.fields.map(field => ({
31
+ field,
32
+ model: group.model,
33
+ displayField: group.displayField
34
+ }))
35
+ );
36
+
37
+ async function seed() {
38
+ try {
39
+ console.log(MONGO_URI);
40
+ await mongoose.connect(MONGO_URI);
41
+ console.log('āœ… Connected to MongoDB');
42
+
43
+ for (const mapping of referenceMappings) {
44
+ const existing = await ValueReferenceMapModel.findOne({ field: mapping.field });
45
+ if (existing) {
46
+ await ValueReferenceMapModel.updateOne(
47
+ { field: mapping.field },
48
+ { $set: mapping }
49
+ );
50
+ console.log(`šŸ”„ Updated mapping for '${mapping.field}'`);
51
+ } else {
52
+ await ValueReferenceMapModel.create(mapping);
53
+ console.log(`āž• Inserted mapping for '${mapping.field}'`);
54
+ }
55
+ }
56
+
57
+ console.log('\n🌱 Seeding complete āœ…');
58
+ } catch (err) {
59
+ console.error('āŒ Seeding failed', err);
60
+ } finally {
61
+ await mongoose.disconnect();
62
+ process.exit();
63
+ }
64
+ }
65
+
66
+ seed();