@autofleet/sadot 1.2.13 → 1.2.14-beta-ed773b95.1

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,2 +1,2 @@
1
- const e=require(`../../../_virtual/rolldown_runtime.cjs`);let t=require(`joi`);t=e.__toESM(t);const n=t.default.object().unknown(!0),r=t.default.object({type:t.default.string().valid(`object`),properties:t.default.object({before:n,after:n}).required(),required:t.default.array().items(t.default.string()),allOf:t.default.array().items(t.default.object()),anyOf:t.default.array().items(t.default.object()),oneOf:t.default.array().items(t.default.object()),additionalProperties:t.default.alternatives().try(t.default.boolean(),t.default.object()),$id:t.default.string(),$schema:t.default.string(),if:t.default.object(),then:t.default.object(),else:t.default.object()}),i={create:t.default.object({entityId:t.default.string().uuid().required(),entityType:t.default.string().required(),schema:r.required()}),update:t.default.object({entityId:t.default.string().uuid(),entityType:t.default.string(),schema:r,disabled:t.default.boolean()}).min(1)};var a=i;exports.default=a;
1
+ const e=require(`../../../_virtual/rolldown_runtime.cjs`);let t=require(`joi`);t=e.__toESM(t);const n=t.default.object().unknown(!0),r=t.default.object({name:t.default.string().required(),model:t.default.string().required(),foreignKey:t.default.string().required(),localKey:t.default.string(),type:t.default.string().valid(`hasMany`,`hasOne`,`belongsTo`).required(),attributes:t.default.array().items(t.default.string())}),i=t.default.object({type:t.default.string().valid(`object`),properties:t.default.object({before:n,after:n}).required(),required:t.default.array().items(t.default.string()),relations:t.default.array().items(r),allOf:t.default.array().items(t.default.object()),anyOf:t.default.array().items(t.default.object()),oneOf:t.default.array().items(t.default.object()),additionalProperties:t.default.alternatives().try(t.default.boolean(),t.default.object()),$id:t.default.string(),$schema:t.default.string(),if:t.default.object(),then:t.default.object(),else:t.default.object()}),a={create:t.default.object({entityId:t.default.string().uuid().required(),entityType:t.default.string().required(),schema:i.required()}),update:t.default.object({entityId:t.default.string().uuid(),entityType:t.default.string(),schema:i,disabled:t.default.boolean()}).min(1)};var o=a;exports.default=o;
2
2
  //# sourceMappingURL=validations.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validations.cjs","names":["Joi","validationSchemas: ValidationSchemas"],"sources":["../../../../src/api/v1/validator/validations.ts"],"sourcesContent":["import Joi from 'joi';\n\nconst jsonSchemaValidation = Joi.object().unknown(true);\n\nexport interface SchemaObject {\n type: 'object';\n properties?: {\n before?: object;\n after?: object;\n };\n required?: string[];\n allOf?: object[];\n anyOf?: object[];\n oneOf?: object[];\n additionalProperties?: boolean | object;\n $id?: string;\n $schema?: string;\n if?: object;\n then?: object;\n else?: object;\n}\n\nconst schemaObject = Joi.object<SchemaObject>({\n type: Joi.string().valid('object'),\n properties: Joi.object({\n before: jsonSchemaValidation,\n after: jsonSchemaValidation,\n }).required(),\n required: Joi.array().items(Joi.string()),\n allOf: Joi.array().items(Joi.object()),\n anyOf: Joi.array().items(Joi.object()),\n oneOf: Joi.array().items(Joi.object()),\n additionalProperties: Joi.alternatives().try(Joi.boolean(), Joi.object()),\n $id: Joi.string(),\n $schema: Joi.string(),\n if: Joi.object(),\n then: Joi.object(),\n else: Joi.object(),\n});\n\nexport interface CreateValidatorPayload {\n entityId: string;\n entityType: string;\n schema: SchemaObject;\n}\n\nexport interface UpdateValidatorPayload extends Partial<CreateValidatorPayload> {\n disabled?: boolean;\n}\n\ninterface ValidationSchemas {\n create: Joi.ObjectSchema<CreateValidatorPayload>;\n update: Joi.ObjectSchema<UpdateValidatorPayload>;\n}\n\nconst validationSchemas: ValidationSchemas = {\n create: Joi.object<CreateValidatorPayload>({\n entityId: Joi.string().uuid().required(),\n entityType: Joi.string().required(),\n schema: schemaObject.required(),\n }),\n\n update: Joi.object<UpdateValidatorPayload>({\n entityId: Joi.string().uuid(),\n entityType: Joi.string(),\n schema: schemaObject,\n disabled: Joi.boolean(),\n }).min(1),\n};\n\nexport default validationSchemas;\n"],"mappings":"8FAEA,MAAM,EAAuBA,EAAAA,QAAI,QAAQ,CAAC,QAAQ,GAAK,CAoBjD,EAAeA,EAAAA,QAAI,OAAqB,CAC5C,KAAMA,EAAAA,QAAI,QAAQ,CAAC,MAAM,SAAS,CAClC,WAAYA,EAAAA,QAAI,OAAO,CACrB,OAAQ,EACR,MAAO,EACR,CAAC,CAAC,UAAU,CACb,SAAUA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACzC,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,qBAAsBA,EAAAA,QAAI,cAAc,CAAC,IAAIA,EAAAA,QAAI,SAAS,CAAEA,EAAAA,QAAI,QAAQ,CAAC,CACzE,IAAKA,EAAAA,QAAI,QAAQ,CACjB,QAASA,EAAAA,QAAI,QAAQ,CACrB,GAAIA,EAAAA,QAAI,QAAQ,CAChB,KAAMA,EAAAA,QAAI,QAAQ,CAClB,KAAMA,EAAAA,QAAI,QAAQ,CACnB,CAAC,CAiBIC,EAAuC,CAC3C,OAAQD,EAAAA,QAAI,OAA+B,CACzC,SAAUA,EAAAA,QAAI,QAAQ,CAAC,MAAM,CAAC,UAAU,CACxC,WAAYA,EAAAA,QAAI,QAAQ,CAAC,UAAU,CACnC,OAAQ,EAAa,UAAU,CAChC,CAAC,CAEF,OAAQA,EAAAA,QAAI,OAA+B,CACzC,SAAUA,EAAAA,QAAI,QAAQ,CAAC,MAAM,CAC7B,WAAYA,EAAAA,QAAI,QAAQ,CACxB,OAAQ,EACR,SAAUA,EAAAA,QAAI,SAAS,CACxB,CAAC,CAAC,IAAI,EAAE,CACV,CAED,IAAA,EAAe"}
1
+ {"version":3,"file":"validations.cjs","names":["Joi","validationSchemas: ValidationSchemas"],"sources":["../../../../src/api/v1/validator/validations.ts"],"sourcesContent":["import Joi from 'joi';\n\nconst jsonSchemaValidation = Joi.object().unknown(true);\n\n/**\n * Configuration for a relation to be fetched for validation\n */\nexport interface RelationConfig {\n /** Relation alias (e.g., 'reservationVehicles') */\n name: string;\n /** Model/table name (e.g., 'ReservationVehicles') */\n model: string;\n /**\n * Foreign key field name.\n * - For hasMany/hasOne: FK on the RELATED table (e.g., 'reservation_id' on ReservationVehicles)\n * - For belongsTo: FK on the CURRENT model (e.g., 'fleet_id' on Reservation)\n */\n foreignKey: string;\n /**\n * Primary/local key field name (default: 'id').\n * - For hasMany/hasOne: PK on current model (e.g., 'id' on Reservation)\n * - For belongsTo: PK on related model (e.g., 'id' on Fleet)\n */\n localKey?: string;\n /** Type of relation */\n type: 'hasMany' | 'hasOne' | 'belongsTo';\n /** Specific columns to fetch (optional, default: all) */\n attributes?: string[];\n}\n\nexport interface SchemaObject {\n type: 'object';\n properties?: {\n before?: object;\n after?: object;\n };\n required?: string[];\n /** Optional relations to fetch for validation */\n relations?: RelationConfig[];\n allOf?: object[];\n anyOf?: object[];\n oneOf?: object[];\n additionalProperties?: boolean | object;\n $id?: string;\n $schema?: string;\n if?: object;\n then?: object;\n else?: object;\n}\n\nconst relationConfig = Joi.object<RelationConfig>({\n name: Joi.string().required(),\n model: Joi.string().required(),\n foreignKey: Joi.string().required(),\n localKey: Joi.string(),\n type: Joi.string().valid('hasMany', 'hasOne', 'belongsTo').required(),\n attributes: Joi.array().items(Joi.string()),\n});\n\nconst schemaObject = Joi.object<SchemaObject>({\n type: Joi.string().valid('object'),\n properties: Joi.object({\n before: jsonSchemaValidation,\n after: jsonSchemaValidation,\n }).required(),\n required: Joi.array().items(Joi.string()),\n relations: Joi.array().items(relationConfig),\n allOf: Joi.array().items(Joi.object()),\n anyOf: Joi.array().items(Joi.object()),\n oneOf: Joi.array().items(Joi.object()),\n additionalProperties: Joi.alternatives().try(Joi.boolean(), Joi.object()),\n $id: Joi.string(),\n $schema: Joi.string(),\n if: Joi.object(),\n then: Joi.object(),\n else: Joi.object(),\n});\n\nexport interface CreateValidatorPayload {\n entityId: string;\n entityType: string;\n schema: SchemaObject;\n}\n\nexport interface UpdateValidatorPayload extends Partial<CreateValidatorPayload> {\n disabled?: boolean;\n}\n\ninterface ValidationSchemas {\n create: Joi.ObjectSchema<CreateValidatorPayload>;\n update: Joi.ObjectSchema<UpdateValidatorPayload>;\n}\n\nconst validationSchemas: ValidationSchemas = {\n create: Joi.object<CreateValidatorPayload>({\n entityId: Joi.string().uuid().required(),\n entityType: Joi.string().required(),\n schema: schemaObject.required(),\n }),\n\n update: Joi.object<UpdateValidatorPayload>({\n entityId: Joi.string().uuid(),\n entityType: Joi.string(),\n schema: schemaObject,\n disabled: Joi.boolean(),\n }).min(1),\n};\n\nexport default validationSchemas;\n"],"mappings":"8FAEA,MAAM,EAAuBA,EAAAA,QAAI,QAAQ,CAAC,QAAQ,GAAK,CAgDjD,EAAiBA,EAAAA,QAAI,OAAuB,CAChD,KAAMA,EAAAA,QAAI,QAAQ,CAAC,UAAU,CAC7B,MAAOA,EAAAA,QAAI,QAAQ,CAAC,UAAU,CAC9B,WAAYA,EAAAA,QAAI,QAAQ,CAAC,UAAU,CACnC,SAAUA,EAAAA,QAAI,QAAQ,CACtB,KAAMA,EAAAA,QAAI,QAAQ,CAAC,MAAM,UAAW,SAAU,YAAY,CAAC,UAAU,CACrE,WAAYA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CAC5C,CAAC,CAEI,EAAeA,EAAAA,QAAI,OAAqB,CAC5C,KAAMA,EAAAA,QAAI,QAAQ,CAAC,MAAM,SAAS,CAClC,WAAYA,EAAAA,QAAI,OAAO,CACrB,OAAQ,EACR,MAAO,EACR,CAAC,CAAC,UAAU,CACb,SAAUA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACzC,UAAWA,EAAAA,QAAI,OAAO,CAAC,MAAM,EAAe,CAC5C,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,MAAOA,EAAAA,QAAI,OAAO,CAAC,MAAMA,EAAAA,QAAI,QAAQ,CAAC,CACtC,qBAAsBA,EAAAA,QAAI,cAAc,CAAC,IAAIA,EAAAA,QAAI,SAAS,CAAEA,EAAAA,QAAI,QAAQ,CAAC,CACzE,IAAKA,EAAAA,QAAI,QAAQ,CACjB,QAASA,EAAAA,QAAI,QAAQ,CACrB,GAAIA,EAAAA,QAAI,QAAQ,CAChB,KAAMA,EAAAA,QAAI,QAAQ,CAClB,KAAMA,EAAAA,QAAI,QAAQ,CACnB,CAAC,CAiBIC,EAAuC,CAC3C,OAAQD,EAAAA,QAAI,OAA+B,CACzC,SAAUA,EAAAA,QAAI,QAAQ,CAAC,MAAM,CAAC,UAAU,CACxC,WAAYA,EAAAA,QAAI,QAAQ,CAAC,UAAU,CACnC,OAAQ,EAAa,UAAU,CAChC,CAAC,CAEF,OAAQA,EAAAA,QAAI,OAA+B,CACzC,SAAUA,EAAAA,QAAI,QAAQ,CAAC,MAAM,CAC7B,WAAYA,EAAAA,QAAI,QAAQ,CACxB,OAAQ,EACR,SAAUA,EAAAA,QAAI,SAAS,CACxB,CAAC,CAAC,IAAI,EAAE,CACV,CAED,IAAA,EAAe"}
@@ -1,6 +1,32 @@
1
1
  import Joi from "joi";
2
2
 
3
3
  //#region src/api/v1/validator/validations.d.ts
4
+
5
+ /**
6
+ * Configuration for a relation to be fetched for validation
7
+ */
8
+ interface RelationConfig {
9
+ /** Relation alias (e.g., 'reservationVehicles') */
10
+ name: string;
11
+ /** Model/table name (e.g., 'ReservationVehicles') */
12
+ model: string;
13
+ /**
14
+ * Foreign key field name.
15
+ * - For hasMany/hasOne: FK on the RELATED table (e.g., 'reservation_id' on ReservationVehicles)
16
+ * - For belongsTo: FK on the CURRENT model (e.g., 'fleet_id' on Reservation)
17
+ */
18
+ foreignKey: string;
19
+ /**
20
+ * Primary/local key field name (default: 'id').
21
+ * - For hasMany/hasOne: PK on current model (e.g., 'id' on Reservation)
22
+ * - For belongsTo: PK on related model (e.g., 'id' on Fleet)
23
+ */
24
+ localKey?: string;
25
+ /** Type of relation */
26
+ type: "hasMany" | "hasOne" | "belongsTo";
27
+ /** Specific columns to fetch (optional, default: all) */
28
+ attributes?: string[];
29
+ }
4
30
  interface SchemaObject {
5
31
  type: "object";
6
32
  properties?: {
@@ -8,6 +34,8 @@ interface SchemaObject {
8
34
  after?: object;
9
35
  };
10
36
  required?: string[];
37
+ /** Optional relations to fetch for validation */
38
+ relations?: RelationConfig[];
11
39
  allOf?: object[];
12
40
  anyOf?: object[];
13
41
  oneOf?: object[];
@@ -1,6 +1,32 @@
1
1
  import Joi from "joi";
2
2
 
3
3
  //#region src/api/v1/validator/validations.d.ts
4
+
5
+ /**
6
+ * Configuration for a relation to be fetched for validation
7
+ */
8
+ interface RelationConfig {
9
+ /** Relation alias (e.g., 'reservationVehicles') */
10
+ name: string;
11
+ /** Model/table name (e.g., 'ReservationVehicles') */
12
+ model: string;
13
+ /**
14
+ * Foreign key field name.
15
+ * - For hasMany/hasOne: FK on the RELATED table (e.g., 'reservation_id' on ReservationVehicles)
16
+ * - For belongsTo: FK on the CURRENT model (e.g., 'fleet_id' on Reservation)
17
+ */
18
+ foreignKey: string;
19
+ /**
20
+ * Primary/local key field name (default: 'id').
21
+ * - For hasMany/hasOne: PK on current model (e.g., 'id' on Reservation)
22
+ * - For belongsTo: PK on related model (e.g., 'id' on Fleet)
23
+ */
24
+ localKey?: string;
25
+ /** Type of relation */
26
+ type: "hasMany" | "hasOne" | "belongsTo";
27
+ /** Specific columns to fetch (optional, default: all) */
28
+ attributes?: string[];
29
+ }
4
30
  interface SchemaObject {
5
31
  type: "object";
6
32
  properties?: {
@@ -8,6 +34,8 @@ interface SchemaObject {
8
34
  after?: object;
9
35
  };
10
36
  required?: string[];
37
+ /** Optional relations to fetch for validation */
38
+ relations?: RelationConfig[];
11
39
  allOf?: object[];
12
40
  anyOf?: object[];
13
41
  oneOf?: object[];
@@ -1,2 +1,2 @@
1
- import e from"joi";const t=e.object().unknown(!0),n=e.object({type:e.string().valid(`object`),properties:e.object({before:t,after:t}).required(),required:e.array().items(e.string()),allOf:e.array().items(e.object()),anyOf:e.array().items(e.object()),oneOf:e.array().items(e.object()),additionalProperties:e.alternatives().try(e.boolean(),e.object()),$id:e.string(),$schema:e.string(),if:e.object(),then:e.object(),else:e.object()});var r={create:e.object({entityId:e.string().uuid().required(),entityType:e.string().required(),schema:n.required()}),update:e.object({entityId:e.string().uuid(),entityType:e.string(),schema:n,disabled:e.boolean()}).min(1)};export{r as default};
1
+ import e from"joi";const t=e.object().unknown(!0),n=e.object({name:e.string().required(),model:e.string().required(),foreignKey:e.string().required(),localKey:e.string(),type:e.string().valid(`hasMany`,`hasOne`,`belongsTo`).required(),attributes:e.array().items(e.string())}),r=e.object({type:e.string().valid(`object`),properties:e.object({before:t,after:t}).required(),required:e.array().items(e.string()),relations:e.array().items(n),allOf:e.array().items(e.object()),anyOf:e.array().items(e.object()),oneOf:e.array().items(e.object()),additionalProperties:e.alternatives().try(e.boolean(),e.object()),$id:e.string(),$schema:e.string(),if:e.object(),then:e.object(),else:e.object()});var i={create:e.object({entityId:e.string().uuid().required(),entityType:e.string().required(),schema:r.required()}),update:e.object({entityId:e.string().uuid(),entityType:e.string(),schema:r,disabled:e.boolean()}).min(1)};export{i as default};
2
2
  //# sourceMappingURL=validations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validations.js","names":["validationSchemas: ValidationSchemas"],"sources":["../../../../src/api/v1/validator/validations.ts"],"sourcesContent":["import Joi from 'joi';\n\nconst jsonSchemaValidation = Joi.object().unknown(true);\n\nexport interface SchemaObject {\n type: 'object';\n properties?: {\n before?: object;\n after?: object;\n };\n required?: string[];\n allOf?: object[];\n anyOf?: object[];\n oneOf?: object[];\n additionalProperties?: boolean | object;\n $id?: string;\n $schema?: string;\n if?: object;\n then?: object;\n else?: object;\n}\n\nconst schemaObject = Joi.object<SchemaObject>({\n type: Joi.string().valid('object'),\n properties: Joi.object({\n before: jsonSchemaValidation,\n after: jsonSchemaValidation,\n }).required(),\n required: Joi.array().items(Joi.string()),\n allOf: Joi.array().items(Joi.object()),\n anyOf: Joi.array().items(Joi.object()),\n oneOf: Joi.array().items(Joi.object()),\n additionalProperties: Joi.alternatives().try(Joi.boolean(), Joi.object()),\n $id: Joi.string(),\n $schema: Joi.string(),\n if: Joi.object(),\n then: Joi.object(),\n else: Joi.object(),\n});\n\nexport interface CreateValidatorPayload {\n entityId: string;\n entityType: string;\n schema: SchemaObject;\n}\n\nexport interface UpdateValidatorPayload extends Partial<CreateValidatorPayload> {\n disabled?: boolean;\n}\n\ninterface ValidationSchemas {\n create: Joi.ObjectSchema<CreateValidatorPayload>;\n update: Joi.ObjectSchema<UpdateValidatorPayload>;\n}\n\nconst validationSchemas: ValidationSchemas = {\n create: Joi.object<CreateValidatorPayload>({\n entityId: Joi.string().uuid().required(),\n entityType: Joi.string().required(),\n schema: schemaObject.required(),\n }),\n\n update: Joi.object<UpdateValidatorPayload>({\n entityId: Joi.string().uuid(),\n entityType: Joi.string(),\n schema: schemaObject,\n disabled: Joi.boolean(),\n }).min(1),\n};\n\nexport default validationSchemas;\n"],"mappings":"mBAEA,MAAM,EAAuB,EAAI,QAAQ,CAAC,QAAQ,GAAK,CAoBjD,EAAe,EAAI,OAAqB,CAC5C,KAAM,EAAI,QAAQ,CAAC,MAAM,SAAS,CAClC,WAAY,EAAI,OAAO,CACrB,OAAQ,EACR,MAAO,EACR,CAAC,CAAC,UAAU,CACb,SAAU,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACzC,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,qBAAsB,EAAI,cAAc,CAAC,IAAI,EAAI,SAAS,CAAE,EAAI,QAAQ,CAAC,CACzE,IAAK,EAAI,QAAQ,CACjB,QAAS,EAAI,QAAQ,CACrB,GAAI,EAAI,QAAQ,CAChB,KAAM,EAAI,QAAQ,CAClB,KAAM,EAAI,QAAQ,CACnB,CAAC,CAgCF,IAAA,EAf6C,CAC3C,OAAQ,EAAI,OAA+B,CACzC,SAAU,EAAI,QAAQ,CAAC,MAAM,CAAC,UAAU,CACxC,WAAY,EAAI,QAAQ,CAAC,UAAU,CACnC,OAAQ,EAAa,UAAU,CAChC,CAAC,CAEF,OAAQ,EAAI,OAA+B,CACzC,SAAU,EAAI,QAAQ,CAAC,MAAM,CAC7B,WAAY,EAAI,QAAQ,CACxB,OAAQ,EACR,SAAU,EAAI,SAAS,CACxB,CAAC,CAAC,IAAI,EAAE,CACV"}
1
+ {"version":3,"file":"validations.js","names":["validationSchemas: ValidationSchemas"],"sources":["../../../../src/api/v1/validator/validations.ts"],"sourcesContent":["import Joi from 'joi';\n\nconst jsonSchemaValidation = Joi.object().unknown(true);\n\n/**\n * Configuration for a relation to be fetched for validation\n */\nexport interface RelationConfig {\n /** Relation alias (e.g., 'reservationVehicles') */\n name: string;\n /** Model/table name (e.g., 'ReservationVehicles') */\n model: string;\n /**\n * Foreign key field name.\n * - For hasMany/hasOne: FK on the RELATED table (e.g., 'reservation_id' on ReservationVehicles)\n * - For belongsTo: FK on the CURRENT model (e.g., 'fleet_id' on Reservation)\n */\n foreignKey: string;\n /**\n * Primary/local key field name (default: 'id').\n * - For hasMany/hasOne: PK on current model (e.g., 'id' on Reservation)\n * - For belongsTo: PK on related model (e.g., 'id' on Fleet)\n */\n localKey?: string;\n /** Type of relation */\n type: 'hasMany' | 'hasOne' | 'belongsTo';\n /** Specific columns to fetch (optional, default: all) */\n attributes?: string[];\n}\n\nexport interface SchemaObject {\n type: 'object';\n properties?: {\n before?: object;\n after?: object;\n };\n required?: string[];\n /** Optional relations to fetch for validation */\n relations?: RelationConfig[];\n allOf?: object[];\n anyOf?: object[];\n oneOf?: object[];\n additionalProperties?: boolean | object;\n $id?: string;\n $schema?: string;\n if?: object;\n then?: object;\n else?: object;\n}\n\nconst relationConfig = Joi.object<RelationConfig>({\n name: Joi.string().required(),\n model: Joi.string().required(),\n foreignKey: Joi.string().required(),\n localKey: Joi.string(),\n type: Joi.string().valid('hasMany', 'hasOne', 'belongsTo').required(),\n attributes: Joi.array().items(Joi.string()),\n});\n\nconst schemaObject = Joi.object<SchemaObject>({\n type: Joi.string().valid('object'),\n properties: Joi.object({\n before: jsonSchemaValidation,\n after: jsonSchemaValidation,\n }).required(),\n required: Joi.array().items(Joi.string()),\n relations: Joi.array().items(relationConfig),\n allOf: Joi.array().items(Joi.object()),\n anyOf: Joi.array().items(Joi.object()),\n oneOf: Joi.array().items(Joi.object()),\n additionalProperties: Joi.alternatives().try(Joi.boolean(), Joi.object()),\n $id: Joi.string(),\n $schema: Joi.string(),\n if: Joi.object(),\n then: Joi.object(),\n else: Joi.object(),\n});\n\nexport interface CreateValidatorPayload {\n entityId: string;\n entityType: string;\n schema: SchemaObject;\n}\n\nexport interface UpdateValidatorPayload extends Partial<CreateValidatorPayload> {\n disabled?: boolean;\n}\n\ninterface ValidationSchemas {\n create: Joi.ObjectSchema<CreateValidatorPayload>;\n update: Joi.ObjectSchema<UpdateValidatorPayload>;\n}\n\nconst validationSchemas: ValidationSchemas = {\n create: Joi.object<CreateValidatorPayload>({\n entityId: Joi.string().uuid().required(),\n entityType: Joi.string().required(),\n schema: schemaObject.required(),\n }),\n\n update: Joi.object<UpdateValidatorPayload>({\n entityId: Joi.string().uuid(),\n entityType: Joi.string(),\n schema: schemaObject,\n disabled: Joi.boolean(),\n }).min(1),\n};\n\nexport default validationSchemas;\n"],"mappings":"mBAEA,MAAM,EAAuB,EAAI,QAAQ,CAAC,QAAQ,GAAK,CAgDjD,EAAiB,EAAI,OAAuB,CAChD,KAAM,EAAI,QAAQ,CAAC,UAAU,CAC7B,MAAO,EAAI,QAAQ,CAAC,UAAU,CAC9B,WAAY,EAAI,QAAQ,CAAC,UAAU,CACnC,SAAU,EAAI,QAAQ,CACtB,KAAM,EAAI,QAAQ,CAAC,MAAM,UAAW,SAAU,YAAY,CAAC,UAAU,CACrE,WAAY,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CAC5C,CAAC,CAEI,EAAe,EAAI,OAAqB,CAC5C,KAAM,EAAI,QAAQ,CAAC,MAAM,SAAS,CAClC,WAAY,EAAI,OAAO,CACrB,OAAQ,EACR,MAAO,EACR,CAAC,CAAC,UAAU,CACb,SAAU,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACzC,UAAW,EAAI,OAAO,CAAC,MAAM,EAAe,CAC5C,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,MAAO,EAAI,OAAO,CAAC,MAAM,EAAI,QAAQ,CAAC,CACtC,qBAAsB,EAAI,cAAc,CAAC,IAAI,EAAI,SAAS,CAAE,EAAI,QAAQ,CAAC,CACzE,IAAK,EAAI,QAAQ,CACjB,QAAS,EAAI,QAAQ,CACrB,GAAI,EAAI,QAAQ,CAChB,KAAM,EAAI,QAAQ,CAClB,KAAM,EAAI,QAAQ,CACnB,CAAC,CAgCF,IAAA,EAf6C,CAC3C,OAAQ,EAAI,OAA+B,CACzC,SAAU,EAAI,QAAQ,CAAC,MAAM,CAAC,UAAU,CACxC,WAAY,EAAI,QAAQ,CAAC,UAAU,CACnC,OAAQ,EAAa,UAAU,CAChC,CAAC,CAEF,OAAQ,EAAI,OAA+B,CACzC,SAAU,EAAI,QAAQ,CAAC,MAAM,CAC7B,WAAY,EAAI,QAAQ,CACxB,OAAQ,EACR,SAAU,EAAI,SAAS,CACxB,CAAC,CAAC,IAAI,EAAE,CACV"}
@@ -1,2 +1,2 @@
1
- const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger/index.cjs`),n=require(`../utils/constants/index.cjs`),r=require(`../errors/index.cjs`),i=require(`../repository/definition.cjs`),a=require(`../repository/validator.cjs`),o=require(`../utils/scopeAttributes.cjs`),s=require(`./utils/updateInstanceValues.cjs`);let c=require(`joi`);c=e.__toESM(c);let l=require(`@autofleet/errors`),u=require(`ajv`);u=e.__toESM(u);let d=require(`ajv-formats`);d=e.__toESM(d);let f=require(`ajv-errors`);f=e.__toESM(f);const p=[`id`,`schema`,`modelType`,`entityId`,`disabled`],m=new u.default({allErrors:!0,strict:!1,strictTypes:!1,$data:!0});(0,d.default)(m),(0,f.default)(m);const h=(e,t)=>({__proto__:null,...e,...t}),g=async(e,n)=>{if(!e.id||!e.customFields||Object.keys(e.customFields).length===0)return e.customFields||{};try{let r=await e.constructor.findOne({where:{id:e.id},attributes:[`customFields`],transaction:n.transaction,raw:!0});if(r?.customFields){let n=h(r.customFields,e.customFields);return t.default.debug(`sadot - fetched complete custom fields for validation`,{fieldsCount:Object.keys(n).length,updateFieldsCount:Object.keys(e.customFields).length}),n}}catch(e){t.default.error(`sadot - error fetching complete model for validation`,{error:e})}return e.customFields||{}},_=e=>{let t={...e.dataValues};(e.changed?.()||[]).forEach(n=>{let r=e.previous?.(n);r!==void 0&&(t[n]=r)});let n=e.previous?.(`customFields`);return n!==void 0&&(t.customFields=n),t},v=e=>e.reduce((e,t)=>{let n=((t.instancePath||``).split(`/`).filter(Boolean).join(`.`).replace(/^after\./,``)+(t.keyword===`required`?`.${t.params?.missingProperty}`:``)).replace(/^\./,``)||`root`;return e[n]=t.message||`Invalid value`,e},{}),y=async(e,n,r,i={},s=!1)=>{let c=e.constructor.name;t.default.debug(`sadot - validating model`,{isCreate:s,modelType:c});let u=o.default(e,r);if(t.default.debug(`sadot - identifiers`,{identifiers:u}),!u||Object.keys(u).length===0){t.default.debug(`sadot - skipping validation: no identifiers`);return}let d=Object.values(u)[0];if(t.default.debug(`sadot - entityId`,{entityId:d}),!d){t.default.debug(`sadot - skipping validation: no entityId`);return}let f,y;n.transaction&&(n.transaction.validationsCache??=new Map,y=`${c}-${d}`,f=n.transaction.validationsCache.get(y)),f||(f=a.findAllByModelType(c,d,{transaction:n.transaction,attributes:p,modelOptions:i}),n.transaction&&n?.transaction?.validationsCache.set(y,f));let b=await f;if(t.default.debug(`sadot - validators found`,{count:b.length}),!b.length){t.default.debug(`sadot - skipping validation: no validators found`);return}let x=null;s||(x=_(e));let S=s?e.customFields||{}:await g(e,n);for(let n of b){let{schema:r}=n,i=r;if(t.default.debug(`sadot - validating with schema`,{schema:r,hasAfterProps:!!i.properties?.after,hasBeforeProps:!!i.properties?.before}),s){if(i.properties?.after){let t=m.compile({...r,properties:{after:i.properties.after}});if(!t(JSON.parse(JSON.stringify({after:{...e.dataValues,customFields:S}})))){let e=t.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),n=v(t.errors);throw new l.BadRequest([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:n})}}}else{let n=m.compile(i),r=h(e.dataValues);r.customFields=S;let a={before:x,after:r},o=n(JSON.parse(JSON.stringify(a)));if(t.default.debug(`sadot - validation result`,{isValid:o,test:{before:x,after:r}}),!o){let e=n.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(n.errors);throw new l.BadRequest([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}},b=async({modelType:e,modelOptions:t,identifiers:n,options:r})=>{let{include:a,useEntityIdFromInclude:o}=t,s={modelType:e,disabled:!1,...!o&&{entityId:n}};return await i.findAll(s,{withDisabled:!1,transaction:r.transaction,include:a?.(n)})},x=(e,t)=>{(e||[]).forEach(e=>{let{fieldType:i,name:a}=e;if([n.CustomFieldDefinitionType.DATE,n.CustomFieldDefinitionType.DATETIME].includes(i)){let e=t.customFields?.[a];if(e){let{value:n,error:i}=c.default.date().validate(e);if(i)throw new r.InvalidValueError(e,a,i);t.customFields[a]=n.toISOString()}}})},S=(e,n={},i={useCustomFieldsEntries:!1})=>async(a,c)=>{t.default.debug(`sadot - before create hook`);let{fields:l}=c,u=a.constructor.name,d=o.default(a,e),f=await b({modelType:u,modelOptions:n,identifiers:d,options:c}),p=f.filter(e=>![null,void 0].includes(e.defaultValue));p.length&&(a.customFields||={},p.filter(e=>a.customFields?.[e.name]===void 0).forEach(({name:e,defaultValue:t})=>{a.customFields[e]=t}));let m;m=i.getRequiredCustomFields?await Promise.resolve(i.getRequiredCustomFields(a,f,c)):Array.from(new Set(f.filter(({required:e})=>e).map(({name:e})=>e)));let{customFields:h}=a,g=Object.keys(h??{}),_=m.filter(e=>!g.includes(e));if(_?.length)throw new r.MissingRequiredCustomFieldError(_);await y(a,c,e,n,!0),x(f,a);let v=l.indexOf(`customFields`);v===-1||!h||!Object.keys(h).length||(await s.default({modelId:a.id,modelType:u,identifiers:d,customFields:h,options:{useCustomFieldsEntries:i.useCustomFieldsEntries,transaction:c.transaction,modelOptions:n}}),l.splice(v,1))},C=(e,n={},r={useCustomFieldsEntries:!1})=>async(i,a)=>{t.default.debug(`sadot - before update hook`);let{fields:c}=a,l=i.constructor.name,u=o.default(i,e),d=await b({modelType:l,modelOptions:n,identifiers:u,options:a});await y(i,a,e,n,!1),x(d,i);let f=c.indexOf(`customFields`);if(f>-1){let{customFields:e}=i;if(!Object.keys(e).length)return;await s.default({modelId:i.id,modelType:l,identifiers:u,customFields:e,options:{useCustomFieldsEntries:r.useCustomFieldsEntries,transaction:a.transaction,modelOptions:n}}),c.splice(f,1)}},w=e=>{e.individualHooks=!0},T=e=>{e.individualHooks=!0};exports.beforeBulkCreate=w,exports.beforeBulkUpdate=T,exports.beforeCreate=S,exports.beforeUpdate=C;
1
+ const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`../utils/logger/index.cjs`),n=require(`../utils/constants/index.cjs`),r=require(`../errors/index.cjs`),i=require(`../repository/definition.cjs`),a=require(`../repository/validator.cjs`),o=require(`../utils/scopeAttributes.cjs`),s=require(`./utils/updateInstanceValues.cjs`);let c=require(`joi`);c=e.__toESM(c);let l=require(`@autofleet/errors`),u=require(`ajv`);u=e.__toESM(u);let d=require(`ajv-formats`);d=e.__toESM(d);let f=require(`ajv-errors`);f=e.__toESM(f);const p=[`id`,`schema`,`modelType`,`entityId`,`disabled`],m=new u.default({allErrors:!0,strict:!1,strictTypes:!1,$data:!0});(0,d.default)(m),(0,f.default)(m);const h=(e,t)=>({__proto__:null,...e,...t}),g=async(e,n)=>{if(!e.id||!e.customFields||Object.keys(e.customFields).length===0)return e.customFields||{};try{let r=await e.constructor.findOne({where:{id:e.id},attributes:[`customFields`],transaction:n.transaction,raw:!0});if(r?.customFields){let n=h(r.customFields,e.customFields);return t.default.debug(`sadot - fetched complete custom fields for validation`,{fieldsCount:Object.keys(n).length,updateFieldsCount:Object.keys(e.customFields).length}),n}}catch(e){t.default.error(`sadot - error fetching complete model for validation`,{error:e})}return e.customFields||{}},_=e=>{let t={...e.dataValues};(e.changed?.()||[]).forEach(n=>{let r=e.previous?.(n);r!==void 0&&(t[n]=r)});let n=e.previous?.(`customFields`);return n!==void 0&&(t.customFields=n),t},v=e=>e.reduce((e,t)=>{let n=((t.instancePath||``).split(`/`).filter(Boolean).join(`.`).replace(/^after\./,``)+(t.keyword===`required`?`.${t.params?.missingProperty}`:``)).replace(/^\./,``)||`root`;return e[n]=t.message||`Invalid value`,e},{}),y=async(e,n,r,i)=>{if(t.default.info(`[VALIDATOR-RELATIONS] fetchRelatedData called`,{relationCount:n?.length||0,hasGetModel:!!i,instanceId:e?.id,instanceConstructor:e?.constructor?.name}),!n||n.length===0)return t.default.info(`[VALIDATOR-RELATIONS] No relations to fetch, returning empty`),{};if(!i)return t.default.warn(`[VALIDATOR-RELATIONS] getModel function not provided, cannot fetch related data for validation`),{};let a={};for(let o of n){let{name:n,model:s,foreignKey:c,localKey:l=`id`,type:u,attributes:d}=o;t.default.info(`[VALIDATOR-RELATIONS] Processing relation`,{relationName:n,relationType:u,model:s,foreignKey:c,localKey:l});let f=e[n]!==void 0;if(t.default.info(`[VALIDATOR-RELATIONS] Checking if relation exists on instance`,{relationName:n,hasRelation:f,relationValue:f?e[n]:`NOT_SET`,isArray:f?Array.isArray(e[n]):!1}),f){let r=e[n];if(Array.isArray(r)){a[n]=r,t.default.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${n}`,{count:r.length});continue}if(typeof r==`object`&&r){a[n]=r,t.default.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${n}`);continue}if(r===null){a[n]=null,t.default.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set null relation for ${n}`);continue}}let p=`get${n.charAt(0).toUpperCase()+n.slice(1)}`,m=typeof e[p]==`function`;if(t.default.info(`[VALIDATOR-RELATIONS] Checking for Sequelize association getter`,{relationName:n,methodName:p,hasMethod:m,availableMethods:Object.keys(e).filter(e=>e.startsWith(`get`))}),m)try{t.default.info(`[VALIDATOR-RELATIONS] Calling ${p}...`);let i=await e[p]({transaction:r.transaction});if(t.default.info(`[VALIDATOR-RELATIONS] Association getter result`,{methodName:p,hasData:i!==void 0,isArray:Array.isArray(i),count:Array.isArray(i)?i.length:`N/A`,data:i}),i!==void 0){a[n]=i,t.default.info(`[VALIDATOR-RELATIONS] ✓ Derived relation data using ${p} for ${n}`,{count:Array.isArray(i)?i.length:`single`});continue}}catch(e){t.default.warn(`[VALIDATOR-RELATIONS] Failed to derive relation ${n}, will query database`,{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0})}t.default.info(`[VALIDATOR-RELATIONS] Falling back to database query for relation`,{relationName:n});let h;try{h=i(s),t.default.info(`[VALIDATOR-RELATIONS] Model fetched successfully`,{model:s,hasModel:!!h})}catch(e){t.default.warn(`[VALIDATOR-RELATIONS] Failed to get model ${s} for relation ${n}`,{error:e});continue}if(!h){t.default.warn(`[VALIDATOR-RELATIONS] Related model ${s} not found for validation. Ensure the model name is correct.`);continue}let g,_;if(u===`belongsTo`){if(_=e[c],!_){t.default.info(`[VALIDATOR-RELATIONS] Foreign key ${c} is null/undefined, skipping relation ${n}`);continue}g={[l]:_}}else{if(_=e[l],!_){t.default.info(`[VALIDATOR-RELATIONS] Local key ${l} is null/undefined, skipping relation ${n}`);continue}g={[c]:_}}let v={where:g,transaction:r.transaction,...d&&{attributes:d},raw:!0};t.default.info(`[VALIDATOR-RELATIONS] Executing database query`,{relationName:n,relationType:u,query:{where:g,attributes:d}});try{if(u===`hasMany`){let e=await h.findAll(v);a[n]=e,t.default.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${e.length} records from database for ${n}`)}else if(u===`hasOne`||u===`belongsTo`){let e=await h.findOne(v);a[n]=e||null,t.default.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${e?`one`:`no`} record from database for ${n}`)}}catch(e){t.default.error(`[VALIDATOR-RELATIONS] Error fetching related data for ${n}`,{error:e,relation:o}),a[n]=u===`hasMany`?[]:null}}return t.default.info(`[VALIDATOR-RELATIONS] fetchRelatedData complete`,{relationsProcessed:Object.keys(a),summary:Object.entries(a).map(([e,t])=>({name:e,count:Array.isArray(t)?t.length:t?`single`:`null`}))}),a},b=async(e,n,r,i,a)=>{if(!n||n.length===0||!a)return{};let o={};for(let e of n){let{name:n,model:s,foreignKey:c,localKey:l=`id`,type:u,attributes:d}=e,f;try{f=a(s)}catch(e){t.default.warn(`Failed to get model ${s} for before state`,{error:e});continue}if(!f)continue;let p,m;if(u===`belongsTo`){if(m=r[c],!m)continue;p={[l]:m}}else{if(m=r[l],!m)continue;p={[c]:m}}let h={where:p,transaction:i.transaction,...d&&{attributes:d},raw:!0};try{u===`hasMany`?o[n]=await f.findAll(h):o[n]=await f.findOne(h)||null}catch(e){t.default.error(`Error fetching before state for ${n}`,{error:e}),o[n]=u===`hasMany`?[]:null}}return o},x=e=>{let t={};for(let[n,r]of Object.entries(e))if(Array.isArray(r))t[`${n}.count`]=r.length,r.forEach((e,r)=>{for(let[i,a]of Object.entries(e))t[`${n}.${r}.${i}`]=a}),t[n]=r;else if(r!==null){for(let[e,i]of Object.entries(r))t[`${n}.${e}`]=i;t[n]=r}return t},S=async(e,n,r,i={},s=!1)=>{let c=e.constructor.name;t.default.debug(`sadot - validating model`,{isCreate:s,modelType:c});let u=o.default(e,r);if(t.default.debug(`sadot - identifiers`,{identifiers:u}),!u||Object.keys(u).length===0){t.default.debug(`sadot - skipping validation: no identifiers`);return}let d=Object.values(u)[0];if(t.default.debug(`sadot - entityId`,{entityId:d}),!d){t.default.debug(`sadot - skipping validation: no entityId`);return}let f,S;n.transaction&&(n.transaction.validationsCache??=new Map,S=`${c}-${d}`,f=n.transaction.validationsCache.get(S)),f||(f=a.findAllByModelType(c,d,{transaction:n.transaction,attributes:p,modelOptions:i}),n.transaction&&n?.transaction?.validationsCache.set(S,f));let C=await f;if(t.default.debug(`sadot - validators found`,{count:C.length}),!C.length){t.default.debug(`sadot - skipping validation: no validators found`);return}let w=null;s||(w=_(e));let T=s?e.customFields||{}:await g(e,n),{getModel:E}=i;for(let r of C){let{schema:i}=r,a=i,o=a.relations||[];if(t.default.info(`[VALIDATOR-RELATIONS] Processing validator schema`,{validatorId:r.id,hasAfterProps:!!a.properties?.after,hasBeforeProps:!!a.properties?.before,hasRelations:o.length>0,relations:o.map(e=>({name:e.name,type:e.type,model:e.model}))}),s){if(a.properties?.after){let t=x(await y(e,o,n,E)),r=m.compile({...i,properties:{after:a.properties.after}});if(!r(JSON.parse(JSON.stringify({after:{...e.dataValues,customFields:T,...t}})))){let e=r.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(r.errors);throw new l.BadRequest([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}else{t.default.info(`[VALIDATOR-RELATIONS] Fetching related data for BEFORE state`);let r=await b(e,o,w,n,E),i=x(r);t.default.info(`[VALIDATOR-RELATIONS] Before state related data`,{raw:r,flattened:i}),t.default.info(`[VALIDATOR-RELATIONS] Fetching related data for AFTER state`);let s=await y(e,o,n,E),u=x(s);t.default.info(`[VALIDATOR-RELATIONS] After state related data`,{raw:s,flattened:u});let d=m.compile(a),f=h(e.dataValues);f.customFields=T;let p={before:{...w,...i},after:{...f,...u}};t.default.info(`[VALIDATOR-RELATIONS] Final validation payload`,{payload:JSON.stringify(p,null,2)});let g=d(JSON.parse(JSON.stringify(p)));if(t.default.info(`[VALIDATOR-RELATIONS] Validation result`,{isValid:g,errors:g?null:d.errors}),!g){let e=d.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(d.errors);throw new l.BadRequest([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}},C=async({modelType:e,modelOptions:t,identifiers:n,options:r})=>{let{include:a,useEntityIdFromInclude:o}=t,s={modelType:e,disabled:!1,...!o&&{entityId:n}};return await i.findAll(s,{withDisabled:!1,transaction:r.transaction,include:a?.(n)})},w=(e,t)=>{(e||[]).forEach(e=>{let{fieldType:i,name:a}=e;if([n.CustomFieldDefinitionType.DATE,n.CustomFieldDefinitionType.DATETIME].includes(i)){let e=t.customFields?.[a];if(e){let{value:n,error:i}=c.default.date().validate(e);if(i)throw new r.InvalidValueError(e,a,i);t.customFields[a]=n.toISOString()}}})},T=(e,n={},i={useCustomFieldsEntries:!1})=>async(a,c)=>{t.default.debug(`sadot - before create hook`);let{fields:l}=c,u=a.constructor.name,d=o.default(a,e),f=await C({modelType:u,modelOptions:n,identifiers:d,options:c}),p=f.filter(e=>![null,void 0].includes(e.defaultValue));p.length&&(a.customFields||={},p.filter(e=>a.customFields?.[e.name]===void 0).forEach(({name:e,defaultValue:t})=>{a.customFields[e]=t}));let m;m=i.getRequiredCustomFields?await Promise.resolve(i.getRequiredCustomFields(a,f,c)):Array.from(new Set(f.filter(({required:e})=>e).map(({name:e})=>e)));let{customFields:h}=a,g=Object.keys(h??{}),_=m.filter(e=>!g.includes(e));if(_?.length)throw new r.MissingRequiredCustomFieldError(_);await S(a,c,e,n,!0),w(f,a);let v=l.indexOf(`customFields`);v===-1||!h||!Object.keys(h).length||(await s.default({modelId:a.id,modelType:u,identifiers:d,customFields:h,options:{useCustomFieldsEntries:i.useCustomFieldsEntries,transaction:c.transaction,modelOptions:n}}),l.splice(v,1))},E=(e,n={},r={useCustomFieldsEntries:!1})=>async(i,a)=>{t.default.debug(`sadot - before update hook`);let{fields:c}=a,l=i.constructor.name,u=o.default(i,e),d=await C({modelType:l,modelOptions:n,identifiers:u,options:a});await S(i,a,e,n,!1),w(d,i);let f=c.indexOf(`customFields`);if(f>-1){let{customFields:e}=i;if(!Object.keys(e).length)return;await s.default({modelId:i.id,modelType:l,identifiers:u,customFields:e,options:{useCustomFieldsEntries:r.useCustomFieldsEntries,transaction:a.transaction,modelOptions:n}}),c.splice(f,1)}},D=e=>{e.individualHooks=!0},O=e=>{e.individualHooks=!0};exports.beforeBulkCreate=D,exports.beforeBulkUpdate=O,exports.beforeCreate=T,exports.beforeUpdate=E;
2
2
  //# sourceMappingURL=hooks.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.cjs","names":["ajv","Ajv","beforeFull: any","applyScopeToInstance","cacheKey: string | undefined","BadRequest","where: WhereOptions","CustomFieldDefinitionType","Joi","InvalidValueError","requiredFieldsNames: string[]","MissingRequiredCustomFieldError","updateInstanceValues"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type {\n BulkCreateOptions, CreateOptions, Transactionable, UpdateOptions, WhereOptions,\n} from 'sequelize';\nimport Ajv from 'ajv';\nimport Joi from 'joi';\nimport addFormats from 'ajv-formats';\nimport { BadRequest } from '@autofleet/errors';\nimport ajvErrors from 'ajv-errors';\nimport logger from '../utils/logger';\nimport * as ValidatorRepo from '../repository/validator';\nimport * as DefinitionRepo from '../repository/definition';\nimport { InvalidValueError, MissingRequiredCustomFieldError } from '../errors';\nimport type { CustomFieldOptions, ModelOptions, TransactionOptions } from '../types';\nimport applyScopeToInstance from '../utils/scopeAttributes';\nimport updateInstanceValues from './utils/updateInstanceValues';\nimport { CustomFieldDefinitionType } from '../utils/constants';\nimport type { CustomFieldDefinition } from '../models';\n\n// Include all required fields for proper validation\nconst CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL = ['id', 'schema', 'modelType', 'entityId', 'disabled'];\n\n// Initialize Ajv with relaxed settings to avoid warnings\nconst ajv = new Ajv({\n allErrors: true,\n strict: false, // Disable strict mode to avoid warnings\n strictTypes: false, // Disable strict type checking\n $data: true, // Enable $data references\n});\n\naddFormats(ajv);\najvErrors(ajv);\n\n/**\n * Helper function to manually copy object properties\n * This is more efficient for large objects and avoids excessive object creation\n */\nconst manualObjectCopy = (sourceObj: Record<string, any>, additionalProps?: Record<string, any>): Record<string, any> =>\n ({ __proto__: null, ...sourceObj, ...additionalProps });\n\n/**\n * Fetches complete custom fields for an instance by merging DB values with update values\n * This is needed for partial updates to ensure all related fields are available for validation\n */\nconst getCompleteCustomFields = async (instance: any, options: Transactionable): Promise<Record<string, any>> => {\n // If we don't have an instance id or no custom fields being updated, return original fields\n if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {\n return instance.customFields || {};\n }\n\n try {\n const ModelClass = instance.constructor;\n // Only select the customFields column to minimize data transfer\n const currentCustomFields = await ModelClass.findOne({\n where: { id: instance.id },\n attributes: ['customFields'],\n transaction: options.transaction,\n raw: true, // Get plain object instead of model instance for better performance\n });\n\n if (currentCustomFields?.customFields) {\n // Merge existing fields with update fields using our helper function\n const completeFields = manualObjectCopy(\n currentCustomFields.customFields,\n instance.customFields,\n );\n\n logger.debug('sadot - fetched complete custom fields for validation', {\n fieldsCount: Object.keys(completeFields).length,\n updateFieldsCount: Object.keys(instance.customFields).length,\n });\n\n return completeFields;\n }\n } catch (error) {\n logger.error('sadot - error fetching complete model for validation', { error });\n // Continue with partial data if we can't fetch the complete model\n }\n\n return instance.customFields || {};\n};\n\nconst buildPreChangeState = (instance: any) => {\n const beforeFull: any = { ...instance.dataValues };\n\n const changedKeys: string[] = instance.changed?.() || [];\n changedKeys.forEach((key) => {\n const prevVal = instance.previous?.(key);\n if (prevVal !== undefined) {\n beforeFull[key] = prevVal;\n }\n });\n\n const prevCF = instance.previous?.('customFields');\n if (prevCF !== undefined) {\n beforeFull.customFields = prevCF;\n }\n\n return beforeFull;\n};\n\nconst formatAjvErrors = (\n errors: {\n instancePath?: string;\n keyword: string;\n message?: string;\n params?: Record<string, any>;\n }[],\n): Record<string, string> => errors.reduce((acc, err) => {\n const basePath = (err.instancePath || '')\n .split('/')\n .filter(Boolean)\n .join('.')\n .replace(/^after\\./, '');\n\n const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';\n const key = (basePath + missingProp).replace(/^\\./, '') || 'root';\n\n const message = err.message || 'Invalid value';\n acc[key] = message;\n\n return acc;\n}, {} as Record<string, string>);\n\n/**\n * Validates the model using custom validators\n */\nconst validateModel = async (\n instance: any,\n options: TransactionOptions,\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n isCreate = false,\n): Promise<void> => {\n const modelType = instance.constructor.name;\n\n logger.debug('sadot - validating model', { isCreate, modelType });\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n logger.debug('sadot - identifiers', { identifiers });\n\n // Skip if no identifiers\n if (!identifiers || Object.keys(identifiers).length === 0) {\n logger.debug('sadot - skipping validation: no identifiers');\n return;\n }\n\n // Find the entityId from identifiers (fleetId, businessModelId, etc.)\n const entityId = Object.values(identifiers)[0]; // Get the first value as entityId\n\n logger.debug('sadot - entityId', { entityId });\n\n if (!entityId) {\n logger.debug('sadot - skipping validation: no entityId');\n return;\n }\n\n let validatorsPromise;\n let cacheKey: string | undefined;\n if (options.transaction) {\n options.transaction.validationsCache ??= new Map();\n cacheKey = `${modelType}-${entityId}`;\n validatorsPromise = options.transaction.validationsCache.get(cacheKey);\n }\n\n if (!validatorsPromise) {\n validatorsPromise = ValidatorRepo.findAllByModelType(\n modelType,\n entityId,\n {\n transaction: options.transaction,\n attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,\n modelOptions,\n },\n );\n if (options.transaction) {\n options?.transaction?.validationsCache!.set(cacheKey!, validatorsPromise);\n }\n }\n const validators = await validatorsPromise;\n\n logger.debug('sadot - validators found', { count: validators.length });\n\n if (!validators.length) {\n logger.debug('sadot - skipping validation: no validators found');\n return;\n }\n\n // For updates, get the previous values\n let originalValues = null;\n if (!isCreate) {\n originalValues = buildPreChangeState(instance);\n }\n\n // Get complete custom fields by merging DB values with update values\n // This is especially important for partial updates to ensure all related fields are available\n const completeCustomFields = !isCreate\n ? await getCompleteCustomFields(instance, options)\n : instance.customFields || {};\n\n for (const validator of validators) {\n const { schema } = validator;\n const typedSchema = schema as Record<string, any>;\n\n logger.debug('sadot - validating with schema', {\n schema,\n hasAfterProps: !!typedSchema.properties?.after,\n hasBeforeProps: !!typedSchema.properties?.before,\n });\n\n if (isCreate) {\n // For create operations, we only need the 'after' state\n if (typedSchema.properties?.after) {\n const validateSchema = ajv.compile({\n ...schema,\n // Focus only on the 'after' validation part for create\n properties: {\n after: typedSchema.properties.after,\n },\n });\n\n const isValid = validateSchema(JSON.parse(JSON.stringify({\n after: {\n ...instance.dataValues,\n customFields: completeCustomFields,\n },\n })));\n\n if (!isValid) {\n const errorDetails = validateSchema.errors?.map(err =>\n `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n } else {\n // For update operations, we need both before and after\n const validateSchema = ajv.compile(typedSchema);\n\n // Create after object with our helper function\n const afterObj = manualObjectCopy(instance.dataValues);\n\n // Add complete custom fields\n afterObj.customFields = completeCustomFields;\n\n // Create validation payload\n const payload = {\n before: originalValues,\n after: afterObj,\n };\n\n // Validate\n const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));\n\n logger.debug('sadot - validation result', {\n isValid,\n test: {\n before: originalValues,\n after: afterObj,\n },\n });\n\n if (!isValid) {\n const errorDetails = validateSchema\n .errors\n ?.map(err => `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n }\n};\n\nconst getFieldDefinitions = async ({\n modelType,\n modelOptions,\n identifiers,\n options,\n}: {\n modelType: any;\n modelOptions: ModelOptions;\n identifiers: any[];\n options: any;\n}) => {\n const { include, useEntityIdFromInclude } = modelOptions;\n const where: WhereOptions = {\n modelType,\n disabled: false,\n ...(!useEntityIdFromInclude && { entityId: identifiers }),\n };\n\n const fieldDefinitions = await DefinitionRepo.findAll(where, {\n withDisabled: false,\n transaction: options.transaction,\n include: include?.(identifiers),\n });\n return fieldDefinitions;\n};\n\nconst formatDates = (fieldDefinitions: CustomFieldDefinition[], instance: any) => {\n (fieldDefinitions || []).forEach((fieldDefinition) => {\n const { fieldType, name } = fieldDefinition;\n if ([CustomFieldDefinitionType.DATE, CustomFieldDefinitionType.DATETIME].includes(fieldType)) {\n const value = instance.customFields?.[name];\n if (value) {\n const { value: joiValue, error: validationError } = Joi.date().validate(value);\n if (validationError) {\n throw new InvalidValueError(value, name, validationError);\n }\n instance.customFields[name] = joiValue.toISOString();\n }\n }\n });\n};\n\n/**\n * Hook to handle validation and custom fields during creation\n */\nexport const beforeCreate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: CreateOptions,\n): Promise<void> => {\n logger.debug('sadot - before create hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n // Step 1: Handle custom fields default values and required fields\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Apply default values\n const fieldsWithDefaultValue = fieldDefinitions.filter(def => ![null, undefined].includes(def.defaultValue));\n if (fieldsWithDefaultValue.length) {\n instance.customFields ||= {};\n fieldsWithDefaultValue\n .filter(def => (instance.customFields?.[def.name] === undefined))\n .forEach(({ name, defaultValue }) => {\n instance.customFields[name] = defaultValue;\n });\n }\n\n // Check for required fields\n let requiredFieldsNames: string[];\n\n if (sadotOptions.getRequiredCustomFields) {\n // Use consumer-provided logic to determine required fields\n requiredFieldsNames = await Promise.resolve(\n sadotOptions.getRequiredCustomFields(instance, fieldDefinitions, options),\n );\n } else {\n // Default behavior: use required flag from fieldDefinitions\n requiredFieldsNames = Array.from(\n new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),\n );\n }\n\n const { customFields } = instance;\n const fieldsNames = Object.keys(customFields ?? {});\n const missingFields = requiredFieldsNames.filter(name => !fieldsNames.includes(name));\n if (missingFields?.length) {\n throw new MissingRequiredCustomFieldError(missingFields);\n }\n\n // Step 2: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, true);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 3: Save custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {\n // No custom fields to update\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n};\n\n/**\n * Hook to handle validation and custom fields during update\n */\nexport const beforeUpdate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: UpdateOptions,\n): Promise<void> => {\n logger.debug('sadot - before update hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Step 1: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, false);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 2: Update custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx > -1) {\n const { customFields } = instance;\n\n if (!Object.keys(customFields).length) {\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n }\n};\n\n/**\n * Hook to enable individual hooks for bulk create operations\n */\nexport const beforeBulkCreate = (options: BulkCreateOptions): void => {\n // This will activate the beforeCreate hook on each instance\n options.individualHooks = true;\n};\n\n/**\n * Hook to enable individual hooks for bulk update operations\n */\nexport const beforeBulkUpdate = (options: UpdateOptions): void => {\n // This will activate the beforeUpdate hook on each instance\n options.individualHooks = true;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"hooks.cjs","names":["ajv","Ajv","beforeFull: any","relatedData: Record<string, any>","where: Record<string, unknown>","keyValue: unknown","flattened: Record<string, any>","applyScopeToInstance","cacheKey: string | undefined","BadRequest","where: WhereOptions","CustomFieldDefinitionType","Joi","InvalidValueError","requiredFieldsNames: string[]","MissingRequiredCustomFieldError","updateInstanceValues"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type {\n BulkCreateOptions, CreateOptions, Transactionable, UpdateOptions, WhereOptions,\n} from 'sequelize';\nimport Ajv from 'ajv';\nimport Joi from 'joi';\nimport addFormats from 'ajv-formats';\nimport { BadRequest } from '@autofleet/errors';\nimport ajvErrors from 'ajv-errors';\nimport logger from '../utils/logger';\nimport * as ValidatorRepo from '../repository/validator';\nimport * as DefinitionRepo from '../repository/definition';\nimport { InvalidValueError, MissingRequiredCustomFieldError } from '../errors';\nimport type { CustomFieldOptions, ModelOptions, TransactionOptions, ModelFetcher } from '../types';\nimport applyScopeToInstance from '../utils/scopeAttributes';\nimport updateInstanceValues from './utils/updateInstanceValues';\nimport { CustomFieldDefinitionType } from '../utils/constants';\nimport type { CustomFieldDefinition } from '../models';\nimport type { RelationConfig } from '../api/v1/validator/validations';\n\n// Include all required fields for proper validation\nconst CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL = ['id', 'schema', 'modelType', 'entityId', 'disabled'];\n\n// Initialize Ajv with relaxed settings to avoid warnings\nconst ajv = new Ajv({\n allErrors: true,\n strict: false, // Disable strict mode to avoid warnings\n strictTypes: false, // Disable strict type checking\n $data: true, // Enable $data references\n});\n\naddFormats(ajv);\najvErrors(ajv);\n\n/**\n * Helper function to manually copy object properties\n * This is more efficient for large objects and avoids excessive object creation\n */\nconst manualObjectCopy = (sourceObj: Record<string, any>, additionalProps?: Record<string, any>): Record<string, any> =>\n ({ __proto__: null, ...sourceObj, ...additionalProps });\n\n/**\n * Fetches complete custom fields for an instance by merging DB values with update values\n * This is needed for partial updates to ensure all related fields are available for validation\n */\nconst getCompleteCustomFields = async (instance: any, options: Transactionable): Promise<Record<string, any>> => {\n // If we don't have an instance id or no custom fields being updated, return original fields\n if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {\n return instance.customFields || {};\n }\n\n try {\n const ModelClass = instance.constructor;\n // Only select the customFields column to minimize data transfer\n const currentCustomFields = await ModelClass.findOne({\n where: { id: instance.id },\n attributes: ['customFields'],\n transaction: options.transaction,\n raw: true, // Get plain object instead of model instance for better performance\n });\n\n if (currentCustomFields?.customFields) {\n // Merge existing fields with update fields using our helper function\n const completeFields = manualObjectCopy(\n currentCustomFields.customFields,\n instance.customFields,\n );\n\n logger.debug('sadot - fetched complete custom fields for validation', {\n fieldsCount: Object.keys(completeFields).length,\n updateFieldsCount: Object.keys(instance.customFields).length,\n });\n\n return completeFields;\n }\n } catch (error) {\n logger.error('sadot - error fetching complete model for validation', { error });\n // Continue with partial data if we can't fetch the complete model\n }\n\n return instance.customFields || {};\n};\n\nconst buildPreChangeState = (instance: any) => {\n const beforeFull: any = { ...instance.dataValues };\n\n const changedKeys: string[] = instance.changed?.() || [];\n changedKeys.forEach((key) => {\n const prevVal = instance.previous?.(key);\n if (prevVal !== undefined) {\n beforeFull[key] = prevVal;\n }\n });\n\n const prevCF = instance.previous?.('customFields');\n if (prevCF !== undefined) {\n beforeFull.customFields = prevCF;\n }\n\n return beforeFull;\n};\n\nconst formatAjvErrors = (\n errors: {\n instancePath?: string;\n keyword: string;\n message?: string;\n params?: Record<string, any>;\n }[],\n): Record<string, string> => errors.reduce((acc, err) => {\n const basePath = (err.instancePath || '')\n .split('/')\n .filter(Boolean)\n .join('.')\n .replace(/^after\\./, '');\n\n const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';\n const key = (basePath + missingProp).replace(/^\\./, '') || 'root';\n\n const message = err.message || 'Invalid value';\n acc[key] = message;\n\n return acc;\n}, {} as Record<string, string>);\n\n/**\n * Fetches related data for validation based on schema relations config\n *\n * @param instance - The model instance being validated\n * @param relations - Array of relation configurations from the schema\n * @param options - Transaction options\n * @param getModel - Model fetcher function from consuming application\n * @returns Object containing related data keyed by relation name\n */\nconst fetchRelatedData = async (\n instance: any,\n relations: RelationConfig[],\n options: TransactionOptions,\n getModel?: ModelFetcher,\n): Promise<Record<string, any>> => {\n logger.info('[VALIDATOR-RELATIONS] fetchRelatedData called', {\n relationCount: relations?.length || 0,\n hasGetModel: !!getModel,\n instanceId: instance?.id,\n instanceConstructor: instance?.constructor?.name,\n });\n\n if (!relations || relations.length === 0) {\n logger.info('[VALIDATOR-RELATIONS] No relations to fetch, returning empty');\n return {};\n }\n if (!getModel) {\n logger.warn('[VALIDATOR-RELATIONS] getModel function not provided, cannot fetch related data for validation');\n return {};\n }\n\n const relatedData: Record<string, any> = {};\n\n for (const relation of relations) {\n const { name, model, foreignKey, localKey = 'id', type, attributes } = relation;\n\n logger.info('[VALIDATOR-RELATIONS] Processing relation', {\n relationName: name,\n relationType: type,\n model,\n foreignKey,\n localKey,\n });\n\n // OPTIMIZATION: Check if the relation data is already loaded/set on the instance\n // This handles cases where:\n // 1. The association was eager-loaded (included in query)\n // 2. The association was set programmatically (e.g., reservation.vehicles = [...])\n // 3. Sequelize has pending association updates\n //\n // This is critical for validating updates that modify associations through helper fields\n // Example: { vehicleIds: [] } → converted to vehicles → sets reservationVehicles\n const instanceHasRelation = instance[name] !== undefined;\n logger.info('[VALIDATOR-RELATIONS] Checking if relation exists on instance', {\n relationName: name,\n hasRelation: instanceHasRelation,\n relationValue: instanceHasRelation ? instance[name] : 'NOT_SET',\n isArray: instanceHasRelation ? Array.isArray(instance[name]) : false,\n });\n\n if (instanceHasRelation) {\n const relationValue = instance[name];\n\n // Handle arrays (hasMany)\n if (Array.isArray(relationValue)) {\n relatedData[name] = relationValue;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${name}`, { count: relationValue.length });\n continue;\n }\n\n // Handle single objects (hasOne, belongsTo)\n if (relationValue !== null && typeof relationValue === 'object') {\n relatedData[name] = relationValue;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${name}`);\n continue;\n }\n\n // Handle null (relation explicitly set to null)\n if (relationValue === null) {\n relatedData[name] = null;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set null relation for ${name}`);\n continue;\n }\n }\n\n // SMART DERIVATION: Try to derive the relation from Sequelize associations\n // This handles cases where a parent association is set but not the specific relation\n // Example: instance.vehicles is set, but we need reservationVehicles\n // We can query the association to get it\n const associationMethod = `get${name.charAt(0).toUpperCase() + name.slice(1)}`;\n const hasAssociationMethod = typeof instance[associationMethod] === 'function';\n\n logger.info('[VALIDATOR-RELATIONS] Checking for Sequelize association getter', {\n relationName: name,\n methodName: associationMethod,\n hasMethod: hasAssociationMethod,\n availableMethods: Object.keys(instance).filter(k => k.startsWith('get')),\n });\n\n if (hasAssociationMethod) {\n try {\n logger.info(`[VALIDATOR-RELATIONS] Calling ${associationMethod}...`);\n const associatedData = await instance[associationMethod]({ transaction: options.transaction });\n\n logger.info(`[VALIDATOR-RELATIONS] Association getter result`, {\n methodName: associationMethod,\n hasData: associatedData !== undefined,\n isArray: Array.isArray(associatedData),\n count: Array.isArray(associatedData) ? associatedData.length : 'N/A',\n data: associatedData,\n });\n\n if (associatedData !== undefined) {\n relatedData[name] = associatedData;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Derived relation data using ${associationMethod} for ${name}`, {\n count: Array.isArray(associatedData) ? associatedData.length : 'single',\n });\n continue;\n }\n } catch (error) {\n // If derivation fails, fall through to database query\n logger.warn(`[VALIDATOR-RELATIONS] Failed to derive relation ${name}, will query database`, {\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n }\n\n // Get the model class using the consuming application's getModel function\n logger.info('[VALIDATOR-RELATIONS] Falling back to database query for relation', { relationName: name });\n\n let RelatedModel;\n try {\n RelatedModel = getModel(model);\n logger.info('[VALIDATOR-RELATIONS] Model fetched successfully', { model, hasModel: !!RelatedModel });\n } catch (error) {\n logger.warn(`[VALIDATOR-RELATIONS] Failed to get model ${model} for relation ${name}`, { error });\n continue;\n }\n\n if (!RelatedModel) {\n logger.warn(`[VALIDATOR-RELATIONS] Related model ${model} not found for validation. Ensure the model name is correct.`);\n continue;\n }\n\n // Build query based on relation type\n let where: Record<string, unknown>;\n let keyValue: unknown;\n\n if (type === 'belongsTo') {\n // For belongsTo: foreignKey is on the CURRENT model, not the related model\n // Example: Reservation.fleet_id → Fleet.id\n keyValue = instance[foreignKey];\n if (!keyValue) {\n logger.info(`[VALIDATOR-RELATIONS] Foreign key ${foreignKey} is null/undefined, skipping relation ${name}`);\n continue;\n }\n where = { [localKey]: keyValue };\n } else {\n // For hasMany/hasOne: foreignKey is on the RELATED model\n // Example: Reservation.id → ReservationVehicles.reservation_id\n keyValue = instance[localKey];\n if (!keyValue) {\n logger.info(`[VALIDATOR-RELATIONS] Local key ${localKey} is null/undefined, skipping relation ${name}`);\n continue;\n }\n where = { [foreignKey]: keyValue };\n }\n\n const query = {\n where,\n transaction: options.transaction,\n ...(attributes && { attributes }),\n raw: true,\n };\n\n logger.info('[VALIDATOR-RELATIONS] Executing database query', {\n relationName: name,\n relationType: type,\n query: { where, attributes },\n });\n\n try {\n // Fetch data\n if (type === 'hasMany') {\n const records = await RelatedModel.findAll(query);\n relatedData[name] = records;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${records.length} records from database for ${name}`);\n } else if (type === 'hasOne' || type === 'belongsTo') {\n const record = await RelatedModel.findOne(query);\n relatedData[name] = record || null;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${record ? 'one' : 'no'} record from database for ${name}`);\n }\n } catch (error) {\n logger.error(`[VALIDATOR-RELATIONS] Error fetching related data for ${name}`, { error, relation });\n // Continue with validation even if related data fetch fails\n relatedData[name] = type === 'hasMany' ? [] : null;\n }\n }\n\n logger.info('[VALIDATOR-RELATIONS] fetchRelatedData complete', {\n relationsProcessed: Object.keys(relatedData),\n summary: Object.entries(relatedData).map(([key, val]) => ({\n name: key,\n count: Array.isArray(val) ? val.length : (val ? 'single' : 'null'),\n })),\n });\n\n return relatedData;\n};\n\n/**\n * Fetches related data for the 'before' state using original values\n * This handles cases where the FK might have changed\n */\nconst fetchRelatedDataForBeforeState = async (\n instance: any,\n relations: RelationConfig[],\n originalValues: any,\n options: TransactionOptions,\n getModel?: ModelFetcher,\n): Promise<Record<string, any>> => {\n if (!relations || relations.length === 0) return {};\n if (!getModel) return {};\n\n const relatedData: Record<string, any> = {};\n\n for (const relation of relations) {\n const { name, model, foreignKey, localKey = 'id', type, attributes } = relation;\n\n let RelatedModel;\n try {\n RelatedModel = getModel(model);\n } catch (error) {\n logger.warn(`Failed to get model ${model} for before state`, { error });\n continue;\n }\n\n if (!RelatedModel) continue;\n\n // Build query based on relation type using ORIGINAL values\n let where: Record<string, unknown>;\n let keyValue: unknown;\n\n if (type === 'belongsTo') {\n // For belongsTo: foreignKey is on the CURRENT model, use original FK value\n keyValue = originalValues[foreignKey];\n if (!keyValue) continue;\n where = { [localKey]: keyValue };\n } else {\n // For hasMany/hasOne: use original local key value\n keyValue = originalValues[localKey];\n if (!keyValue) continue;\n where = { [foreignKey]: keyValue };\n }\n\n const query = {\n where,\n transaction: options.transaction,\n ...(attributes && { attributes }),\n raw: true,\n };\n\n try {\n if (type === 'hasMany') {\n relatedData[name] = await RelatedModel.findAll(query);\n } else {\n relatedData[name] = await RelatedModel.findOne(query) || null;\n }\n } catch (error) {\n logger.error(`Error fetching before state for ${name}`, { error });\n relatedData[name] = type === 'hasMany' ? [] : null;\n }\n }\n\n return relatedData;\n};\n\n/**\n * Flattens related data into dot-notation for validation\n * Input: { reservationVehicles: [{ id: 1, vehicle_id: 'v1', status: 'confirmed' }, {...}] }\n * Output: {\n * 'reservationVehicles.count': 2,\n * 'reservationVehicles.0.vehicle_id': 'v1',\n * 'reservationVehicles.0.status': 'confirmed',\n * 'reservationVehicles': [...] // Keep original too\n * }\n */\nconst flattenRelatedData = (\n relatedData: Record<string, any>,\n): Record<string, any> => {\n const flattened: Record<string, any> = {};\n\n for (const [relationName, data] of Object.entries(relatedData)) {\n if (Array.isArray(data)) {\n // Add count aggregate\n flattened[`${relationName}.count`] = data.length;\n\n // Add indexed access\n data.forEach((record, index) => {\n for (const [key, value] of Object.entries(record)) {\n flattened[`${relationName}.${index}.${key}`] = value;\n }\n });\n\n // Keep original array\n flattened[relationName] = data;\n } else if (data !== null) {\n // Single record (hasOne/belongsTo)\n for (const [key, value] of Object.entries(data)) {\n flattened[`${relationName}.${key}`] = value;\n }\n flattened[relationName] = data;\n }\n }\n\n return flattened;\n};\n\n/**\n * Validates the model using custom validators\n */\nconst validateModel = async (\n instance: any,\n options: TransactionOptions,\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n isCreate = false,\n): Promise<void> => {\n const modelType = instance.constructor.name;\n\n logger.debug('sadot - validating model', { isCreate, modelType });\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n logger.debug('sadot - identifiers', { identifiers });\n\n // Skip if no identifiers\n if (!identifiers || Object.keys(identifiers).length === 0) {\n logger.debug('sadot - skipping validation: no identifiers');\n return;\n }\n\n // Find the entityId from identifiers (fleetId, businessModelId, etc.)\n const entityId = Object.values(identifiers)[0]; // Get the first value as entityId\n\n logger.debug('sadot - entityId', { entityId });\n\n if (!entityId) {\n logger.debug('sadot - skipping validation: no entityId');\n return;\n }\n\n let validatorsPromise;\n let cacheKey: string | undefined;\n if (options.transaction) {\n options.transaction.validationsCache ??= new Map();\n cacheKey = `${modelType}-${entityId}`;\n validatorsPromise = options.transaction.validationsCache.get(cacheKey);\n }\n\n if (!validatorsPromise) {\n validatorsPromise = ValidatorRepo.findAllByModelType(\n modelType,\n entityId,\n {\n transaction: options.transaction,\n attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,\n modelOptions,\n },\n );\n if (options.transaction) {\n options?.transaction?.validationsCache!.set(cacheKey!, validatorsPromise);\n }\n }\n const validators = await validatorsPromise;\n\n logger.debug('sadot - validators found', { count: validators.length });\n\n if (!validators.length) {\n logger.debug('sadot - skipping validation: no validators found');\n return;\n }\n\n // For updates, get the previous values\n let originalValues = null;\n if (!isCreate) {\n originalValues = buildPreChangeState(instance);\n }\n\n // Get complete custom fields by merging DB values with update values\n // This is especially important for partial updates to ensure all related fields are available\n const completeCustomFields = !isCreate\n ? await getCompleteCustomFields(instance, options)\n : instance.customFields || {};\n\n // Extract getModel from modelOptions for relation fetching\n const { getModel } = modelOptions;\n\n for (const validator of validators) {\n const { schema } = validator;\n const typedSchema = schema as Record<string, any>;\n const relations = typedSchema.relations || [];\n\n logger.info('[VALIDATOR-RELATIONS] Processing validator schema', {\n validatorId: validator.id,\n hasAfterProps: !!typedSchema.properties?.after,\n hasBeforeProps: !!typedSchema.properties?.before,\n hasRelations: relations.length > 0,\n relations: relations.map((r: RelationConfig) => ({ name: r.name, type: r.type, model: r.model })),\n });\n\n if (isCreate) {\n // For create operations, we only need the 'after' state\n if (typedSchema.properties?.after) {\n // Fetch related data for 'after' state\n const relatedData = await fetchRelatedData(instance, relations, options, getModel);\n const flatRelated = flattenRelatedData(relatedData);\n\n const validateSchema = ajv.compile({\n ...schema,\n // Focus only on the 'after' validation part for create\n properties: {\n after: typedSchema.properties.after,\n },\n });\n\n const isValid = validateSchema(JSON.parse(JSON.stringify({\n after: {\n ...instance.dataValues,\n customFields: completeCustomFields,\n ...flatRelated,\n },\n })));\n\n if (!isValid) {\n const errorDetails = validateSchema.errors?.map(err =>\n `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n } else {\n // For update operations, we need both before and after\n\n logger.info('[VALIDATOR-RELATIONS] Fetching related data for BEFORE state');\n // Fetch related data for BOTH before and after states\n const beforeRelatedData = await fetchRelatedDataForBeforeState(\n instance,\n relations,\n originalValues,\n options,\n getModel,\n );\n const flatBeforeRelated = flattenRelatedData(beforeRelatedData);\n logger.info('[VALIDATOR-RELATIONS] Before state related data', {\n raw: beforeRelatedData,\n flattened: flatBeforeRelated,\n });\n\n logger.info('[VALIDATOR-RELATIONS] Fetching related data for AFTER state');\n const afterRelatedData = await fetchRelatedData(instance, relations, options, getModel);\n const flatAfterRelated = flattenRelatedData(afterRelatedData);\n logger.info('[VALIDATOR-RELATIONS] After state related data', {\n raw: afterRelatedData,\n flattened: flatAfterRelated,\n });\n\n const validateSchema = ajv.compile(typedSchema);\n\n // Create after object with our helper function\n const afterObj = manualObjectCopy(instance.dataValues);\n\n // Add complete custom fields\n afterObj.customFields = completeCustomFields;\n\n // Create validation payload with related data\n const payload = {\n before: {\n ...originalValues,\n ...flatBeforeRelated,\n },\n after: {\n ...afterObj,\n ...flatAfterRelated,\n },\n };\n\n logger.info('[VALIDATOR-RELATIONS] Final validation payload', {\n payload: JSON.stringify(payload, null, 2),\n });\n\n // Validate\n const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));\n\n logger.info('[VALIDATOR-RELATIONS] Validation result', {\n isValid,\n errors: isValid ? null : validateSchema.errors,\n });\n\n if (!isValid) {\n const errorDetails = validateSchema\n .errors\n ?.map(err => `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n }\n};\n\nconst getFieldDefinitions = async ({\n modelType,\n modelOptions,\n identifiers,\n options,\n}: {\n modelType: any;\n modelOptions: ModelOptions;\n identifiers: any[];\n options: any;\n}) => {\n const { include, useEntityIdFromInclude } = modelOptions;\n const where: WhereOptions = {\n modelType,\n disabled: false,\n ...(!useEntityIdFromInclude && { entityId: identifiers }),\n };\n\n const fieldDefinitions = await DefinitionRepo.findAll(where, {\n withDisabled: false,\n transaction: options.transaction,\n include: include?.(identifiers),\n });\n return fieldDefinitions;\n};\n\nconst formatDates = (fieldDefinitions: CustomFieldDefinition[], instance: any) => {\n (fieldDefinitions || []).forEach((fieldDefinition) => {\n const { fieldType, name } = fieldDefinition;\n if ([CustomFieldDefinitionType.DATE, CustomFieldDefinitionType.DATETIME].includes(fieldType)) {\n const value = instance.customFields?.[name];\n if (value) {\n const { value: joiValue, error: validationError } = Joi.date().validate(value);\n if (validationError) {\n throw new InvalidValueError(value, name, validationError);\n }\n instance.customFields[name] = joiValue.toISOString();\n }\n }\n });\n};\n\n/**\n * Hook to handle validation and custom fields during creation\n */\nexport const beforeCreate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: CreateOptions,\n): Promise<void> => {\n logger.debug('sadot - before create hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n // Step 1: Handle custom fields default values and required fields\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Apply default values\n const fieldsWithDefaultValue = fieldDefinitions.filter(def => ![null, undefined].includes(def.defaultValue));\n if (fieldsWithDefaultValue.length) {\n instance.customFields ||= {};\n fieldsWithDefaultValue\n .filter(def => (instance.customFields?.[def.name] === undefined))\n .forEach(({ name, defaultValue }) => {\n instance.customFields[name] = defaultValue;\n });\n }\n\n // Check for required fields\n let requiredFieldsNames: string[];\n\n if (sadotOptions.getRequiredCustomFields) {\n // Use consumer-provided logic to determine required fields\n requiredFieldsNames = await Promise.resolve(\n sadotOptions.getRequiredCustomFields(instance, fieldDefinitions, options),\n );\n } else {\n // Default behavior: use required flag from fieldDefinitions\n requiredFieldsNames = Array.from(\n new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),\n );\n }\n\n const { customFields } = instance;\n const fieldsNames = Object.keys(customFields ?? {});\n const missingFields = requiredFieldsNames.filter(name => !fieldsNames.includes(name));\n if (missingFields?.length) {\n throw new MissingRequiredCustomFieldError(missingFields);\n }\n\n // Step 2: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, true);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 3: Save custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {\n // No custom fields to update\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n};\n\n/**\n * Hook to handle validation and custom fields during update\n */\nexport const beforeUpdate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: UpdateOptions,\n): Promise<void> => {\n logger.debug('sadot - before update hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Step 1: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, false);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 2: Update custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx > -1) {\n const { customFields } = instance;\n\n if (!Object.keys(customFields).length) {\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n }\n};\n\n/**\n * Hook to enable individual hooks for bulk create operations\n */\nexport const beforeBulkCreate = (options: BulkCreateOptions): void => {\n // This will activate the beforeCreate hook on each instance\n options.individualHooks = true;\n};\n\n/**\n * Hook to enable individual hooks for bulk update operations\n */\nexport const beforeBulkUpdate = (options: UpdateOptions): void => {\n // This will activate the beforeUpdate hook on each instance\n options.individualHooks = true;\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import e from"../utils/logger/index.js";import{CustomFieldDefinitionType as t}from"../utils/constants/index.js";import{InvalidValueError as n,MissingRequiredCustomFieldError as r}from"../errors/index.js";import{findAll as i}from"../repository/definition.js";import{findAllByModelType as a}from"../repository/validator.js";import o from"../utils/scopeAttributes.js";import s from"./utils/updateInstanceValues.js";import c from"joi";import{BadRequest as l}from"@autofleet/errors";import u from"ajv";import d from"ajv-formats";import f from"ajv-errors";const p=[`id`,`schema`,`modelType`,`entityId`,`disabled`],m=new u({allErrors:!0,strict:!1,strictTypes:!1,$data:!0});d(m),f(m);const h=(e,t)=>({__proto__:null,...e,...t}),g=async(t,n)=>{if(!t.id||!t.customFields||Object.keys(t.customFields).length===0)return t.customFields||{};try{let r=await t.constructor.findOne({where:{id:t.id},attributes:[`customFields`],transaction:n.transaction,raw:!0});if(r?.customFields){let n=h(r.customFields,t.customFields);return e.debug(`sadot - fetched complete custom fields for validation`,{fieldsCount:Object.keys(n).length,updateFieldsCount:Object.keys(t.customFields).length}),n}}catch(t){e.error(`sadot - error fetching complete model for validation`,{error:t})}return t.customFields||{}},_=e=>{let t={...e.dataValues};(e.changed?.()||[]).forEach(n=>{let r=e.previous?.(n);r!==void 0&&(t[n]=r)});let n=e.previous?.(`customFields`);return n!==void 0&&(t.customFields=n),t},v=e=>e.reduce((e,t)=>{let n=((t.instancePath||``).split(`/`).filter(Boolean).join(`.`).replace(/^after\./,``)+(t.keyword===`required`?`.${t.params?.missingProperty}`:``)).replace(/^\./,``)||`root`;return e[n]=t.message||`Invalid value`,e},{}),y=async(t,n,r,i={},s=!1)=>{let c=t.constructor.name;e.debug(`sadot - validating model`,{isCreate:s,modelType:c});let u=o(t,r);if(e.debug(`sadot - identifiers`,{identifiers:u}),!u||Object.keys(u).length===0){e.debug(`sadot - skipping validation: no identifiers`);return}let d=Object.values(u)[0];if(e.debug(`sadot - entityId`,{entityId:d}),!d){e.debug(`sadot - skipping validation: no entityId`);return}let f,y;n.transaction&&(n.transaction.validationsCache??=new Map,y=`${c}-${d}`,f=n.transaction.validationsCache.get(y)),f||(f=a(c,d,{transaction:n.transaction,attributes:p,modelOptions:i}),n.transaction&&n?.transaction?.validationsCache.set(y,f));let b=await f;if(e.debug(`sadot - validators found`,{count:b.length}),!b.length){e.debug(`sadot - skipping validation: no validators found`);return}let x=null;s||(x=_(t));let S=s?t.customFields||{}:await g(t,n);for(let n of b){let{schema:r}=n,i=r;if(e.debug(`sadot - validating with schema`,{schema:r,hasAfterProps:!!i.properties?.after,hasBeforeProps:!!i.properties?.before}),s){if(i.properties?.after){let e=m.compile({...r,properties:{after:i.properties.after}});if(!e(JSON.parse(JSON.stringify({after:{...t.dataValues,customFields:S}})))){let t=e.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),n=v(e.errors);throw new l([Error(`Validation failed for ${c}: ${t}`)],void 0,{customError:n})}}}else{let n=m.compile(i),r=h(t.dataValues);r.customFields=S;let a={before:x,after:r},o=n(JSON.parse(JSON.stringify(a)));if(e.debug(`sadot - validation result`,{isValid:o,test:{before:x,after:r}}),!o){let e=n.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(n.errors);throw new l([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}},b=async({modelType:e,modelOptions:t,identifiers:n,options:r})=>{let{include:a,useEntityIdFromInclude:o}=t;return await i({modelType:e,disabled:!1,...!o&&{entityId:n}},{withDisabled:!1,transaction:r.transaction,include:a?.(n)})},x=(e,r)=>{(e||[]).forEach(e=>{let{fieldType:i,name:a}=e;if([t.DATE,t.DATETIME].includes(i)){let e=r.customFields?.[a];if(e){let{value:t,error:i}=c.date().validate(e);if(i)throw new n(e,a,i);r.customFields[a]=t.toISOString()}}})},S=(t,n={},i={useCustomFieldsEntries:!1})=>async(a,c)=>{e.debug(`sadot - before create hook`);let{fields:l}=c,u=a.constructor.name,d=o(a,t),f=await b({modelType:u,modelOptions:n,identifiers:d,options:c}),p=f.filter(e=>![null,void 0].includes(e.defaultValue));p.length&&(a.customFields||={},p.filter(e=>a.customFields?.[e.name]===void 0).forEach(({name:e,defaultValue:t})=>{a.customFields[e]=t}));let m;m=i.getRequiredCustomFields?await Promise.resolve(i.getRequiredCustomFields(a,f,c)):Array.from(new Set(f.filter(({required:e})=>e).map(({name:e})=>e)));let{customFields:h}=a,g=Object.keys(h??{}),_=m.filter(e=>!g.includes(e));if(_?.length)throw new r(_);await y(a,c,t,n,!0),x(f,a);let v=l.indexOf(`customFields`);v===-1||!h||!Object.keys(h).length||(await s({modelId:a.id,modelType:u,identifiers:d,customFields:h,options:{useCustomFieldsEntries:i.useCustomFieldsEntries,transaction:c.transaction,modelOptions:n}}),l.splice(v,1))},C=(t,n={},r={useCustomFieldsEntries:!1})=>async(i,a)=>{e.debug(`sadot - before update hook`);let{fields:c}=a,l=i.constructor.name,u=o(i,t),d=await b({modelType:l,modelOptions:n,identifiers:u,options:a});await y(i,a,t,n,!1),x(d,i);let f=c.indexOf(`customFields`);if(f>-1){let{customFields:e}=i;if(!Object.keys(e).length)return;await s({modelId:i.id,modelType:l,identifiers:u,customFields:e,options:{useCustomFieldsEntries:r.useCustomFieldsEntries,transaction:a.transaction,modelOptions:n}}),c.splice(f,1)}},w=e=>{e.individualHooks=!0},T=e=>{e.individualHooks=!0};export{w as beforeBulkCreate,T as beforeBulkUpdate,S as beforeCreate,C as beforeUpdate};
1
+ import e from"../utils/logger/index.js";import{CustomFieldDefinitionType as t}from"../utils/constants/index.js";import{InvalidValueError as n,MissingRequiredCustomFieldError as r}from"../errors/index.js";import{findAll as i}from"../repository/definition.js";import{findAllByModelType as a}from"../repository/validator.js";import o from"../utils/scopeAttributes.js";import s from"./utils/updateInstanceValues.js";import c from"joi";import{BadRequest as l}from"@autofleet/errors";import u from"ajv";import d from"ajv-formats";import f from"ajv-errors";const p=[`id`,`schema`,`modelType`,`entityId`,`disabled`],m=new u({allErrors:!0,strict:!1,strictTypes:!1,$data:!0});d(m),f(m);const h=(e,t)=>({__proto__:null,...e,...t}),g=async(t,n)=>{if(!t.id||!t.customFields||Object.keys(t.customFields).length===0)return t.customFields||{};try{let r=await t.constructor.findOne({where:{id:t.id},attributes:[`customFields`],transaction:n.transaction,raw:!0});if(r?.customFields){let n=h(r.customFields,t.customFields);return e.debug(`sadot - fetched complete custom fields for validation`,{fieldsCount:Object.keys(n).length,updateFieldsCount:Object.keys(t.customFields).length}),n}}catch(t){e.error(`sadot - error fetching complete model for validation`,{error:t})}return t.customFields||{}},_=e=>{let t={...e.dataValues};(e.changed?.()||[]).forEach(n=>{let r=e.previous?.(n);r!==void 0&&(t[n]=r)});let n=e.previous?.(`customFields`);return n!==void 0&&(t.customFields=n),t},v=e=>e.reduce((e,t)=>{let n=((t.instancePath||``).split(`/`).filter(Boolean).join(`.`).replace(/^after\./,``)+(t.keyword===`required`?`.${t.params?.missingProperty}`:``)).replace(/^\./,``)||`root`;return e[n]=t.message||`Invalid value`,e},{}),y=async(t,n,r,i)=>{if(e.info(`[VALIDATOR-RELATIONS] fetchRelatedData called`,{relationCount:n?.length||0,hasGetModel:!!i,instanceId:t?.id,instanceConstructor:t?.constructor?.name}),!n||n.length===0)return e.info(`[VALIDATOR-RELATIONS] No relations to fetch, returning empty`),{};if(!i)return e.warn(`[VALIDATOR-RELATIONS] getModel function not provided, cannot fetch related data for validation`),{};let a={};for(let o of n){let{name:n,model:s,foreignKey:c,localKey:l=`id`,type:u,attributes:d}=o;e.info(`[VALIDATOR-RELATIONS] Processing relation`,{relationName:n,relationType:u,model:s,foreignKey:c,localKey:l});let f=t[n]!==void 0;if(e.info(`[VALIDATOR-RELATIONS] Checking if relation exists on instance`,{relationName:n,hasRelation:f,relationValue:f?t[n]:`NOT_SET`,isArray:f?Array.isArray(t[n]):!1}),f){let r=t[n];if(Array.isArray(r)){a[n]=r,e.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${n}`,{count:r.length});continue}if(typeof r==`object`&&r){a[n]=r,e.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${n}`);continue}if(r===null){a[n]=null,e.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set null relation for ${n}`);continue}}let p=`get${n.charAt(0).toUpperCase()+n.slice(1)}`,m=typeof t[p]==`function`;if(e.info(`[VALIDATOR-RELATIONS] Checking for Sequelize association getter`,{relationName:n,methodName:p,hasMethod:m,availableMethods:Object.keys(t).filter(e=>e.startsWith(`get`))}),m)try{e.info(`[VALIDATOR-RELATIONS] Calling ${p}...`);let i=await t[p]({transaction:r.transaction});if(e.info(`[VALIDATOR-RELATIONS] Association getter result`,{methodName:p,hasData:i!==void 0,isArray:Array.isArray(i),count:Array.isArray(i)?i.length:`N/A`,data:i}),i!==void 0){a[n]=i,e.info(`[VALIDATOR-RELATIONS] ✓ Derived relation data using ${p} for ${n}`,{count:Array.isArray(i)?i.length:`single`});continue}}catch(t){e.warn(`[VALIDATOR-RELATIONS] Failed to derive relation ${n}, will query database`,{error:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0})}e.info(`[VALIDATOR-RELATIONS] Falling back to database query for relation`,{relationName:n});let h;try{h=i(s),e.info(`[VALIDATOR-RELATIONS] Model fetched successfully`,{model:s,hasModel:!!h})}catch(t){e.warn(`[VALIDATOR-RELATIONS] Failed to get model ${s} for relation ${n}`,{error:t});continue}if(!h){e.warn(`[VALIDATOR-RELATIONS] Related model ${s} not found for validation. Ensure the model name is correct.`);continue}let g,_;if(u===`belongsTo`){if(_=t[c],!_){e.info(`[VALIDATOR-RELATIONS] Foreign key ${c} is null/undefined, skipping relation ${n}`);continue}g={[l]:_}}else{if(_=t[l],!_){e.info(`[VALIDATOR-RELATIONS] Local key ${l} is null/undefined, skipping relation ${n}`);continue}g={[c]:_}}let v={where:g,transaction:r.transaction,...d&&{attributes:d},raw:!0};e.info(`[VALIDATOR-RELATIONS] Executing database query`,{relationName:n,relationType:u,query:{where:g,attributes:d}});try{if(u===`hasMany`){let t=await h.findAll(v);a[n]=t,e.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${t.length} records from database for ${n}`)}else if(u===`hasOne`||u===`belongsTo`){let t=await h.findOne(v);a[n]=t||null,e.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${t?`one`:`no`} record from database for ${n}`)}}catch(t){e.error(`[VALIDATOR-RELATIONS] Error fetching related data for ${n}`,{error:t,relation:o}),a[n]=u===`hasMany`?[]:null}}return e.info(`[VALIDATOR-RELATIONS] fetchRelatedData complete`,{relationsProcessed:Object.keys(a),summary:Object.entries(a).map(([e,t])=>({name:e,count:Array.isArray(t)?t.length:t?`single`:`null`}))}),a},b=async(t,n,r,i,a)=>{if(!n||n.length===0||!a)return{};let o={};for(let t of n){let{name:n,model:s,foreignKey:c,localKey:l=`id`,type:u,attributes:d}=t,f;try{f=a(s)}catch(t){e.warn(`Failed to get model ${s} for before state`,{error:t});continue}if(!f)continue;let p,m;if(u===`belongsTo`){if(m=r[c],!m)continue;p={[l]:m}}else{if(m=r[l],!m)continue;p={[c]:m}}let h={where:p,transaction:i.transaction,...d&&{attributes:d},raw:!0};try{u===`hasMany`?o[n]=await f.findAll(h):o[n]=await f.findOne(h)||null}catch(t){e.error(`Error fetching before state for ${n}`,{error:t}),o[n]=u===`hasMany`?[]:null}}return o},x=e=>{let t={};for(let[n,r]of Object.entries(e))if(Array.isArray(r))t[`${n}.count`]=r.length,r.forEach((e,r)=>{for(let[i,a]of Object.entries(e))t[`${n}.${r}.${i}`]=a}),t[n]=r;else if(r!==null){for(let[e,i]of Object.entries(r))t[`${n}.${e}`]=i;t[n]=r}return t},S=async(t,n,r,i={},s=!1)=>{let c=t.constructor.name;e.debug(`sadot - validating model`,{isCreate:s,modelType:c});let u=o(t,r);if(e.debug(`sadot - identifiers`,{identifiers:u}),!u||Object.keys(u).length===0){e.debug(`sadot - skipping validation: no identifiers`);return}let d=Object.values(u)[0];if(e.debug(`sadot - entityId`,{entityId:d}),!d){e.debug(`sadot - skipping validation: no entityId`);return}let f,S;n.transaction&&(n.transaction.validationsCache??=new Map,S=`${c}-${d}`,f=n.transaction.validationsCache.get(S)),f||(f=a(c,d,{transaction:n.transaction,attributes:p,modelOptions:i}),n.transaction&&n?.transaction?.validationsCache.set(S,f));let C=await f;if(e.debug(`sadot - validators found`,{count:C.length}),!C.length){e.debug(`sadot - skipping validation: no validators found`);return}let w=null;s||(w=_(t));let T=s?t.customFields||{}:await g(t,n),{getModel:E}=i;for(let r of C){let{schema:i}=r,a=i,o=a.relations||[];if(e.info(`[VALIDATOR-RELATIONS] Processing validator schema`,{validatorId:r.id,hasAfterProps:!!a.properties?.after,hasBeforeProps:!!a.properties?.before,hasRelations:o.length>0,relations:o.map(e=>({name:e.name,type:e.type,model:e.model}))}),s){if(a.properties?.after){let e=x(await y(t,o,n,E)),r=m.compile({...i,properties:{after:a.properties.after}});if(!r(JSON.parse(JSON.stringify({after:{...t.dataValues,customFields:T,...e}})))){let e=r.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(r.errors);throw new l([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}else{e.info(`[VALIDATOR-RELATIONS] Fetching related data for BEFORE state`);let r=await b(t,o,w,n,E),i=x(r);e.info(`[VALIDATOR-RELATIONS] Before state related data`,{raw:r,flattened:i}),e.info(`[VALIDATOR-RELATIONS] Fetching related data for AFTER state`);let s=await y(t,o,n,E),u=x(s);e.info(`[VALIDATOR-RELATIONS] After state related data`,{raw:s,flattened:u});let d=m.compile(a),f=h(t.dataValues);f.customFields=T;let p={before:{...w,...i},after:{...f,...u}};e.info(`[VALIDATOR-RELATIONS] Final validation payload`,{payload:JSON.stringify(p,null,2)});let g=d(JSON.parse(JSON.stringify(p)));if(e.info(`[VALIDATOR-RELATIONS] Validation result`,{isValid:g,errors:g?null:d.errors}),!g){let e=d.errors?.map(e=>`${e.instancePath||``} ${e.message||`Invalid value`}`).join(`, `),t=v(d.errors);throw new l([Error(`Validation failed for ${c}: ${e}`)],void 0,{customError:t})}}}},C=async({modelType:e,modelOptions:t,identifiers:n,options:r})=>{let{include:a,useEntityIdFromInclude:o}=t;return await i({modelType:e,disabled:!1,...!o&&{entityId:n}},{withDisabled:!1,transaction:r.transaction,include:a?.(n)})},w=(e,r)=>{(e||[]).forEach(e=>{let{fieldType:i,name:a}=e;if([t.DATE,t.DATETIME].includes(i)){let e=r.customFields?.[a];if(e){let{value:t,error:i}=c.date().validate(e);if(i)throw new n(e,a,i);r.customFields[a]=t.toISOString()}}})},T=(t,n={},i={useCustomFieldsEntries:!1})=>async(a,c)=>{e.debug(`sadot - before create hook`);let{fields:l}=c,u=a.constructor.name,d=o(a,t),f=await C({modelType:u,modelOptions:n,identifiers:d,options:c}),p=f.filter(e=>![null,void 0].includes(e.defaultValue));p.length&&(a.customFields||={},p.filter(e=>a.customFields?.[e.name]===void 0).forEach(({name:e,defaultValue:t})=>{a.customFields[e]=t}));let m;m=i.getRequiredCustomFields?await Promise.resolve(i.getRequiredCustomFields(a,f,c)):Array.from(new Set(f.filter(({required:e})=>e).map(({name:e})=>e)));let{customFields:h}=a,g=Object.keys(h??{}),_=m.filter(e=>!g.includes(e));if(_?.length)throw new r(_);await S(a,c,t,n,!0),w(f,a);let v=l.indexOf(`customFields`);v===-1||!h||!Object.keys(h).length||(await s({modelId:a.id,modelType:u,identifiers:d,customFields:h,options:{useCustomFieldsEntries:i.useCustomFieldsEntries,transaction:c.transaction,modelOptions:n}}),l.splice(v,1))},E=(t,n={},r={useCustomFieldsEntries:!1})=>async(i,a)=>{e.debug(`sadot - before update hook`);let{fields:c}=a,l=i.constructor.name,u=o(i,t),d=await C({modelType:l,modelOptions:n,identifiers:u,options:a});await S(i,a,t,n,!1),w(d,i);let f=c.indexOf(`customFields`);if(f>-1){let{customFields:e}=i;if(!Object.keys(e).length)return;await s({modelId:i.id,modelType:l,identifiers:u,customFields:e,options:{useCustomFieldsEntries:r.useCustomFieldsEntries,transaction:a.transaction,modelOptions:n}}),c.splice(f,1)}},D=e=>{e.individualHooks=!0},O=e=>{e.individualHooks=!0};export{D as beforeBulkCreate,O as beforeBulkUpdate,T as beforeCreate,E as beforeUpdate};
2
2
  //# sourceMappingURL=hooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","names":["beforeFull: any","applyScopeToInstance","cacheKey: string | undefined","ValidatorRepo.findAllByModelType","where: WhereOptions","DefinitionRepo.findAll","requiredFieldsNames: string[]","updateInstanceValues"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type {\n BulkCreateOptions, CreateOptions, Transactionable, UpdateOptions, WhereOptions,\n} from 'sequelize';\nimport Ajv from 'ajv';\nimport Joi from 'joi';\nimport addFormats from 'ajv-formats';\nimport { BadRequest } from '@autofleet/errors';\nimport ajvErrors from 'ajv-errors';\nimport logger from '../utils/logger';\nimport * as ValidatorRepo from '../repository/validator';\nimport * as DefinitionRepo from '../repository/definition';\nimport { InvalidValueError, MissingRequiredCustomFieldError } from '../errors';\nimport type { CustomFieldOptions, ModelOptions, TransactionOptions } from '../types';\nimport applyScopeToInstance from '../utils/scopeAttributes';\nimport updateInstanceValues from './utils/updateInstanceValues';\nimport { CustomFieldDefinitionType } from '../utils/constants';\nimport type { CustomFieldDefinition } from '../models';\n\n// Include all required fields for proper validation\nconst CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL = ['id', 'schema', 'modelType', 'entityId', 'disabled'];\n\n// Initialize Ajv with relaxed settings to avoid warnings\nconst ajv = new Ajv({\n allErrors: true,\n strict: false, // Disable strict mode to avoid warnings\n strictTypes: false, // Disable strict type checking\n $data: true, // Enable $data references\n});\n\naddFormats(ajv);\najvErrors(ajv);\n\n/**\n * Helper function to manually copy object properties\n * This is more efficient for large objects and avoids excessive object creation\n */\nconst manualObjectCopy = (sourceObj: Record<string, any>, additionalProps?: Record<string, any>): Record<string, any> =>\n ({ __proto__: null, ...sourceObj, ...additionalProps });\n\n/**\n * Fetches complete custom fields for an instance by merging DB values with update values\n * This is needed for partial updates to ensure all related fields are available for validation\n */\nconst getCompleteCustomFields = async (instance: any, options: Transactionable): Promise<Record<string, any>> => {\n // If we don't have an instance id or no custom fields being updated, return original fields\n if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {\n return instance.customFields || {};\n }\n\n try {\n const ModelClass = instance.constructor;\n // Only select the customFields column to minimize data transfer\n const currentCustomFields = await ModelClass.findOne({\n where: { id: instance.id },\n attributes: ['customFields'],\n transaction: options.transaction,\n raw: true, // Get plain object instead of model instance for better performance\n });\n\n if (currentCustomFields?.customFields) {\n // Merge existing fields with update fields using our helper function\n const completeFields = manualObjectCopy(\n currentCustomFields.customFields,\n instance.customFields,\n );\n\n logger.debug('sadot - fetched complete custom fields for validation', {\n fieldsCount: Object.keys(completeFields).length,\n updateFieldsCount: Object.keys(instance.customFields).length,\n });\n\n return completeFields;\n }\n } catch (error) {\n logger.error('sadot - error fetching complete model for validation', { error });\n // Continue with partial data if we can't fetch the complete model\n }\n\n return instance.customFields || {};\n};\n\nconst buildPreChangeState = (instance: any) => {\n const beforeFull: any = { ...instance.dataValues };\n\n const changedKeys: string[] = instance.changed?.() || [];\n changedKeys.forEach((key) => {\n const prevVal = instance.previous?.(key);\n if (prevVal !== undefined) {\n beforeFull[key] = prevVal;\n }\n });\n\n const prevCF = instance.previous?.('customFields');\n if (prevCF !== undefined) {\n beforeFull.customFields = prevCF;\n }\n\n return beforeFull;\n};\n\nconst formatAjvErrors = (\n errors: {\n instancePath?: string;\n keyword: string;\n message?: string;\n params?: Record<string, any>;\n }[],\n): Record<string, string> => errors.reduce((acc, err) => {\n const basePath = (err.instancePath || '')\n .split('/')\n .filter(Boolean)\n .join('.')\n .replace(/^after\\./, '');\n\n const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';\n const key = (basePath + missingProp).replace(/^\\./, '') || 'root';\n\n const message = err.message || 'Invalid value';\n acc[key] = message;\n\n return acc;\n}, {} as Record<string, string>);\n\n/**\n * Validates the model using custom validators\n */\nconst validateModel = async (\n instance: any,\n options: TransactionOptions,\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n isCreate = false,\n): Promise<void> => {\n const modelType = instance.constructor.name;\n\n logger.debug('sadot - validating model', { isCreate, modelType });\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n logger.debug('sadot - identifiers', { identifiers });\n\n // Skip if no identifiers\n if (!identifiers || Object.keys(identifiers).length === 0) {\n logger.debug('sadot - skipping validation: no identifiers');\n return;\n }\n\n // Find the entityId from identifiers (fleetId, businessModelId, etc.)\n const entityId = Object.values(identifiers)[0]; // Get the first value as entityId\n\n logger.debug('sadot - entityId', { entityId });\n\n if (!entityId) {\n logger.debug('sadot - skipping validation: no entityId');\n return;\n }\n\n let validatorsPromise;\n let cacheKey: string | undefined;\n if (options.transaction) {\n options.transaction.validationsCache ??= new Map();\n cacheKey = `${modelType}-${entityId}`;\n validatorsPromise = options.transaction.validationsCache.get(cacheKey);\n }\n\n if (!validatorsPromise) {\n validatorsPromise = ValidatorRepo.findAllByModelType(\n modelType,\n entityId,\n {\n transaction: options.transaction,\n attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,\n modelOptions,\n },\n );\n if (options.transaction) {\n options?.transaction?.validationsCache!.set(cacheKey!, validatorsPromise);\n }\n }\n const validators = await validatorsPromise;\n\n logger.debug('sadot - validators found', { count: validators.length });\n\n if (!validators.length) {\n logger.debug('sadot - skipping validation: no validators found');\n return;\n }\n\n // For updates, get the previous values\n let originalValues = null;\n if (!isCreate) {\n originalValues = buildPreChangeState(instance);\n }\n\n // Get complete custom fields by merging DB values with update values\n // This is especially important for partial updates to ensure all related fields are available\n const completeCustomFields = !isCreate\n ? await getCompleteCustomFields(instance, options)\n : instance.customFields || {};\n\n for (const validator of validators) {\n const { schema } = validator;\n const typedSchema = schema as Record<string, any>;\n\n logger.debug('sadot - validating with schema', {\n schema,\n hasAfterProps: !!typedSchema.properties?.after,\n hasBeforeProps: !!typedSchema.properties?.before,\n });\n\n if (isCreate) {\n // For create operations, we only need the 'after' state\n if (typedSchema.properties?.after) {\n const validateSchema = ajv.compile({\n ...schema,\n // Focus only on the 'after' validation part for create\n properties: {\n after: typedSchema.properties.after,\n },\n });\n\n const isValid = validateSchema(JSON.parse(JSON.stringify({\n after: {\n ...instance.dataValues,\n customFields: completeCustomFields,\n },\n })));\n\n if (!isValid) {\n const errorDetails = validateSchema.errors?.map(err =>\n `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n } else {\n // For update operations, we need both before and after\n const validateSchema = ajv.compile(typedSchema);\n\n // Create after object with our helper function\n const afterObj = manualObjectCopy(instance.dataValues);\n\n // Add complete custom fields\n afterObj.customFields = completeCustomFields;\n\n // Create validation payload\n const payload = {\n before: originalValues,\n after: afterObj,\n };\n\n // Validate\n const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));\n\n logger.debug('sadot - validation result', {\n isValid,\n test: {\n before: originalValues,\n after: afterObj,\n },\n });\n\n if (!isValid) {\n const errorDetails = validateSchema\n .errors\n ?.map(err => `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n }\n};\n\nconst getFieldDefinitions = async ({\n modelType,\n modelOptions,\n identifiers,\n options,\n}: {\n modelType: any;\n modelOptions: ModelOptions;\n identifiers: any[];\n options: any;\n}) => {\n const { include, useEntityIdFromInclude } = modelOptions;\n const where: WhereOptions = {\n modelType,\n disabled: false,\n ...(!useEntityIdFromInclude && { entityId: identifiers }),\n };\n\n const fieldDefinitions = await DefinitionRepo.findAll(where, {\n withDisabled: false,\n transaction: options.transaction,\n include: include?.(identifiers),\n });\n return fieldDefinitions;\n};\n\nconst formatDates = (fieldDefinitions: CustomFieldDefinition[], instance: any) => {\n (fieldDefinitions || []).forEach((fieldDefinition) => {\n const { fieldType, name } = fieldDefinition;\n if ([CustomFieldDefinitionType.DATE, CustomFieldDefinitionType.DATETIME].includes(fieldType)) {\n const value = instance.customFields?.[name];\n if (value) {\n const { value: joiValue, error: validationError } = Joi.date().validate(value);\n if (validationError) {\n throw new InvalidValueError(value, name, validationError);\n }\n instance.customFields[name] = joiValue.toISOString();\n }\n }\n });\n};\n\n/**\n * Hook to handle validation and custom fields during creation\n */\nexport const beforeCreate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: CreateOptions,\n): Promise<void> => {\n logger.debug('sadot - before create hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n // Step 1: Handle custom fields default values and required fields\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Apply default values\n const fieldsWithDefaultValue = fieldDefinitions.filter(def => ![null, undefined].includes(def.defaultValue));\n if (fieldsWithDefaultValue.length) {\n instance.customFields ||= {};\n fieldsWithDefaultValue\n .filter(def => (instance.customFields?.[def.name] === undefined))\n .forEach(({ name, defaultValue }) => {\n instance.customFields[name] = defaultValue;\n });\n }\n\n // Check for required fields\n let requiredFieldsNames: string[];\n\n if (sadotOptions.getRequiredCustomFields) {\n // Use consumer-provided logic to determine required fields\n requiredFieldsNames = await Promise.resolve(\n sadotOptions.getRequiredCustomFields(instance, fieldDefinitions, options),\n );\n } else {\n // Default behavior: use required flag from fieldDefinitions\n requiredFieldsNames = Array.from(\n new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),\n );\n }\n\n const { customFields } = instance;\n const fieldsNames = Object.keys(customFields ?? {});\n const missingFields = requiredFieldsNames.filter(name => !fieldsNames.includes(name));\n if (missingFields?.length) {\n throw new MissingRequiredCustomFieldError(missingFields);\n }\n\n // Step 2: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, true);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 3: Save custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {\n // No custom fields to update\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n};\n\n/**\n * Hook to handle validation and custom fields during update\n */\nexport const beforeUpdate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: UpdateOptions,\n): Promise<void> => {\n logger.debug('sadot - before update hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Step 1: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, false);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 2: Update custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx > -1) {\n const { customFields } = instance;\n\n if (!Object.keys(customFields).length) {\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n }\n};\n\n/**\n * Hook to enable individual hooks for bulk create operations\n */\nexport const beforeBulkCreate = (options: BulkCreateOptions): void => {\n // This will activate the beforeCreate hook on each instance\n options.individualHooks = true;\n};\n\n/**\n * Hook to enable individual hooks for bulk update operations\n */\nexport const beforeBulkUpdate = (options: UpdateOptions): void => {\n // This will activate the beforeUpdate hook on each instance\n options.individualHooks = true;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"hooks.js","names":["beforeFull: any","relatedData: Record<string, any>","where: Record<string, unknown>","keyValue: unknown","flattened: Record<string, any>","applyScopeToInstance","cacheKey: string | undefined","ValidatorRepo.findAllByModelType","where: WhereOptions","DefinitionRepo.findAll","requiredFieldsNames: string[]","updateInstanceValues"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type {\n BulkCreateOptions, CreateOptions, Transactionable, UpdateOptions, WhereOptions,\n} from 'sequelize';\nimport Ajv from 'ajv';\nimport Joi from 'joi';\nimport addFormats from 'ajv-formats';\nimport { BadRequest } from '@autofleet/errors';\nimport ajvErrors from 'ajv-errors';\nimport logger from '../utils/logger';\nimport * as ValidatorRepo from '../repository/validator';\nimport * as DefinitionRepo from '../repository/definition';\nimport { InvalidValueError, MissingRequiredCustomFieldError } from '../errors';\nimport type { CustomFieldOptions, ModelOptions, TransactionOptions, ModelFetcher } from '../types';\nimport applyScopeToInstance from '../utils/scopeAttributes';\nimport updateInstanceValues from './utils/updateInstanceValues';\nimport { CustomFieldDefinitionType } from '../utils/constants';\nimport type { CustomFieldDefinition } from '../models';\nimport type { RelationConfig } from '../api/v1/validator/validations';\n\n// Include all required fields for proper validation\nconst CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL = ['id', 'schema', 'modelType', 'entityId', 'disabled'];\n\n// Initialize Ajv with relaxed settings to avoid warnings\nconst ajv = new Ajv({\n allErrors: true,\n strict: false, // Disable strict mode to avoid warnings\n strictTypes: false, // Disable strict type checking\n $data: true, // Enable $data references\n});\n\naddFormats(ajv);\najvErrors(ajv);\n\n/**\n * Helper function to manually copy object properties\n * This is more efficient for large objects and avoids excessive object creation\n */\nconst manualObjectCopy = (sourceObj: Record<string, any>, additionalProps?: Record<string, any>): Record<string, any> =>\n ({ __proto__: null, ...sourceObj, ...additionalProps });\n\n/**\n * Fetches complete custom fields for an instance by merging DB values with update values\n * This is needed for partial updates to ensure all related fields are available for validation\n */\nconst getCompleteCustomFields = async (instance: any, options: Transactionable): Promise<Record<string, any>> => {\n // If we don't have an instance id or no custom fields being updated, return original fields\n if (!instance.id || !instance.customFields || Object.keys(instance.customFields).length === 0) {\n return instance.customFields || {};\n }\n\n try {\n const ModelClass = instance.constructor;\n // Only select the customFields column to minimize data transfer\n const currentCustomFields = await ModelClass.findOne({\n where: { id: instance.id },\n attributes: ['customFields'],\n transaction: options.transaction,\n raw: true, // Get plain object instead of model instance for better performance\n });\n\n if (currentCustomFields?.customFields) {\n // Merge existing fields with update fields using our helper function\n const completeFields = manualObjectCopy(\n currentCustomFields.customFields,\n instance.customFields,\n );\n\n logger.debug('sadot - fetched complete custom fields for validation', {\n fieldsCount: Object.keys(completeFields).length,\n updateFieldsCount: Object.keys(instance.customFields).length,\n });\n\n return completeFields;\n }\n } catch (error) {\n logger.error('sadot - error fetching complete model for validation', { error });\n // Continue with partial data if we can't fetch the complete model\n }\n\n return instance.customFields || {};\n};\n\nconst buildPreChangeState = (instance: any) => {\n const beforeFull: any = { ...instance.dataValues };\n\n const changedKeys: string[] = instance.changed?.() || [];\n changedKeys.forEach((key) => {\n const prevVal = instance.previous?.(key);\n if (prevVal !== undefined) {\n beforeFull[key] = prevVal;\n }\n });\n\n const prevCF = instance.previous?.('customFields');\n if (prevCF !== undefined) {\n beforeFull.customFields = prevCF;\n }\n\n return beforeFull;\n};\n\nconst formatAjvErrors = (\n errors: {\n instancePath?: string;\n keyword: string;\n message?: string;\n params?: Record<string, any>;\n }[],\n): Record<string, string> => errors.reduce((acc, err) => {\n const basePath = (err.instancePath || '')\n .split('/')\n .filter(Boolean)\n .join('.')\n .replace(/^after\\./, '');\n\n const missingProp = err.keyword === 'required' ? `.${err.params?.missingProperty}` : '';\n const key = (basePath + missingProp).replace(/^\\./, '') || 'root';\n\n const message = err.message || 'Invalid value';\n acc[key] = message;\n\n return acc;\n}, {} as Record<string, string>);\n\n/**\n * Fetches related data for validation based on schema relations config\n *\n * @param instance - The model instance being validated\n * @param relations - Array of relation configurations from the schema\n * @param options - Transaction options\n * @param getModel - Model fetcher function from consuming application\n * @returns Object containing related data keyed by relation name\n */\nconst fetchRelatedData = async (\n instance: any,\n relations: RelationConfig[],\n options: TransactionOptions,\n getModel?: ModelFetcher,\n): Promise<Record<string, any>> => {\n logger.info('[VALIDATOR-RELATIONS] fetchRelatedData called', {\n relationCount: relations?.length || 0,\n hasGetModel: !!getModel,\n instanceId: instance?.id,\n instanceConstructor: instance?.constructor?.name,\n });\n\n if (!relations || relations.length === 0) {\n logger.info('[VALIDATOR-RELATIONS] No relations to fetch, returning empty');\n return {};\n }\n if (!getModel) {\n logger.warn('[VALIDATOR-RELATIONS] getModel function not provided, cannot fetch related data for validation');\n return {};\n }\n\n const relatedData: Record<string, any> = {};\n\n for (const relation of relations) {\n const { name, model, foreignKey, localKey = 'id', type, attributes } = relation;\n\n logger.info('[VALIDATOR-RELATIONS] Processing relation', {\n relationName: name,\n relationType: type,\n model,\n foreignKey,\n localKey,\n });\n\n // OPTIMIZATION: Check if the relation data is already loaded/set on the instance\n // This handles cases where:\n // 1. The association was eager-loaded (included in query)\n // 2. The association was set programmatically (e.g., reservation.vehicles = [...])\n // 3. Sequelize has pending association updates\n //\n // This is critical for validating updates that modify associations through helper fields\n // Example: { vehicleIds: [] } → converted to vehicles → sets reservationVehicles\n const instanceHasRelation = instance[name] !== undefined;\n logger.info('[VALIDATOR-RELATIONS] Checking if relation exists on instance', {\n relationName: name,\n hasRelation: instanceHasRelation,\n relationValue: instanceHasRelation ? instance[name] : 'NOT_SET',\n isArray: instanceHasRelation ? Array.isArray(instance[name]) : false,\n });\n\n if (instanceHasRelation) {\n const relationValue = instance[name];\n\n // Handle arrays (hasMany)\n if (Array.isArray(relationValue)) {\n relatedData[name] = relationValue;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${name}`, { count: relationValue.length });\n continue;\n }\n\n // Handle single objects (hasOne, belongsTo)\n if (relationValue !== null && typeof relationValue === 'object') {\n relatedData[name] = relationValue;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set relation data for ${name}`);\n continue;\n }\n\n // Handle null (relation explicitly set to null)\n if (relationValue === null) {\n relatedData[name] = null;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Using pre-loaded/set null relation for ${name}`);\n continue;\n }\n }\n\n // SMART DERIVATION: Try to derive the relation from Sequelize associations\n // This handles cases where a parent association is set but not the specific relation\n // Example: instance.vehicles is set, but we need reservationVehicles\n // We can query the association to get it\n const associationMethod = `get${name.charAt(0).toUpperCase() + name.slice(1)}`;\n const hasAssociationMethod = typeof instance[associationMethod] === 'function';\n\n logger.info('[VALIDATOR-RELATIONS] Checking for Sequelize association getter', {\n relationName: name,\n methodName: associationMethod,\n hasMethod: hasAssociationMethod,\n availableMethods: Object.keys(instance).filter(k => k.startsWith('get')),\n });\n\n if (hasAssociationMethod) {\n try {\n logger.info(`[VALIDATOR-RELATIONS] Calling ${associationMethod}...`);\n const associatedData = await instance[associationMethod]({ transaction: options.transaction });\n\n logger.info(`[VALIDATOR-RELATIONS] Association getter result`, {\n methodName: associationMethod,\n hasData: associatedData !== undefined,\n isArray: Array.isArray(associatedData),\n count: Array.isArray(associatedData) ? associatedData.length : 'N/A',\n data: associatedData,\n });\n\n if (associatedData !== undefined) {\n relatedData[name] = associatedData;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Derived relation data using ${associationMethod} for ${name}`, {\n count: Array.isArray(associatedData) ? associatedData.length : 'single',\n });\n continue;\n }\n } catch (error) {\n // If derivation fails, fall through to database query\n logger.warn(`[VALIDATOR-RELATIONS] Failed to derive relation ${name}, will query database`, {\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n }\n\n // Get the model class using the consuming application's getModel function\n logger.info('[VALIDATOR-RELATIONS] Falling back to database query for relation', { relationName: name });\n\n let RelatedModel;\n try {\n RelatedModel = getModel(model);\n logger.info('[VALIDATOR-RELATIONS] Model fetched successfully', { model, hasModel: !!RelatedModel });\n } catch (error) {\n logger.warn(`[VALIDATOR-RELATIONS] Failed to get model ${model} for relation ${name}`, { error });\n continue;\n }\n\n if (!RelatedModel) {\n logger.warn(`[VALIDATOR-RELATIONS] Related model ${model} not found for validation. Ensure the model name is correct.`);\n continue;\n }\n\n // Build query based on relation type\n let where: Record<string, unknown>;\n let keyValue: unknown;\n\n if (type === 'belongsTo') {\n // For belongsTo: foreignKey is on the CURRENT model, not the related model\n // Example: Reservation.fleet_id → Fleet.id\n keyValue = instance[foreignKey];\n if (!keyValue) {\n logger.info(`[VALIDATOR-RELATIONS] Foreign key ${foreignKey} is null/undefined, skipping relation ${name}`);\n continue;\n }\n where = { [localKey]: keyValue };\n } else {\n // For hasMany/hasOne: foreignKey is on the RELATED model\n // Example: Reservation.id → ReservationVehicles.reservation_id\n keyValue = instance[localKey];\n if (!keyValue) {\n logger.info(`[VALIDATOR-RELATIONS] Local key ${localKey} is null/undefined, skipping relation ${name}`);\n continue;\n }\n where = { [foreignKey]: keyValue };\n }\n\n const query = {\n where,\n transaction: options.transaction,\n ...(attributes && { attributes }),\n raw: true,\n };\n\n logger.info('[VALIDATOR-RELATIONS] Executing database query', {\n relationName: name,\n relationType: type,\n query: { where, attributes },\n });\n\n try {\n // Fetch data\n if (type === 'hasMany') {\n const records = await RelatedModel.findAll(query);\n relatedData[name] = records;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${records.length} records from database for ${name}`);\n } else if (type === 'hasOne' || type === 'belongsTo') {\n const record = await RelatedModel.findOne(query);\n relatedData[name] = record || null;\n logger.info(`[VALIDATOR-RELATIONS] ✓ Fetched ${record ? 'one' : 'no'} record from database for ${name}`);\n }\n } catch (error) {\n logger.error(`[VALIDATOR-RELATIONS] Error fetching related data for ${name}`, { error, relation });\n // Continue with validation even if related data fetch fails\n relatedData[name] = type === 'hasMany' ? [] : null;\n }\n }\n\n logger.info('[VALIDATOR-RELATIONS] fetchRelatedData complete', {\n relationsProcessed: Object.keys(relatedData),\n summary: Object.entries(relatedData).map(([key, val]) => ({\n name: key,\n count: Array.isArray(val) ? val.length : (val ? 'single' : 'null'),\n })),\n });\n\n return relatedData;\n};\n\n/**\n * Fetches related data for the 'before' state using original values\n * This handles cases where the FK might have changed\n */\nconst fetchRelatedDataForBeforeState = async (\n instance: any,\n relations: RelationConfig[],\n originalValues: any,\n options: TransactionOptions,\n getModel?: ModelFetcher,\n): Promise<Record<string, any>> => {\n if (!relations || relations.length === 0) return {};\n if (!getModel) return {};\n\n const relatedData: Record<string, any> = {};\n\n for (const relation of relations) {\n const { name, model, foreignKey, localKey = 'id', type, attributes } = relation;\n\n let RelatedModel;\n try {\n RelatedModel = getModel(model);\n } catch (error) {\n logger.warn(`Failed to get model ${model} for before state`, { error });\n continue;\n }\n\n if (!RelatedModel) continue;\n\n // Build query based on relation type using ORIGINAL values\n let where: Record<string, unknown>;\n let keyValue: unknown;\n\n if (type === 'belongsTo') {\n // For belongsTo: foreignKey is on the CURRENT model, use original FK value\n keyValue = originalValues[foreignKey];\n if (!keyValue) continue;\n where = { [localKey]: keyValue };\n } else {\n // For hasMany/hasOne: use original local key value\n keyValue = originalValues[localKey];\n if (!keyValue) continue;\n where = { [foreignKey]: keyValue };\n }\n\n const query = {\n where,\n transaction: options.transaction,\n ...(attributes && { attributes }),\n raw: true,\n };\n\n try {\n if (type === 'hasMany') {\n relatedData[name] = await RelatedModel.findAll(query);\n } else {\n relatedData[name] = await RelatedModel.findOne(query) || null;\n }\n } catch (error) {\n logger.error(`Error fetching before state for ${name}`, { error });\n relatedData[name] = type === 'hasMany' ? [] : null;\n }\n }\n\n return relatedData;\n};\n\n/**\n * Flattens related data into dot-notation for validation\n * Input: { reservationVehicles: [{ id: 1, vehicle_id: 'v1', status: 'confirmed' }, {...}] }\n * Output: {\n * 'reservationVehicles.count': 2,\n * 'reservationVehicles.0.vehicle_id': 'v1',\n * 'reservationVehicles.0.status': 'confirmed',\n * 'reservationVehicles': [...] // Keep original too\n * }\n */\nconst flattenRelatedData = (\n relatedData: Record<string, any>,\n): Record<string, any> => {\n const flattened: Record<string, any> = {};\n\n for (const [relationName, data] of Object.entries(relatedData)) {\n if (Array.isArray(data)) {\n // Add count aggregate\n flattened[`${relationName}.count`] = data.length;\n\n // Add indexed access\n data.forEach((record, index) => {\n for (const [key, value] of Object.entries(record)) {\n flattened[`${relationName}.${index}.${key}`] = value;\n }\n });\n\n // Keep original array\n flattened[relationName] = data;\n } else if (data !== null) {\n // Single record (hasOne/belongsTo)\n for (const [key, value] of Object.entries(data)) {\n flattened[`${relationName}.${key}`] = value;\n }\n flattened[relationName] = data;\n }\n }\n\n return flattened;\n};\n\n/**\n * Validates the model using custom validators\n */\nconst validateModel = async (\n instance: any,\n options: TransactionOptions,\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n isCreate = false,\n): Promise<void> => {\n const modelType = instance.constructor.name;\n\n logger.debug('sadot - validating model', { isCreate, modelType });\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n logger.debug('sadot - identifiers', { identifiers });\n\n // Skip if no identifiers\n if (!identifiers || Object.keys(identifiers).length === 0) {\n logger.debug('sadot - skipping validation: no identifiers');\n return;\n }\n\n // Find the entityId from identifiers (fleetId, businessModelId, etc.)\n const entityId = Object.values(identifiers)[0]; // Get the first value as entityId\n\n logger.debug('sadot - entityId', { entityId });\n\n if (!entityId) {\n logger.debug('sadot - skipping validation: no entityId');\n return;\n }\n\n let validatorsPromise;\n let cacheKey: string | undefined;\n if (options.transaction) {\n options.transaction.validationsCache ??= new Map();\n cacheKey = `${modelType}-${entityId}`;\n validatorsPromise = options.transaction.validationsCache.get(cacheKey);\n }\n\n if (!validatorsPromise) {\n validatorsPromise = ValidatorRepo.findAllByModelType(\n modelType,\n entityId,\n {\n transaction: options.transaction,\n attributes: CUSTOM_VALIDATOR_ATTRIBUTES_TO_PULL,\n modelOptions,\n },\n );\n if (options.transaction) {\n options?.transaction?.validationsCache!.set(cacheKey!, validatorsPromise);\n }\n }\n const validators = await validatorsPromise;\n\n logger.debug('sadot - validators found', { count: validators.length });\n\n if (!validators.length) {\n logger.debug('sadot - skipping validation: no validators found');\n return;\n }\n\n // For updates, get the previous values\n let originalValues = null;\n if (!isCreate) {\n originalValues = buildPreChangeState(instance);\n }\n\n // Get complete custom fields by merging DB values with update values\n // This is especially important for partial updates to ensure all related fields are available\n const completeCustomFields = !isCreate\n ? await getCompleteCustomFields(instance, options)\n : instance.customFields || {};\n\n // Extract getModel from modelOptions for relation fetching\n const { getModel } = modelOptions;\n\n for (const validator of validators) {\n const { schema } = validator;\n const typedSchema = schema as Record<string, any>;\n const relations = typedSchema.relations || [];\n\n logger.info('[VALIDATOR-RELATIONS] Processing validator schema', {\n validatorId: validator.id,\n hasAfterProps: !!typedSchema.properties?.after,\n hasBeforeProps: !!typedSchema.properties?.before,\n hasRelations: relations.length > 0,\n relations: relations.map((r: RelationConfig) => ({ name: r.name, type: r.type, model: r.model })),\n });\n\n if (isCreate) {\n // For create operations, we only need the 'after' state\n if (typedSchema.properties?.after) {\n // Fetch related data for 'after' state\n const relatedData = await fetchRelatedData(instance, relations, options, getModel);\n const flatRelated = flattenRelatedData(relatedData);\n\n const validateSchema = ajv.compile({\n ...schema,\n // Focus only on the 'after' validation part for create\n properties: {\n after: typedSchema.properties.after,\n },\n });\n\n const isValid = validateSchema(JSON.parse(JSON.stringify({\n after: {\n ...instance.dataValues,\n customFields: completeCustomFields,\n ...flatRelated,\n },\n })));\n\n if (!isValid) {\n const errorDetails = validateSchema.errors?.map(err =>\n `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n } else {\n // For update operations, we need both before and after\n\n logger.info('[VALIDATOR-RELATIONS] Fetching related data for BEFORE state');\n // Fetch related data for BOTH before and after states\n const beforeRelatedData = await fetchRelatedDataForBeforeState(\n instance,\n relations,\n originalValues,\n options,\n getModel,\n );\n const flatBeforeRelated = flattenRelatedData(beforeRelatedData);\n logger.info('[VALIDATOR-RELATIONS] Before state related data', {\n raw: beforeRelatedData,\n flattened: flatBeforeRelated,\n });\n\n logger.info('[VALIDATOR-RELATIONS] Fetching related data for AFTER state');\n const afterRelatedData = await fetchRelatedData(instance, relations, options, getModel);\n const flatAfterRelated = flattenRelatedData(afterRelatedData);\n logger.info('[VALIDATOR-RELATIONS] After state related data', {\n raw: afterRelatedData,\n flattened: flatAfterRelated,\n });\n\n const validateSchema = ajv.compile(typedSchema);\n\n // Create after object with our helper function\n const afterObj = manualObjectCopy(instance.dataValues);\n\n // Add complete custom fields\n afterObj.customFields = completeCustomFields;\n\n // Create validation payload with related data\n const payload = {\n before: {\n ...originalValues,\n ...flatBeforeRelated,\n },\n after: {\n ...afterObj,\n ...flatAfterRelated,\n },\n };\n\n logger.info('[VALIDATOR-RELATIONS] Final validation payload', {\n payload: JSON.stringify(payload, null, 2),\n });\n\n // Validate\n const isValid = validateSchema(JSON.parse(JSON.stringify(payload)));\n\n logger.info('[VALIDATOR-RELATIONS] Validation result', {\n isValid,\n errors: isValid ? null : validateSchema.errors,\n });\n\n if (!isValid) {\n const errorDetails = validateSchema\n .errors\n ?.map(err => `${(err as any).instancePath || ''} ${(err as any).message || 'Invalid value'}`).join(', ');\n\n const formattedErrors = formatAjvErrors(validateSchema.errors!);\n throw new BadRequest(\n [new Error(`Validation failed for ${modelType}: ${errorDetails}`)],\n undefined,\n {\n customError: formattedErrors,\n },\n );\n }\n }\n }\n};\n\nconst getFieldDefinitions = async ({\n modelType,\n modelOptions,\n identifiers,\n options,\n}: {\n modelType: any;\n modelOptions: ModelOptions;\n identifiers: any[];\n options: any;\n}) => {\n const { include, useEntityIdFromInclude } = modelOptions;\n const where: WhereOptions = {\n modelType,\n disabled: false,\n ...(!useEntityIdFromInclude && { entityId: identifiers }),\n };\n\n const fieldDefinitions = await DefinitionRepo.findAll(where, {\n withDisabled: false,\n transaction: options.transaction,\n include: include?.(identifiers),\n });\n return fieldDefinitions;\n};\n\nconst formatDates = (fieldDefinitions: CustomFieldDefinition[], instance: any) => {\n (fieldDefinitions || []).forEach((fieldDefinition) => {\n const { fieldType, name } = fieldDefinition;\n if ([CustomFieldDefinitionType.DATE, CustomFieldDefinitionType.DATETIME].includes(fieldType)) {\n const value = instance.customFields?.[name];\n if (value) {\n const { value: joiValue, error: validationError } = Joi.date().validate(value);\n if (validationError) {\n throw new InvalidValueError(value, name, validationError);\n }\n instance.customFields[name] = joiValue.toISOString();\n }\n }\n });\n};\n\n/**\n * Hook to handle validation and custom fields during creation\n */\nexport const beforeCreate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: CreateOptions,\n): Promise<void> => {\n logger.debug('sadot - before create hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n // Step 1: Handle custom fields default values and required fields\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Apply default values\n const fieldsWithDefaultValue = fieldDefinitions.filter(def => ![null, undefined].includes(def.defaultValue));\n if (fieldsWithDefaultValue.length) {\n instance.customFields ||= {};\n fieldsWithDefaultValue\n .filter(def => (instance.customFields?.[def.name] === undefined))\n .forEach(({ name, defaultValue }) => {\n instance.customFields[name] = defaultValue;\n });\n }\n\n // Check for required fields\n let requiredFieldsNames: string[];\n\n if (sadotOptions.getRequiredCustomFields) {\n // Use consumer-provided logic to determine required fields\n requiredFieldsNames = await Promise.resolve(\n sadotOptions.getRequiredCustomFields(instance, fieldDefinitions, options),\n );\n } else {\n // Default behavior: use required flag from fieldDefinitions\n requiredFieldsNames = Array.from(\n new Set(fieldDefinitions.filter(({ required }) => required).map(({ name }) => name)),\n );\n }\n\n const { customFields } = instance;\n const fieldsNames = Object.keys(customFields ?? {});\n const missingFields = requiredFieldsNames.filter(name => !fieldsNames.includes(name));\n if (missingFields?.length) {\n throw new MissingRequiredCustomFieldError(missingFields);\n }\n\n // Step 2: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, true);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 3: Save custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx === -1 || !customFields || !Object.keys(customFields).length) {\n // No custom fields to update\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n};\n\n/**\n * Hook to handle validation and custom fields during update\n */\nexport const beforeUpdate = (\n scopeAttributes: string[],\n modelOptions: ModelOptions = {},\n sadotOptions: Pick<CustomFieldOptions, 'useCustomFieldsEntries' | 'getRequiredCustomFields'> = {\n useCustomFieldsEntries: false,\n },\n) => async (\n instance: any,\n options: UpdateOptions,\n): Promise<void> => {\n logger.debug('sadot - before update hook');\n const { fields } = options;\n const modelType = instance.constructor.name;\n const identifiers = applyScopeToInstance(instance, scopeAttributes);\n\n const fieldDefinitions = await getFieldDefinitions({\n modelType, modelOptions, identifiers, options,\n });\n\n // Step 1: Validate the model data (including custom fields)\n await validateModel(instance, options as TransactionOptions, scopeAttributes, modelOptions, false);\n\n // format date and datetime fields\n formatDates(fieldDefinitions, instance);\n\n // Step 2: Update custom field values if they exist\n const customFieldsIdx = fields!.indexOf('customFields');\n if (customFieldsIdx > -1) {\n const { customFields } = instance;\n\n if (!Object.keys(customFields).length) {\n return;\n }\n\n // Save custom field values\n await updateInstanceValues({\n modelId: instance.id,\n modelType,\n identifiers,\n customFields,\n options: {\n useCustomFieldsEntries: sadotOptions.useCustomFieldsEntries,\n transaction: options.transaction,\n modelOptions,\n },\n });\n\n // Remove customFields from fields array after handling\n fields!.splice(customFieldsIdx, 1);\n }\n};\n\n/**\n * Hook to enable individual hooks for bulk create operations\n */\nexport const beforeBulkCreate = (options: BulkCreateOptions): void => {\n // This will activate the beforeCreate hook on each instance\n options.individualHooks = true;\n};\n\n/**\n * Hook to enable individual hooks for bulk update operations\n */\nexport const beforeBulkUpdate = (options: UpdateOptions): void => {\n // This will activate the beforeUpdate hook on each instance\n options.individualHooks = true;\n};\n"],"mappings":"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"}
@@ -23,6 +23,11 @@ interface ModelOptions {
23
23
  * Which attributes to include in the model
24
24
  */
25
25
  attributes?: string[];
26
+ /**
27
+ * Model fetcher function for accessing application models
28
+ * Used for fetching related data in validation
29
+ */
30
+ getModel?: ModelFetcher;
26
31
  }
27
32
  interface Models {
28
33
  name: string;
@@ -24,6 +24,11 @@ interface ModelOptions {
24
24
  * Which attributes to include in the model
25
25
  */
26
26
  attributes?: string[];
27
+ /**
28
+ * Model fetcher function for accessing application models
29
+ * Used for fetching related data in validation
30
+ */
31
+ getModel?: ModelFetcher;
27
32
  }
28
33
  interface Models {
29
34
  name: string;
@@ -1,2 +1,2 @@
1
- const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`./logger/index.cjs`),n=require(`../models/CustomFieldValue.cjs`),r=require(`../models/CustomFieldDefinition.cjs`),i=require(`../models/CustomValidator.cjs`);require(`../models/index.cjs`);const a=require(`../hooks/enrich.cjs`),o=require(`../hooks/find.cjs`),s=require(`../hooks/hooks.cjs`);require(`../hooks/index.cjs`);const c=require(`../scopes/filter.cjs`);require(`../scopes/index.cjs`);let l=require(`sequelize`),u=require(`@autofleet/common-types`),d=require(`@autofleet/common-types/lib/custom-fields`);const{CUSTOM_FIELDS_FILTER_SCOPE:f}=u.customFields,p=(e,n,r={useCustomFieldsEntries:!1})=>{e.forEach(async({name:e,scopeAttributes:i,modelOptions:c,hasTypeId:u,getRequiredCustomFields:d})=>{try{let f=n(e);if(!f){t.default.warn(`sadot - tried to addHooks to a model that does not exist yet`,{name:e,scopeAttributes:i});return}f.rawAttributes.customFields={type:l.DataTypes.VIRTUAL},f.refreshAttributes();let p={...r,hasTypeId:u??!1,getRequiredCustomFields:d};f.addHook(`beforeFind`,`sadot-beforeFind`,o.beforeFind(i)),f.addHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`,s.beforeBulkCreate),f.addHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`,s.beforeBulkUpdate),f.addHook(`beforeCreate`,`sadot-beforeCreate`,s.beforeCreate(i,c,p)),f.addHook(`beforeUpdate`,`sadot-beforeUpdate`,s.beforeUpdate(i,c,p)),f.addHook(`afterFind`,`sadot-afterFind`,a.default(e,i,`afterFind`,c,p)),f.addHook(`afterUpdate`,`sadot-afterUpdate`,a.default(e,i,void 0,c,p)),f.addHook(`afterCreate`,`sadot-afterCreate`,a.default(e,i,void 0,c,p))}catch(n){t.default.error(`Could not add custom fields hook to model ${e}. `,n)}})},m=(e,n)=>{e.forEach(async({name:e})=>{try{let t=n(e);if(!t)return;t.rawAttributes.customFields&&(delete t.rawAttributes.customFields,t.refreshAttributes()),t.removeHook(`beforeFind`,`sadot-beforeFind`),t.removeHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`),t.removeHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`),t.removeHook(`beforeCreate`,`sadot-beforeCreate`),t.removeHook(`beforeUpdate`,`sadot-beforeUpdate`),t.removeHook(`afterFind`,`sadot-afterFind`),t.removeHook(`afterUpdate`,`sadot-afterUpdate`)}catch(n){t.default.error(`Could not add custom fields hook to model ${e}. `,n)}})},h=(e,t,r)=>{r?.useCustomFieldsEntries||(e.hasMany(n.default,{foreignKey:`modelId`,as:`customFieldValue`}),n.default.belongsTo(e,{foreignKey:`modelId`,as:t}))},g=(e,n,r={useCustomFieldsEntries:!1})=>{e.forEach(async({name:e,scopeAttributes:i})=>{try{let a=n(e);if(!a){t.default.warn(`sadot - tried to addScopes to a model that does not exist yet`,{name:e,scopeAttributes:i});return}h(a,e,r),a.addScope(f,c.customFieldsFilterScope(e,r)),a.addScope(d.CUSTOM_FIELDS_SORT_SCOPE,c.customFieldsSortScope(e,r))}catch(n){t.default.error(`Could not add custom fields scopes to model ${e}. `,n)}})},_=e=>{e.forEach(({modelOptions:e})=>{e?.customAssociation?.(r.default),e?.customAssociation?.(i.default)})};exports.addHooks=p,exports.addScopes=g,exports.applyCustomAssociation=_,exports.removeHooks=m;
1
+ const e=require(`../_virtual/rolldown_runtime.cjs`),t=require(`./logger/index.cjs`),n=require(`../models/CustomFieldValue.cjs`),r=require(`../models/CustomFieldDefinition.cjs`),i=require(`../models/CustomValidator.cjs`);require(`../models/index.cjs`);const a=require(`../hooks/enrich.cjs`),o=require(`../hooks/find.cjs`),s=require(`../hooks/hooks.cjs`);require(`../hooks/index.cjs`);const c=require(`../scopes/filter.cjs`);require(`../scopes/index.cjs`);let l=require(`sequelize`),u=require(`@autofleet/common-types`),d=require(`@autofleet/common-types/lib/custom-fields`);const{CUSTOM_FIELDS_FILTER_SCOPE:f}=u.customFields,p=(e,n,r={useCustomFieldsEntries:!1})=>{e.forEach(async({name:e,scopeAttributes:i,modelOptions:c,hasTypeId:u,getRequiredCustomFields:d})=>{try{let f=n(e);if(!f){t.default.warn(`sadot - tried to addHooks to a model that does not exist yet`,{name:e,scopeAttributes:i});return}f.rawAttributes.customFields={type:l.DataTypes.VIRTUAL},f.refreshAttributes();let p={...r,hasTypeId:u??!1,getRequiredCustomFields:d},m={...c,getModel:n};f.addHook(`beforeFind`,`sadot-beforeFind`,o.beforeFind(i)),f.addHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`,s.beforeBulkCreate),f.addHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`,s.beforeBulkUpdate),f.addHook(`beforeCreate`,`sadot-beforeCreate`,s.beforeCreate(i,m,p)),f.addHook(`beforeUpdate`,`sadot-beforeUpdate`,s.beforeUpdate(i,m,p)),f.addHook(`afterFind`,`sadot-afterFind`,a.default(e,i,`afterFind`,m,p)),f.addHook(`afterUpdate`,`sadot-afterUpdate`,a.default(e,i,void 0,m,p)),f.addHook(`afterCreate`,`sadot-afterCreate`,a.default(e,i,void 0,m,p))}catch(n){t.default.error(`Could not add custom fields hook to model ${e}. `,n)}})},m=(e,n)=>{e.forEach(async({name:e})=>{try{let t=n(e);if(!t)return;t.rawAttributes.customFields&&(delete t.rawAttributes.customFields,t.refreshAttributes()),t.removeHook(`beforeFind`,`sadot-beforeFind`),t.removeHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`),t.removeHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`),t.removeHook(`beforeCreate`,`sadot-beforeCreate`),t.removeHook(`beforeUpdate`,`sadot-beforeUpdate`),t.removeHook(`afterFind`,`sadot-afterFind`),t.removeHook(`afterUpdate`,`sadot-afterUpdate`)}catch(n){t.default.error(`Could not add custom fields hook to model ${e}. `,n)}})},h=(e,t,r)=>{r?.useCustomFieldsEntries||(e.hasMany(n.default,{foreignKey:`modelId`,as:`customFieldValue`}),n.default.belongsTo(e,{foreignKey:`modelId`,as:t}))},g=(e,n,r={useCustomFieldsEntries:!1})=>{e.forEach(async({name:e,scopeAttributes:i})=>{try{let a=n(e);if(!a){t.default.warn(`sadot - tried to addScopes to a model that does not exist yet`,{name:e,scopeAttributes:i});return}h(a,e,r),a.addScope(f,c.customFieldsFilterScope(e,r)),a.addScope(d.CUSTOM_FIELDS_SORT_SCOPE,c.customFieldsSortScope(e,r))}catch(n){t.default.error(`Could not add custom fields scopes to model ${e}. `,n)}})},_=e=>{e.forEach(({modelOptions:e})=>{e?.customAssociation?.(r.default),e?.customAssociation?.(i.default)})};exports.addHooks=p,exports.addScopes=g,exports.applyCustomAssociation=_,exports.removeHooks=m;
2
2
  //# sourceMappingURL=init.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.cjs","names":["customFields","DataTypes","beforeFind","beforeBulkCreate","beforeBulkUpdate","beforeCreate","beforeUpdate","enrichResults","CustomFieldValue","customFieldsFilterScope","CUSTOM_FIELDS_SORT_SCOPE","customFieldsSortScope","CustomFieldDefinition","CustomValidator"],"sources":["../../src/utils/init.ts"],"sourcesContent":["import { DataTypes } from 'sequelize';\nimport type { ModelCtor } from 'sequelize-typescript';\nimport { customFields } from '@autofleet/common-types';\nimport { CUSTOM_FIELDS_SORT_SCOPE } from '@autofleet/common-types/lib/custom-fields';\nimport {\n CustomFieldDefinition,\n CustomFieldValue,\n CustomValidator,\n} from '../models';\nimport {\n beforeFind,\n enrichResults,\n beforeBulkUpdate,\n beforeUpdate,\n beforeCreate,\n beforeBulkCreate,\n} from '../hooks';\nimport { customFieldsFilterScope } from '../scopes';\nimport logger from './logger';\nimport type { CustomFieldOptions, ModelFetcher, Models } from '../types';\nimport { customFieldsSortScope } from '../scopes/filter';\n\nconst { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;\n\nexport const addHooks = (\n models: Models[],\n getModel: ModelFetcher,\n sadotOptions: { useCustomFieldsEntries?: boolean; } = { useCustomFieldsEntries: false },\n): void => {\n models.forEach(async ({\n name, scopeAttributes, modelOptions, hasTypeId, getRequiredCustomFields,\n }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addHooks to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n model.rawAttributes.customFields = {\n type: DataTypes.VIRTUAL,\n };\n model.refreshAttributes();\n\n const enrichOptions = {\n ...sadotOptions,\n hasTypeId: hasTypeId ?? false,\n getRequiredCustomFields,\n };\n\n // TODO: Uncomment after tests are passed\n // model.addHook('afterFind', workaround);\n model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));\n model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);\n model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);\n model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, modelOptions, enrichOptions));\n model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, modelOptions, enrichOptions));\n model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', modelOptions, enrichOptions));\n model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, undefined, modelOptions, enrichOptions));\n model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, undefined, modelOptions, enrichOptions));\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\nexport const removeHooks = (models: Models[], getModel: ModelFetcher): void => {\n models.forEach(async ({ name }) => {\n try {\n const model = getModel(name);\n if (!model) return;\n if (model.rawAttributes.customFields) {\n delete model.rawAttributes.customFields;\n model.refreshAttributes();\n }\n // model.removeHook('afterFind', 'sadot-workaround');\n model.removeHook('beforeFind', 'sadot-beforeFind');\n model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');\n model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');\n model.removeHook('beforeCreate', 'sadot-beforeCreate');\n model.removeHook('beforeUpdate', 'sadot-beforeUpdate');\n model.removeHook('afterFind', 'sadot-afterFind');\n model.removeHook('afterUpdate', 'sadot-afterUpdate');\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\n/**\n * Necessary associations for the {@link customFieldsFilterScope} scope\n */\nconst addAssociations = (model: ModelCtor, modelName: string, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): void => {\n if (options?.useCustomFieldsEntries) return;\n model.hasMany(CustomFieldValue, { foreignKey: 'modelId', as: 'customFieldValue' });\n // TBC: maybe can be removed\n CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });\n};\n\nexport const addScopes = (models: Models[], getModel: ModelFetcher, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false }): void => {\n models.forEach(async ({ name, scopeAttributes }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addScopes to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n // Necessary associations for the filtering scope\n addAssociations(model, name, options);\n // Add filter scope\n model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, customFieldsFilterScope(name, options));\n model.addScope(CUSTOM_FIELDS_SORT_SCOPE, customFieldsSortScope(name, options));\n } catch (e) {\n logger.error(`Could not add custom fields scopes to model ${name}. `, e);\n }\n });\n};\n\nexport const applyCustomAssociation = (models: Models[]): void => {\n models.forEach(({ modelOptions }) => {\n modelOptions?.customAssociation?.(CustomFieldDefinition);\n modelOptions?.customAssociation?.(CustomValidator);\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"init.cjs","names":["customFields","DataTypes","beforeFind","beforeBulkCreate","beforeBulkUpdate","beforeCreate","beforeUpdate","enrichResults","CustomFieldValue","customFieldsFilterScope","CUSTOM_FIELDS_SORT_SCOPE","customFieldsSortScope","CustomFieldDefinition","CustomValidator"],"sources":["../../src/utils/init.ts"],"sourcesContent":["import { DataTypes } from 'sequelize';\nimport type { ModelCtor } from 'sequelize-typescript';\nimport { customFields } from '@autofleet/common-types';\nimport { CUSTOM_FIELDS_SORT_SCOPE } from '@autofleet/common-types/lib/custom-fields';\nimport {\n CustomFieldDefinition,\n CustomFieldValue,\n CustomValidator,\n} from '../models';\nimport {\n beforeFind,\n enrichResults,\n beforeBulkUpdate,\n beforeUpdate,\n beforeCreate,\n beforeBulkCreate,\n} from '../hooks';\nimport { customFieldsFilterScope } from '../scopes';\nimport logger from './logger';\nimport type { CustomFieldOptions, ModelFetcher, Models } from '../types';\nimport { customFieldsSortScope } from '../scopes/filter';\n\nconst { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;\n\nexport const addHooks = (\n models: Models[],\n getModel: ModelFetcher,\n sadotOptions: { useCustomFieldsEntries?: boolean; } = { useCustomFieldsEntries: false },\n): void => {\n models.forEach(async ({\n name, scopeAttributes, modelOptions, hasTypeId, getRequiredCustomFields,\n }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addHooks to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n model.rawAttributes.customFields = {\n type: DataTypes.VIRTUAL,\n };\n model.refreshAttributes();\n\n const enrichOptions = {\n ...sadotOptions,\n hasTypeId: hasTypeId ?? false,\n getRequiredCustomFields,\n };\n\n // Enrich modelOptions with getModel for validator relations support\n const enrichedModelOptions = {\n ...modelOptions,\n getModel,\n };\n\n // TODO: Uncomment after tests are passed\n // model.addHook('afterFind', workaround);\n model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));\n model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);\n model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);\n model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, enrichedModelOptions, enrichOptions));\n model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, enrichedModelOptions, enrichOptions));\n model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', enrichedModelOptions, enrichOptions));\n model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, undefined, enrichedModelOptions, enrichOptions));\n model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, undefined, enrichedModelOptions, enrichOptions));\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\nexport const removeHooks = (models: Models[], getModel: ModelFetcher): void => {\n models.forEach(async ({ name }) => {\n try {\n const model = getModel(name);\n if (!model) return;\n if (model.rawAttributes.customFields) {\n delete model.rawAttributes.customFields;\n model.refreshAttributes();\n }\n // model.removeHook('afterFind', 'sadot-workaround');\n model.removeHook('beforeFind', 'sadot-beforeFind');\n model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');\n model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');\n model.removeHook('beforeCreate', 'sadot-beforeCreate');\n model.removeHook('beforeUpdate', 'sadot-beforeUpdate');\n model.removeHook('afterFind', 'sadot-afterFind');\n model.removeHook('afterUpdate', 'sadot-afterUpdate');\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\n/**\n * Necessary associations for the {@link customFieldsFilterScope} scope\n */\nconst addAssociations = (model: ModelCtor, modelName: string, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): void => {\n if (options?.useCustomFieldsEntries) return;\n model.hasMany(CustomFieldValue, { foreignKey: 'modelId', as: 'customFieldValue' });\n // TBC: maybe can be removed\n CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });\n};\n\nexport const addScopes = (models: Models[], getModel: ModelFetcher, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false }): void => {\n models.forEach(async ({ name, scopeAttributes }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addScopes to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n // Necessary associations for the filtering scope\n addAssociations(model, name, options);\n // Add filter scope\n model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, customFieldsFilterScope(name, options));\n model.addScope(CUSTOM_FIELDS_SORT_SCOPE, customFieldsSortScope(name, options));\n } catch (e) {\n logger.error(`Could not add custom fields scopes to model ${name}. `, e);\n }\n });\n};\n\nexport const applyCustomAssociation = (models: Models[]): void => {\n models.forEach(({ modelOptions }) => {\n modelOptions?.customAssociation?.(CustomFieldDefinition);\n modelOptions?.customAssociation?.(CustomValidator);\n });\n};\n"],"mappings":"6jBAsBA,KAAM,CAAE,8BAA+BA,EAAAA,aAE1B,GACX,EACA,EACA,EAAsD,CAAE,uBAAwB,GAAO,GAC9E,CACT,EAAO,QAAQ,MAAO,CACpB,OAAM,kBAAiB,eAAc,YAAW,6BAC5C,CACJ,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAA,QAAO,KAAK,+DAAgE,CAC1E,OACA,kBACD,CAAC,CACF,OAEF,EAAM,cAAc,aAAe,CACjC,KAAMC,EAAAA,UAAU,QACjB,CACD,EAAM,mBAAmB,CAEzB,IAAM,EAAgB,CACpB,GAAG,EACH,UAAW,GAAa,GACxB,0BACD,CAGK,EAAuB,CAC3B,GAAG,EACH,WACD,CAID,EAAM,QAAQ,aAAc,mBAAoBC,EAAAA,WAAW,EAAgB,CAAC,CAC5E,EAAM,QAAQ,mBAAoB,yBAA0BC,EAAAA,iBAAiB,CAC7E,EAAM,QAAQ,mBAAoB,yBAA0BC,EAAAA,iBAAiB,CAC7E,EAAM,QAAQ,eAAgB,qBAAsBC,EAAAA,aAAa,EAAiB,EAAsB,EAAc,CAAC,CACvH,EAAM,QAAQ,eAAgB,qBAAsBC,EAAAA,aAAa,EAAiB,EAAsB,EAAc,CAAC,CACvH,EAAM,QAAQ,YAAa,kBAAmBC,EAAAA,QAAc,EAAM,EAAiB,YAAa,EAAsB,EAAc,CAAC,CACrI,EAAM,QAAQ,cAAe,oBAAqBA,EAAAA,QAAc,EAAM,EAAiB,IAAA,GAAW,EAAsB,EAAc,CAAC,CACvI,EAAM,QAAQ,cAAe,oBAAqBA,EAAAA,QAAc,EAAM,EAAiB,IAAA,GAAW,EAAsB,EAAc,CAAC,OAChI,EAAG,CACV,EAAA,QAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAGS,GAAe,EAAkB,IAAiC,CAC7E,EAAO,QAAQ,MAAO,CAAE,UAAW,CACjC,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,OACR,EAAM,cAAc,eACtB,OAAO,EAAM,cAAc,aAC3B,EAAM,mBAAmB,EAG3B,EAAM,WAAW,aAAc,mBAAmB,CAClD,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,YAAa,kBAAkB,CAChD,EAAM,WAAW,cAAe,oBAAoB,OAC7C,EAAG,CACV,EAAA,QAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAME,GAAmB,EAAkB,EAAmB,IAAsE,CAC9H,GAAS,yBACb,EAAM,QAAQC,EAAAA,QAAkB,CAAE,WAAY,UAAW,GAAI,mBAAoB,CAAC,CAElF,EAAA,QAAiB,UAAU,EAAO,CAAE,WAAY,UAAW,GAAI,EAAW,CAAC,GAGhE,GAAa,EAAkB,EAAwB,EAA8D,CAAE,uBAAwB,GAAO,GAAW,CAC5K,EAAO,QAAQ,MAAO,CAAE,OAAM,qBAAsB,CAClD,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAA,QAAO,KAAK,gEAAiE,CAC3E,OACA,kBACD,CAAC,CACF,OAGF,EAAgB,EAAO,EAAM,EAAQ,CAErC,EAAM,SAAS,EAA4BC,EAAAA,wBAAwB,EAAM,EAAQ,CAAC,CAClF,EAAM,SAASC,EAAAA,yBAA0BC,EAAAA,sBAAsB,EAAM,EAAQ,CAAC,OACvE,EAAG,CACV,EAAA,QAAO,MAAM,+CAA+C,EAAK,IAAK,EAAE,GAE1E,EAGS,EAA0B,GAA2B,CAChE,EAAO,SAAS,CAAE,kBAAmB,CACnC,GAAc,oBAAoBC,EAAAA,QAAsB,CACxD,GAAc,oBAAoBC,EAAAA,QAAgB,EAClD"}
@@ -1,2 +1,2 @@
1
- import e from"./logger/index.js";import t from"../models/CustomFieldValue.js";import n from"../models/CustomFieldDefinition.js";import r from"../models/CustomValidator.js";import"../models/index.js";import i from"../hooks/enrich.js";import{beforeFind as a}from"../hooks/find.js";import{beforeBulkCreate as o,beforeBulkUpdate as s,beforeCreate as c,beforeUpdate as l}from"../hooks/hooks.js";import"../hooks/index.js";import{customFieldsFilterScope as u,customFieldsSortScope as d}from"../scopes/filter.js";import"../scopes/index.js";import{DataTypes as f}from"sequelize";import{customFields as p}from"@autofleet/common-types";import{CUSTOM_FIELDS_SORT_SCOPE as m}from"@autofleet/common-types/lib/custom-fields";const{CUSTOM_FIELDS_FILTER_SCOPE:h}=p,g=(t,n,r={useCustomFieldsEntries:!1})=>{t.forEach(async({name:t,scopeAttributes:u,modelOptions:d,hasTypeId:p,getRequiredCustomFields:m})=>{try{let h=n(t);if(!h){e.warn(`sadot - tried to addHooks to a model that does not exist yet`,{name:t,scopeAttributes:u});return}h.rawAttributes.customFields={type:f.VIRTUAL},h.refreshAttributes();let g={...r,hasTypeId:p??!1,getRequiredCustomFields:m};h.addHook(`beforeFind`,`sadot-beforeFind`,a(u)),h.addHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`,o),h.addHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`,s),h.addHook(`beforeCreate`,`sadot-beforeCreate`,c(u,d,g)),h.addHook(`beforeUpdate`,`sadot-beforeUpdate`,l(u,d,g)),h.addHook(`afterFind`,`sadot-afterFind`,i(t,u,`afterFind`,d,g)),h.addHook(`afterUpdate`,`sadot-afterUpdate`,i(t,u,void 0,d,g)),h.addHook(`afterCreate`,`sadot-afterCreate`,i(t,u,void 0,d,g))}catch(n){e.error(`Could not add custom fields hook to model ${t}. `,n)}})},_=(t,n)=>{t.forEach(async({name:t})=>{try{let e=n(t);if(!e)return;e.rawAttributes.customFields&&(delete e.rawAttributes.customFields,e.refreshAttributes()),e.removeHook(`beforeFind`,`sadot-beforeFind`),e.removeHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`),e.removeHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`),e.removeHook(`beforeCreate`,`sadot-beforeCreate`),e.removeHook(`beforeUpdate`,`sadot-beforeUpdate`),e.removeHook(`afterFind`,`sadot-afterFind`),e.removeHook(`afterUpdate`,`sadot-afterUpdate`)}catch(n){e.error(`Could not add custom fields hook to model ${t}. `,n)}})},v=(e,n,r)=>{r?.useCustomFieldsEntries||(e.hasMany(t,{foreignKey:`modelId`,as:`customFieldValue`}),t.belongsTo(e,{foreignKey:`modelId`,as:n}))},y=(t,n,r={useCustomFieldsEntries:!1})=>{t.forEach(async({name:t,scopeAttributes:i})=>{try{let a=n(t);if(!a){e.warn(`sadot - tried to addScopes to a model that does not exist yet`,{name:t,scopeAttributes:i});return}v(a,t,r),a.addScope(h,u(t,r)),a.addScope(m,d(t,r))}catch(n){e.error(`Could not add custom fields scopes to model ${t}. `,n)}})},b=e=>{e.forEach(({modelOptions:e})=>{e?.customAssociation?.(n),e?.customAssociation?.(r)})};export{g as addHooks,y as addScopes,b as applyCustomAssociation,_ as removeHooks};
1
+ import e from"./logger/index.js";import t from"../models/CustomFieldValue.js";import n from"../models/CustomFieldDefinition.js";import r from"../models/CustomValidator.js";import"../models/index.js";import i from"../hooks/enrich.js";import{beforeFind as a}from"../hooks/find.js";import{beforeBulkCreate as o,beforeBulkUpdate as s,beforeCreate as c,beforeUpdate as l}from"../hooks/hooks.js";import"../hooks/index.js";import{customFieldsFilterScope as u,customFieldsSortScope as d}from"../scopes/filter.js";import"../scopes/index.js";import{DataTypes as f}from"sequelize";import{customFields as p}from"@autofleet/common-types";import{CUSTOM_FIELDS_SORT_SCOPE as m}from"@autofleet/common-types/lib/custom-fields";const{CUSTOM_FIELDS_FILTER_SCOPE:h}=p,g=(t,n,r={useCustomFieldsEntries:!1})=>{t.forEach(async({name:t,scopeAttributes:u,modelOptions:d,hasTypeId:p,getRequiredCustomFields:m})=>{try{let h=n(t);if(!h){e.warn(`sadot - tried to addHooks to a model that does not exist yet`,{name:t,scopeAttributes:u});return}h.rawAttributes.customFields={type:f.VIRTUAL},h.refreshAttributes();let g={...r,hasTypeId:p??!1,getRequiredCustomFields:m},_={...d,getModel:n};h.addHook(`beforeFind`,`sadot-beforeFind`,a(u)),h.addHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`,o),h.addHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`,s),h.addHook(`beforeCreate`,`sadot-beforeCreate`,c(u,_,g)),h.addHook(`beforeUpdate`,`sadot-beforeUpdate`,l(u,_,g)),h.addHook(`afterFind`,`sadot-afterFind`,i(t,u,`afterFind`,_,g)),h.addHook(`afterUpdate`,`sadot-afterUpdate`,i(t,u,void 0,_,g)),h.addHook(`afterCreate`,`sadot-afterCreate`,i(t,u,void 0,_,g))}catch(n){e.error(`Could not add custom fields hook to model ${t}. `,n)}})},_=(t,n)=>{t.forEach(async({name:t})=>{try{let e=n(t);if(!e)return;e.rawAttributes.customFields&&(delete e.rawAttributes.customFields,e.refreshAttributes()),e.removeHook(`beforeFind`,`sadot-beforeFind`),e.removeHook(`beforeBulkCreate`,`sadot-beforeBulkCreate`),e.removeHook(`beforeBulkUpdate`,`sadot-beforeBulkUpdate`),e.removeHook(`beforeCreate`,`sadot-beforeCreate`),e.removeHook(`beforeUpdate`,`sadot-beforeUpdate`),e.removeHook(`afterFind`,`sadot-afterFind`),e.removeHook(`afterUpdate`,`sadot-afterUpdate`)}catch(n){e.error(`Could not add custom fields hook to model ${t}. `,n)}})},v=(e,n,r)=>{r?.useCustomFieldsEntries||(e.hasMany(t,{foreignKey:`modelId`,as:`customFieldValue`}),t.belongsTo(e,{foreignKey:`modelId`,as:n}))},y=(t,n,r={useCustomFieldsEntries:!1})=>{t.forEach(async({name:t,scopeAttributes:i})=>{try{let a=n(t);if(!a){e.warn(`sadot - tried to addScopes to a model that does not exist yet`,{name:t,scopeAttributes:i});return}v(a,t,r),a.addScope(h,u(t,r)),a.addScope(m,d(t,r))}catch(n){e.error(`Could not add custom fields scopes to model ${t}. `,n)}})},b=e=>{e.forEach(({modelOptions:e})=>{e?.customAssociation?.(n),e?.customAssociation?.(r)})};export{g as addHooks,y as addScopes,b as applyCustomAssociation,_ as removeHooks};
2
2
  //# sourceMappingURL=init.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":["enrichResults","CustomFieldValue","CustomFieldDefinition","CustomValidator"],"sources":["../../src/utils/init.ts"],"sourcesContent":["import { DataTypes } from 'sequelize';\nimport type { ModelCtor } from 'sequelize-typescript';\nimport { customFields } from '@autofleet/common-types';\nimport { CUSTOM_FIELDS_SORT_SCOPE } from '@autofleet/common-types/lib/custom-fields';\nimport {\n CustomFieldDefinition,\n CustomFieldValue,\n CustomValidator,\n} from '../models';\nimport {\n beforeFind,\n enrichResults,\n beforeBulkUpdate,\n beforeUpdate,\n beforeCreate,\n beforeBulkCreate,\n} from '../hooks';\nimport { customFieldsFilterScope } from '../scopes';\nimport logger from './logger';\nimport type { CustomFieldOptions, ModelFetcher, Models } from '../types';\nimport { customFieldsSortScope } from '../scopes/filter';\n\nconst { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;\n\nexport const addHooks = (\n models: Models[],\n getModel: ModelFetcher,\n sadotOptions: { useCustomFieldsEntries?: boolean; } = { useCustomFieldsEntries: false },\n): void => {\n models.forEach(async ({\n name, scopeAttributes, modelOptions, hasTypeId, getRequiredCustomFields,\n }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addHooks to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n model.rawAttributes.customFields = {\n type: DataTypes.VIRTUAL,\n };\n model.refreshAttributes();\n\n const enrichOptions = {\n ...sadotOptions,\n hasTypeId: hasTypeId ?? false,\n getRequiredCustomFields,\n };\n\n // TODO: Uncomment after tests are passed\n // model.addHook('afterFind', workaround);\n model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));\n model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);\n model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);\n model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, modelOptions, enrichOptions));\n model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, modelOptions, enrichOptions));\n model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', modelOptions, enrichOptions));\n model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, undefined, modelOptions, enrichOptions));\n model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, undefined, modelOptions, enrichOptions));\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\nexport const removeHooks = (models: Models[], getModel: ModelFetcher): void => {\n models.forEach(async ({ name }) => {\n try {\n const model = getModel(name);\n if (!model) return;\n if (model.rawAttributes.customFields) {\n delete model.rawAttributes.customFields;\n model.refreshAttributes();\n }\n // model.removeHook('afterFind', 'sadot-workaround');\n model.removeHook('beforeFind', 'sadot-beforeFind');\n model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');\n model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');\n model.removeHook('beforeCreate', 'sadot-beforeCreate');\n model.removeHook('beforeUpdate', 'sadot-beforeUpdate');\n model.removeHook('afterFind', 'sadot-afterFind');\n model.removeHook('afterUpdate', 'sadot-afterUpdate');\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\n/**\n * Necessary associations for the {@link customFieldsFilterScope} scope\n */\nconst addAssociations = (model: ModelCtor, modelName: string, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): void => {\n if (options?.useCustomFieldsEntries) return;\n model.hasMany(CustomFieldValue, { foreignKey: 'modelId', as: 'customFieldValue' });\n // TBC: maybe can be removed\n CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });\n};\n\nexport const addScopes = (models: Models[], getModel: ModelFetcher, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false }): void => {\n models.forEach(async ({ name, scopeAttributes }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addScopes to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n // Necessary associations for the filtering scope\n addAssociations(model, name, options);\n // Add filter scope\n model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, customFieldsFilterScope(name, options));\n model.addScope(CUSTOM_FIELDS_SORT_SCOPE, customFieldsSortScope(name, options));\n } catch (e) {\n logger.error(`Could not add custom fields scopes to model ${name}. `, e);\n }\n });\n};\n\nexport const applyCustomAssociation = (models: Models[]): void => {\n models.forEach(({ modelOptions }) => {\n modelOptions?.customAssociation?.(CustomFieldDefinition);\n modelOptions?.customAssociation?.(CustomValidator);\n });\n};\n"],"mappings":"ssBAsBA,KAAM,CAAE,8BAA+B,EAE1B,GACX,EACA,EACA,EAAsD,CAAE,uBAAwB,GAAO,GAC9E,CACT,EAAO,QAAQ,MAAO,CACpB,OAAM,kBAAiB,eAAc,YAAW,6BAC5C,CACJ,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAO,KAAK,+DAAgE,CAC1E,OACA,kBACD,CAAC,CACF,OAEF,EAAM,cAAc,aAAe,CACjC,KAAM,EAAU,QACjB,CACD,EAAM,mBAAmB,CAEzB,IAAM,EAAgB,CACpB,GAAG,EACH,UAAW,GAAa,GACxB,0BACD,CAID,EAAM,QAAQ,aAAc,mBAAoB,EAAW,EAAgB,CAAC,CAC5E,EAAM,QAAQ,mBAAoB,yBAA0B,EAAiB,CAC7E,EAAM,QAAQ,mBAAoB,yBAA0B,EAAiB,CAC7E,EAAM,QAAQ,eAAgB,qBAAsB,EAAa,EAAiB,EAAc,EAAc,CAAC,CAC/G,EAAM,QAAQ,eAAgB,qBAAsB,EAAa,EAAiB,EAAc,EAAc,CAAC,CAC/G,EAAM,QAAQ,YAAa,kBAAmBA,EAAc,EAAM,EAAiB,YAAa,EAAc,EAAc,CAAC,CAC7H,EAAM,QAAQ,cAAe,oBAAqBA,EAAc,EAAM,EAAiB,IAAA,GAAW,EAAc,EAAc,CAAC,CAC/H,EAAM,QAAQ,cAAe,oBAAqBA,EAAc,EAAM,EAAiB,IAAA,GAAW,EAAc,EAAc,CAAC,OACxH,EAAG,CACV,EAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAGS,GAAe,EAAkB,IAAiC,CAC7E,EAAO,QAAQ,MAAO,CAAE,UAAW,CACjC,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,OACR,EAAM,cAAc,eACtB,OAAO,EAAM,cAAc,aAC3B,EAAM,mBAAmB,EAG3B,EAAM,WAAW,aAAc,mBAAmB,CAClD,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,YAAa,kBAAkB,CAChD,EAAM,WAAW,cAAe,oBAAoB,OAC7C,EAAG,CACV,EAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAME,GAAmB,EAAkB,EAAmB,IAAsE,CAC9H,GAAS,yBACb,EAAM,QAAQC,EAAkB,CAAE,WAAY,UAAW,GAAI,mBAAoB,CAAC,CAElF,EAAiB,UAAU,EAAO,CAAE,WAAY,UAAW,GAAI,EAAW,CAAC,GAGhE,GAAa,EAAkB,EAAwB,EAA8D,CAAE,uBAAwB,GAAO,GAAW,CAC5K,EAAO,QAAQ,MAAO,CAAE,OAAM,qBAAsB,CAClD,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAO,KAAK,gEAAiE,CAC3E,OACA,kBACD,CAAC,CACF,OAGF,EAAgB,EAAO,EAAM,EAAQ,CAErC,EAAM,SAAS,EAA4B,EAAwB,EAAM,EAAQ,CAAC,CAClF,EAAM,SAAS,EAA0B,EAAsB,EAAM,EAAQ,CAAC,OACvE,EAAG,CACV,EAAO,MAAM,+CAA+C,EAAK,IAAK,EAAE,GAE1E,EAGS,EAA0B,GAA2B,CAChE,EAAO,SAAS,CAAE,kBAAmB,CACnC,GAAc,oBAAoBC,EAAsB,CACxD,GAAc,oBAAoBC,EAAgB,EAClD"}
1
+ {"version":3,"file":"init.js","names":["enrichResults","CustomFieldValue","CustomFieldDefinition","CustomValidator"],"sources":["../../src/utils/init.ts"],"sourcesContent":["import { DataTypes } from 'sequelize';\nimport type { ModelCtor } from 'sequelize-typescript';\nimport { customFields } from '@autofleet/common-types';\nimport { CUSTOM_FIELDS_SORT_SCOPE } from '@autofleet/common-types/lib/custom-fields';\nimport {\n CustomFieldDefinition,\n CustomFieldValue,\n CustomValidator,\n} from '../models';\nimport {\n beforeFind,\n enrichResults,\n beforeBulkUpdate,\n beforeUpdate,\n beforeCreate,\n beforeBulkCreate,\n} from '../hooks';\nimport { customFieldsFilterScope } from '../scopes';\nimport logger from './logger';\nimport type { CustomFieldOptions, ModelFetcher, Models } from '../types';\nimport { customFieldsSortScope } from '../scopes/filter';\n\nconst { CUSTOM_FIELDS_FILTER_SCOPE } = customFields;\n\nexport const addHooks = (\n models: Models[],\n getModel: ModelFetcher,\n sadotOptions: { useCustomFieldsEntries?: boolean; } = { useCustomFieldsEntries: false },\n): void => {\n models.forEach(async ({\n name, scopeAttributes, modelOptions, hasTypeId, getRequiredCustomFields,\n }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addHooks to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n model.rawAttributes.customFields = {\n type: DataTypes.VIRTUAL,\n };\n model.refreshAttributes();\n\n const enrichOptions = {\n ...sadotOptions,\n hasTypeId: hasTypeId ?? false,\n getRequiredCustomFields,\n };\n\n // Enrich modelOptions with getModel for validator relations support\n const enrichedModelOptions = {\n ...modelOptions,\n getModel,\n };\n\n // TODO: Uncomment after tests are passed\n // model.addHook('afterFind', workaround);\n model.addHook('beforeFind', 'sadot-beforeFind', beforeFind(scopeAttributes));\n model.addHook('beforeBulkCreate', 'sadot-beforeBulkCreate', beforeBulkCreate);\n model.addHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate', beforeBulkUpdate);\n model.addHook('beforeCreate', 'sadot-beforeCreate', beforeCreate(scopeAttributes, enrichedModelOptions, enrichOptions));\n model.addHook('beforeUpdate', 'sadot-beforeUpdate', beforeUpdate(scopeAttributes, enrichedModelOptions, enrichOptions));\n model.addHook('afterFind', 'sadot-afterFind', enrichResults(name, scopeAttributes, 'afterFind', enrichedModelOptions, enrichOptions));\n model.addHook('afterUpdate', 'sadot-afterUpdate', enrichResults(name, scopeAttributes, undefined, enrichedModelOptions, enrichOptions));\n model.addHook('afterCreate', 'sadot-afterCreate', enrichResults(name, scopeAttributes, undefined, enrichedModelOptions, enrichOptions));\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\nexport const removeHooks = (models: Models[], getModel: ModelFetcher): void => {\n models.forEach(async ({ name }) => {\n try {\n const model = getModel(name);\n if (!model) return;\n if (model.rawAttributes.customFields) {\n delete model.rawAttributes.customFields;\n model.refreshAttributes();\n }\n // model.removeHook('afterFind', 'sadot-workaround');\n model.removeHook('beforeFind', 'sadot-beforeFind');\n model.removeHook('beforeBulkCreate', 'sadot-beforeBulkCreate');\n model.removeHook('beforeBulkUpdate', 'sadot-beforeBulkUpdate');\n model.removeHook('beforeCreate', 'sadot-beforeCreate');\n model.removeHook('beforeUpdate', 'sadot-beforeUpdate');\n model.removeHook('afterFind', 'sadot-afterFind');\n model.removeHook('afterUpdate', 'sadot-afterUpdate');\n } catch (e) {\n logger.error(`Could not add custom fields hook to model ${name}. `, e);\n }\n });\n};\n\n/**\n * Necessary associations for the {@link customFieldsFilterScope} scope\n */\nconst addAssociations = (model: ModelCtor, modelName: string, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'>): void => {\n if (options?.useCustomFieldsEntries) return;\n model.hasMany(CustomFieldValue, { foreignKey: 'modelId', as: 'customFieldValue' });\n // TBC: maybe can be removed\n CustomFieldValue.belongsTo(model, { foreignKey: 'modelId', as: modelName });\n};\n\nexport const addScopes = (models: Models[], getModel: ModelFetcher, options: Pick<CustomFieldOptions, 'useCustomFieldsEntries'> = { useCustomFieldsEntries: false }): void => {\n models.forEach(async ({ name, scopeAttributes }) => {\n try {\n const model = getModel(name);\n if (!model) {\n logger.warn('sadot - tried to addScopes to a model that does not exist yet', {\n name,\n scopeAttributes,\n });\n return;\n }\n // Necessary associations for the filtering scope\n addAssociations(model, name, options);\n // Add filter scope\n model.addScope(CUSTOM_FIELDS_FILTER_SCOPE, customFieldsFilterScope(name, options));\n model.addScope(CUSTOM_FIELDS_SORT_SCOPE, customFieldsSortScope(name, options));\n } catch (e) {\n logger.error(`Could not add custom fields scopes to model ${name}. `, e);\n }\n });\n};\n\nexport const applyCustomAssociation = (models: Models[]): void => {\n models.forEach(({ modelOptions }) => {\n modelOptions?.customAssociation?.(CustomFieldDefinition);\n modelOptions?.customAssociation?.(CustomValidator);\n });\n};\n"],"mappings":"ssBAsBA,KAAM,CAAE,8BAA+B,EAE1B,GACX,EACA,EACA,EAAsD,CAAE,uBAAwB,GAAO,GAC9E,CACT,EAAO,QAAQ,MAAO,CACpB,OAAM,kBAAiB,eAAc,YAAW,6BAC5C,CACJ,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAO,KAAK,+DAAgE,CAC1E,OACA,kBACD,CAAC,CACF,OAEF,EAAM,cAAc,aAAe,CACjC,KAAM,EAAU,QACjB,CACD,EAAM,mBAAmB,CAEzB,IAAM,EAAgB,CACpB,GAAG,EACH,UAAW,GAAa,GACxB,0BACD,CAGK,EAAuB,CAC3B,GAAG,EACH,WACD,CAID,EAAM,QAAQ,aAAc,mBAAoB,EAAW,EAAgB,CAAC,CAC5E,EAAM,QAAQ,mBAAoB,yBAA0B,EAAiB,CAC7E,EAAM,QAAQ,mBAAoB,yBAA0B,EAAiB,CAC7E,EAAM,QAAQ,eAAgB,qBAAsB,EAAa,EAAiB,EAAsB,EAAc,CAAC,CACvH,EAAM,QAAQ,eAAgB,qBAAsB,EAAa,EAAiB,EAAsB,EAAc,CAAC,CACvH,EAAM,QAAQ,YAAa,kBAAmBA,EAAc,EAAM,EAAiB,YAAa,EAAsB,EAAc,CAAC,CACrI,EAAM,QAAQ,cAAe,oBAAqBA,EAAc,EAAM,EAAiB,IAAA,GAAW,EAAsB,EAAc,CAAC,CACvI,EAAM,QAAQ,cAAe,oBAAqBA,EAAc,EAAM,EAAiB,IAAA,GAAW,EAAsB,EAAc,CAAC,OAChI,EAAG,CACV,EAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAGS,GAAe,EAAkB,IAAiC,CAC7E,EAAO,QAAQ,MAAO,CAAE,UAAW,CACjC,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,OACR,EAAM,cAAc,eACtB,OAAO,EAAM,cAAc,aAC3B,EAAM,mBAAmB,EAG3B,EAAM,WAAW,aAAc,mBAAmB,CAClD,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,mBAAoB,yBAAyB,CAC9D,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,eAAgB,qBAAqB,CACtD,EAAM,WAAW,YAAa,kBAAkB,CAChD,EAAM,WAAW,cAAe,oBAAoB,OAC7C,EAAG,CACV,EAAO,MAAM,6CAA6C,EAAK,IAAK,EAAE,GAExE,EAME,GAAmB,EAAkB,EAAmB,IAAsE,CAC9H,GAAS,yBACb,EAAM,QAAQC,EAAkB,CAAE,WAAY,UAAW,GAAI,mBAAoB,CAAC,CAElF,EAAiB,UAAU,EAAO,CAAE,WAAY,UAAW,GAAI,EAAW,CAAC,GAGhE,GAAa,EAAkB,EAAwB,EAA8D,CAAE,uBAAwB,GAAO,GAAW,CAC5K,EAAO,QAAQ,MAAO,CAAE,OAAM,qBAAsB,CAClD,GAAI,CACF,IAAM,EAAQ,EAAS,EAAK,CAC5B,GAAI,CAAC,EAAO,CACV,EAAO,KAAK,gEAAiE,CAC3E,OACA,kBACD,CAAC,CACF,OAGF,EAAgB,EAAO,EAAM,EAAQ,CAErC,EAAM,SAAS,EAA4B,EAAwB,EAAM,EAAQ,CAAC,CAClF,EAAM,SAAS,EAA0B,EAAsB,EAAM,EAAQ,CAAC,OACvE,EAAG,CACV,EAAO,MAAM,+CAA+C,EAAK,IAAK,EAAE,GAE1E,EAGS,EAA0B,GAA2B,CAChE,EAAO,SAAS,CAAE,kBAAmB,CACnC,GAAc,oBAAoBC,EAAsB,CACxD,GAAc,oBAAoBC,EAAgB,EAClD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sadot",
3
- "version": "1.2.13",
3
+ "version": "1.2.14-beta-ed773b95.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,8 +52,8 @@
52
52
  "express": "^4.21.2",
53
53
  "npm-watch": "^0.11.0",
54
54
  "supertest": "^7.0.0",
55
- "@autofleet/errors": "^3.1.41",
56
55
  "@autofleet/logger": "^4.2.45",
56
+ "@autofleet/errors": "^3.1.41",
57
57
  "@autofleet/node-common": "^4.2.22",
58
58
  "@autofleet/zehut": "^4.7.5"
59
59
  },