@machinemetrics/mm-erp-sdk 0.1.1-beta.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.
Files changed (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/dist/config-2l5vnNkA.js +418 -0
  4. package/dist/config-2l5vnNkA.js.map +1 -0
  5. package/dist/connector-factory-CQ8e7Tae.js +21 -0
  6. package/dist/connector-factory-CQ8e7Tae.js.map +1 -0
  7. package/dist/hashed-cache-manager-Ci9X3GAB.js +292 -0
  8. package/dist/hashed-cache-manager-Ci9X3GAB.js.map +1 -0
  9. package/dist/index-Cn9ccxOO.js +179 -0
  10. package/dist/index-Cn9ccxOO.js.map +1 -0
  11. package/dist/index.d.ts +35 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/knexfile-1qKKIORB.js +20 -0
  14. package/dist/knexfile-1qKKIORB.js.map +1 -0
  15. package/dist/knexfile.d.ts +6 -0
  16. package/dist/knexfile.d.ts.map +1 -0
  17. package/dist/logger-QG73MndU.js +17523 -0
  18. package/dist/logger-QG73MndU.js.map +1 -0
  19. package/dist/migrations/20241015162631_create_cache_table.js +17 -0
  20. package/dist/migrations/20241015162631_create_cache_table.js.map +1 -0
  21. package/dist/migrations/20241015162632_create_sdk_cache_table.js +17 -0
  22. package/dist/migrations/20241015162632_create_sdk_cache_table.js.map +1 -0
  23. package/dist/migrations/20250103162631_create_record_tracking_table.js +17 -0
  24. package/dist/migrations/20250103162631_create_record_tracking_table.js.map +1 -0
  25. package/dist/mm-erp-sdk.js +3503 -0
  26. package/dist/mm-erp-sdk.js.map +1 -0
  27. package/dist/services/caching-service/batch-cache-manager.d.ts +52 -0
  28. package/dist/services/caching-service/batch-cache-manager.d.ts.map +1 -0
  29. package/dist/services/caching-service/hashed-cache-manager.d.ts +83 -0
  30. package/dist/services/caching-service/hashed-cache-manager.d.ts.map +1 -0
  31. package/dist/services/caching-service/index.d.ts +92 -0
  32. package/dist/services/caching-service/index.d.ts.map +1 -0
  33. package/dist/services/caching-service/record-tracking-manager.d.ts +15 -0
  34. package/dist/services/caching-service/record-tracking-manager.d.ts.map +1 -0
  35. package/dist/services/data-sync-service/configuration-manager.d.ts +50 -0
  36. package/dist/services/data-sync-service/configuration-manager.d.ts.map +1 -0
  37. package/dist/services/data-sync-service/data-sync-service.d.ts +2 -0
  38. package/dist/services/data-sync-service/data-sync-service.d.ts.map +1 -0
  39. package/dist/services/data-sync-service/index.d.ts +10 -0
  40. package/dist/services/data-sync-service/index.d.ts.map +1 -0
  41. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.d.ts +2 -0
  42. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.d.ts.map +1 -0
  43. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.js +41 -0
  44. package/dist/services/data-sync-service/jobs/clean-up-expired-cache.js.map +1 -0
  45. package/dist/services/data-sync-service/jobs/from-erp.d.ts +4 -0
  46. package/dist/services/data-sync-service/jobs/from-erp.d.ts.map +1 -0
  47. package/dist/services/data-sync-service/jobs/from-erp.js +42 -0
  48. package/dist/services/data-sync-service/jobs/from-erp.js.map +1 -0
  49. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.d.ts +2 -0
  50. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.d.ts.map +1 -0
  51. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.js +41 -0
  52. package/dist/services/data-sync-service/jobs/retry-failed-labor-tickets.js.map +1 -0
  53. package/dist/services/data-sync-service/jobs/run-migrations.d.ts +2 -0
  54. package/dist/services/data-sync-service/jobs/run-migrations.d.ts.map +1 -0
  55. package/dist/services/data-sync-service/jobs/run-migrations.js +21 -0
  56. package/dist/services/data-sync-service/jobs/run-migrations.js.map +1 -0
  57. package/dist/services/data-sync-service/jobs/to-erp.d.ts +4 -0
  58. package/dist/services/data-sync-service/jobs/to-erp.d.ts.map +1 -0
  59. package/dist/services/data-sync-service/jobs/to-erp.js +39 -0
  60. package/dist/services/data-sync-service/jobs/to-erp.js.map +1 -0
  61. package/dist/services/erp-api-services/errors.d.ts +22 -0
  62. package/dist/services/erp-api-services/errors.d.ts.map +1 -0
  63. package/dist/services/erp-api-services/graphql/graphql-service.d.ts +40 -0
  64. package/dist/services/erp-api-services/graphql/graphql-service.d.ts.map +1 -0
  65. package/dist/services/erp-api-services/graphql/types.d.ts +32 -0
  66. package/dist/services/erp-api-services/graphql/types.d.ts.map +1 -0
  67. package/dist/services/erp-api-services/index.d.ts +9 -0
  68. package/dist/services/erp-api-services/index.d.ts.map +1 -0
  69. package/dist/services/erp-api-services/oauth-client.d.ts +45 -0
  70. package/dist/services/erp-api-services/oauth-client.d.ts.map +1 -0
  71. package/dist/services/erp-api-services/rest/get-query-params.d.ts +50 -0
  72. package/dist/services/erp-api-services/rest/get-query-params.d.ts.map +1 -0
  73. package/dist/services/erp-api-services/rest/rest-api-service.d.ts +35 -0
  74. package/dist/services/erp-api-services/rest/rest-api-service.d.ts.map +1 -0
  75. package/dist/services/erp-api-services/types.d.ts +27 -0
  76. package/dist/services/erp-api-services/types.d.ts.map +1 -0
  77. package/dist/services/mm-api-service/index.d.ts +31 -0
  78. package/dist/services/mm-api-service/index.d.ts.map +1 -0
  79. package/dist/services/mm-api-service/mm-api-service.d.ts +214 -0
  80. package/dist/services/mm-api-service/mm-api-service.d.ts.map +1 -0
  81. package/dist/services/mm-api-service/token-mgr.d.ts +33 -0
  82. package/dist/services/mm-api-service/token-mgr.d.ts.map +1 -0
  83. package/dist/services/mm-api-service/types/checkpoint.d.ts +13 -0
  84. package/dist/services/mm-api-service/types/checkpoint.d.ts.map +1 -0
  85. package/dist/services/mm-api-service/types/entity-transformer.d.ts +58 -0
  86. package/dist/services/mm-api-service/types/entity-transformer.d.ts.map +1 -0
  87. package/dist/services/mm-api-service/types/mm-response-interfaces.d.ts +263 -0
  88. package/dist/services/mm-api-service/types/mm-response-interfaces.d.ts.map +1 -0
  89. package/dist/services/mm-api-service/types/receive-types.d.ts +57 -0
  90. package/dist/services/mm-api-service/types/receive-types.d.ts.map +1 -0
  91. package/dist/services/mm-api-service/types/send-types.d.ts +147 -0
  92. package/dist/services/mm-api-service/types/send-types.d.ts.map +1 -0
  93. package/dist/services/reporting-service/index.d.ts +5 -0
  94. package/dist/services/reporting-service/index.d.ts.map +1 -0
  95. package/dist/services/reporting-service/logger.d.ts +4 -0
  96. package/dist/services/reporting-service/logger.d.ts.map +1 -0
  97. package/dist/services/sql-server-erp-service/configuration.d.ts +15 -0
  98. package/dist/services/sql-server-erp-service/configuration.d.ts.map +1 -0
  99. package/dist/services/sql-server-erp-service/index.d.ts +16 -0
  100. package/dist/services/sql-server-erp-service/index.d.ts.map +1 -0
  101. package/dist/services/sql-server-erp-service/internal/sql-labor-ticket-operations.d.ts +31 -0
  102. package/dist/services/sql-server-erp-service/internal/sql-labor-ticket-operations.d.ts.map +1 -0
  103. package/dist/services/sql-server-erp-service/internal/sql-server-config.d.ts +65 -0
  104. package/dist/services/sql-server-erp-service/internal/sql-server-config.d.ts.map +1 -0
  105. package/dist/services/sql-server-erp-service/internal/sql-transaction-manager.d.ts +20 -0
  106. package/dist/services/sql-server-erp-service/internal/sql-transaction-manager.d.ts.map +1 -0
  107. package/dist/services/sql-server-erp-service/internal/types/sql-server-types.d.ts +3 -0
  108. package/dist/services/sql-server-erp-service/internal/types/sql-server-types.d.ts.map +1 -0
  109. package/dist/services/sql-server-erp-service/sql-server-helpers.d.ts +35 -0
  110. package/dist/services/sql-server-erp-service/sql-server-helpers.d.ts.map +1 -0
  111. package/dist/services/sql-server-erp-service/sql-server-service.d.ts +37 -0
  112. package/dist/services/sql-server-erp-service/sql-server-service.d.ts.map +1 -0
  113. package/dist/services/sql-server-erp-service/types/sql-input-param.d.ts +10 -0
  114. package/dist/services/sql-server-erp-service/types/sql-input-param.d.ts.map +1 -0
  115. package/dist/services/sqlite-service/index.d.ts +2 -0
  116. package/dist/services/sqlite-service/index.d.ts.map +1 -0
  117. package/dist/services/sqlite-service/sqlite-coordinator.d.ts +28 -0
  118. package/dist/services/sqlite-service/sqlite-coordinator.d.ts.map +1 -0
  119. package/dist/types/erp-connector.d.ts +40 -0
  120. package/dist/types/erp-connector.d.ts.map +1 -0
  121. package/dist/types/erp-types.d.ts +32 -0
  122. package/dist/types/erp-types.d.ts.map +1 -0
  123. package/dist/types/index.d.ts +7 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/utils/application-initializer.d.ts +15 -0
  126. package/dist/utils/application-initializer.d.ts.map +1 -0
  127. package/dist/utils/cleanup-numbers.d.ts +2 -0
  128. package/dist/utils/cleanup-numbers.d.ts.map +1 -0
  129. package/dist/utils/connector-factory.d.ts +8 -0
  130. package/dist/utils/connector-factory.d.ts.map +1 -0
  131. package/dist/utils/data-transformation.d.ts +20 -0
  132. package/dist/utils/data-transformation.d.ts.map +1 -0
  133. package/dist/utils/erp-type-from-entity.d.ts +5 -0
  134. package/dist/utils/erp-type-from-entity.d.ts.map +1 -0
  135. package/dist/utils/http-client.d.ts +35 -0
  136. package/dist/utils/http-client.d.ts.map +1 -0
  137. package/dist/utils/index.d.ts +57 -0
  138. package/dist/utils/index.d.ts.map +1 -0
  139. package/dist/utils/local-data-store/database-lock.d.ts +29 -0
  140. package/dist/utils/local-data-store/database-lock.d.ts.map +1 -0
  141. package/dist/utils/local-data-store/jobs-shared-data.d.ts +34 -0
  142. package/dist/utils/local-data-store/jobs-shared-data.d.ts.map +1 -0
  143. package/dist/utils/mm-labor-ticket-helpers.d.ts +9 -0
  144. package/dist/utils/mm-labor-ticket-helpers.d.ts.map +1 -0
  145. package/dist/utils/removeExtraneousFields.d.ts +6 -0
  146. package/dist/utils/removeExtraneousFields.d.ts.map +1 -0
  147. package/dist/utils/removeIdFieldFromPayload.d.ts +6 -0
  148. package/dist/utils/removeIdFieldFromPayload.d.ts.map +1 -0
  149. package/dist/utils/resource-group.d.ts +11 -0
  150. package/dist/utils/resource-group.d.ts.map +1 -0
  151. package/dist/utils/standard-process-drivers/error-processor.d.ts +68 -0
  152. package/dist/utils/standard-process-drivers/error-processor.d.ts.map +1 -0
  153. package/dist/utils/standard-process-drivers/index.d.ts +3 -0
  154. package/dist/utils/standard-process-drivers/index.d.ts.map +1 -0
  155. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.d.ts +18 -0
  156. package/dist/utils/standard-process-drivers/labor-ticket-erp-synchronizer.d.ts.map +1 -0
  157. package/dist/utils/standard-process-drivers/mm-entity-processor.d.ts +40 -0
  158. package/dist/utils/standard-process-drivers/mm-entity-processor.d.ts.map +1 -0
  159. package/dist/utils/standard-process-drivers/standard-process-drivers.d.ts +178 -0
  160. package/dist/utils/standard-process-drivers/standard-process-drivers.d.ts.map +1 -0
  161. package/dist/utils/time-utils.d.ts +31 -0
  162. package/dist/utils/time-utils.d.ts.map +1 -0
  163. package/dist/utils/timezone.d.ts +21 -0
  164. package/dist/utils/timezone.d.ts.map +1 -0
  165. package/dist/utils/trimObjectValues.d.ts +5 -0
  166. package/dist/utils/trimObjectValues.d.ts.map +1 -0
  167. package/dist/utils/uniqueRows.d.ts +9 -0
  168. package/dist/utils/uniqueRows.d.ts.map +1 -0
  169. package/package.json +50 -0
  170. package/src/index.ts +98 -0
  171. package/src/knexfile.ts +21 -0
  172. package/src/migrations/20241015162631_create_cache_table.ts +15 -0
  173. package/src/migrations/20241015162632_create_sdk_cache_table.ts +15 -0
  174. package/src/migrations/20250103162631_create_record_tracking_table.ts +18 -0
  175. package/src/services/caching-service/batch-cache-manager.ts +111 -0
  176. package/src/services/caching-service/hashed-cache-manager.ts +253 -0
  177. package/src/services/caching-service/index.ts +114 -0
  178. package/src/services/caching-service/record-tracking-manager.ts +41 -0
  179. package/src/services/data-sync-service/configuration-manager.ts +153 -0
  180. package/src/services/data-sync-service/data-sync-service.ts +100 -0
  181. package/src/services/data-sync-service/index.ts +14 -0
  182. package/src/services/data-sync-service/jobs/clean-up-expired-cache.ts +38 -0
  183. package/src/services/data-sync-service/jobs/from-erp.ts +55 -0
  184. package/src/services/data-sync-service/jobs/retry-failed-labor-tickets.ts +44 -0
  185. package/src/services/data-sync-service/jobs/run-migrations.ts +21 -0
  186. package/src/services/data-sync-service/jobs/to-erp.ts +53 -0
  187. package/src/services/erp-api-services/errors.ts +115 -0
  188. package/src/services/erp-api-services/graphql/graphql-service.ts +116 -0
  189. package/src/services/erp-api-services/graphql/types.ts +30 -0
  190. package/src/services/erp-api-services/index.ts +14 -0
  191. package/src/services/erp-api-services/oauth-client.ts +72 -0
  192. package/src/services/erp-api-services/rest/get-query-params.ts +63 -0
  193. package/src/services/erp-api-services/rest/rest-api-service.ts +212 -0
  194. package/src/services/erp-api-services/types.ts +28 -0
  195. package/src/services/mm-api-service/index.ts +83 -0
  196. package/src/services/mm-api-service/mm-api-service.ts +685 -0
  197. package/src/services/mm-api-service/token-mgr.ts +123 -0
  198. package/src/services/mm-api-service/types/checkpoint.ts +13 -0
  199. package/src/services/mm-api-service/types/entity-transformer.ts +298 -0
  200. package/src/services/mm-api-service/types/mm-response-interfaces.ts +293 -0
  201. package/src/services/mm-api-service/types/receive-types.ts +89 -0
  202. package/src/services/mm-api-service/types/send-types.ts +383 -0
  203. package/src/services/reporting-service/index.ts +4 -0
  204. package/src/services/reporting-service/logger.ts +117 -0
  205. package/src/services/sql-server-erp-service/configuration.ts +14 -0
  206. package/src/services/sql-server-erp-service/index.ts +18 -0
  207. package/src/services/sql-server-erp-service/internal/sql-labor-ticket-operations.ts +66 -0
  208. package/src/services/sql-server-erp-service/internal/sql-server-config.ts +38 -0
  209. package/src/services/sql-server-erp-service/internal/sql-transaction-manager.ts +45 -0
  210. package/src/services/sql-server-erp-service/internal/types/sql-server-types.ts +23 -0
  211. package/src/services/sql-server-erp-service/sql-server-helpers.ts +99 -0
  212. package/src/services/sql-server-erp-service/sql-server-service.ts +191 -0
  213. package/src/services/sql-server-erp-service/types/sql-input-param.ts +14 -0
  214. package/src/services/sqlite-service/index.ts +1 -0
  215. package/src/services/sqlite-service/sqlite-coordinator.ts +80 -0
  216. package/src/types/erp-connector.ts +46 -0
  217. package/src/types/erp-types.ts +37 -0
  218. package/src/types/index.ts +13 -0
  219. package/src/utils/application-initializer.ts +62 -0
  220. package/src/utils/cleanup-numbers.ts +5 -0
  221. package/src/utils/connector-factory.ts +34 -0
  222. package/src/utils/data-transformation.ts +58 -0
  223. package/src/utils/erp-type-from-entity.ts +12 -0
  224. package/src/utils/http-client.ts +137 -0
  225. package/src/utils/index.ts +71 -0
  226. package/src/utils/local-data-store/database-lock.ts +86 -0
  227. package/src/utils/local-data-store/jobs-shared-data.ts +111 -0
  228. package/src/utils/mm-labor-ticket-helpers.ts +28 -0
  229. package/src/utils/removeExtraneousFields.ts +22 -0
  230. package/src/utils/removeIdFieldFromPayload.ts +22 -0
  231. package/src/utils/resource-group.ts +92 -0
  232. package/src/utils/standard-process-drivers/error-processor.ts +417 -0
  233. package/src/utils/standard-process-drivers/index.ts +6 -0
  234. package/src/utils/standard-process-drivers/labor-ticket-erp-synchronizer.ts +261 -0
  235. package/src/utils/standard-process-drivers/mm-entity-processor.ts +265 -0
  236. package/src/utils/standard-process-drivers/standard-process-drivers.ts +459 -0
  237. package/src/utils/time-utils.ts +131 -0
  238. package/src/utils/timezone.ts +96 -0
  239. package/src/utils/trimObjectValues.ts +12 -0
  240. package/src/utils/uniqueRows.ts +40 -0
@@ -0,0 +1,111 @@
1
+ import { HashedCacheManager } from "./hashed-cache-manager";
2
+ import { ERPObjType } from "../../types/erp-types";
3
+
4
+ export type BatchResult<T> = {
5
+ duplicates: T[];
6
+ nonDuplicates: T[];
7
+ };
8
+
9
+ type BatchCacheManagerOptions = {
10
+ ttl?: number;
11
+ tableName?: string;
12
+ };
13
+
14
+ export class BatchCacheManager {
15
+ private cacheManager: HashedCacheManager;
16
+
17
+ constructor(options?: BatchCacheManagerOptions) {
18
+ this.cacheManager = new HashedCacheManager({
19
+ ttl: options?.ttl,
20
+ tableName: options?.tableName,
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Checks a batch of objects against the cache and separates them into duplicates and non-duplicates
26
+ * @param type The type of objects
27
+ * @param objects Array of objects to check
28
+ * @returns Object containing arrays of duplicate and non-duplicate objects
29
+ */
30
+ async dedupeBatch(
31
+ type: ERPObjType,
32
+ objects: object[]
33
+ ): Promise<BatchResult<object>> {
34
+ const result: BatchResult<object> = {
35
+ duplicates: [],
36
+ nonDuplicates: [],
37
+ };
38
+
39
+ // Check each object individually
40
+ for (const object of objects) {
41
+ const hasChanged = await this.cacheManager.hasChanged(type, object);
42
+
43
+ if (hasChanged) {
44
+ result.nonDuplicates.push(object);
45
+ } else {
46
+ result.duplicates.push(object);
47
+ }
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ /**
54
+ * Stores a batch of objects in the cache
55
+ * @param type The type of objects
56
+ * @param objects Array of objects to store
57
+ */
58
+ async storeBatch(
59
+ type: ERPObjType,
60
+ objects: object[]
61
+ ): Promise<{ totalInserted: number }> {
62
+ // Store each object individually
63
+ let totalInserted = 0;
64
+ for (const object of objects) {
65
+ if (await this.cacheManager.store(type, object)) {
66
+ totalInserted++;
67
+ }
68
+ }
69
+ return { totalInserted };
70
+ }
71
+
72
+ /**
73
+ * Removes objects from the cache
74
+ * @param type The type of objects
75
+ * @param objects Array of objects to remove
76
+ */
77
+ async removeObjects(type: ERPObjType, objects: object[]): Promise<void> {
78
+ // Remove each object individually
79
+ for (const object of objects) {
80
+ await this.cacheManager.removeRecord(type, object);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Gets all records of a specific type
86
+ */
87
+ async getRecordsByType(type: ERPObjType): Promise<string[]> {
88
+ return this.cacheManager.getRecordsByType(type);
89
+ }
90
+
91
+ /**
92
+ * Removes all records of a specific type
93
+ */
94
+ async removeRecordsByType(type: ERPObjType): Promise<void> {
95
+ return this.cacheManager.removeRecordsByType(type);
96
+ }
97
+
98
+ /**
99
+ * Clears all records from the cache
100
+ */
101
+ async clear(): Promise<void> {
102
+ return this.cacheManager.clear();
103
+ }
104
+
105
+ /**
106
+ * Cleans up resources
107
+ */
108
+ async destroy(): Promise<void> {
109
+ return this.cacheManager.destroy();
110
+ }
111
+ }
@@ -0,0 +1,253 @@
1
+ import knex, { Knex } from "knex";
2
+ import config from "../../knexfile";
3
+ import stringify from "json-stable-stringify";
4
+ import XXH from "xxhashjs";
5
+ import { ERPObjType } from "../../types/erp-types";
6
+ import { CacheMetrics } from "./index";
7
+ import { CoreConfiguration } from "../data-sync-service/configuration-manager";
8
+ import { logger } from "../reporting-service";
9
+
10
+ type HashedCacheManagerOptions = {
11
+ ttl?: number;
12
+ tableName?: string;
13
+ };
14
+
15
+ export class HashedCacheManager {
16
+ private static TABLE_NAME = "sdk_cache";
17
+ private db: Knex;
18
+ private options: HashedCacheManagerOptions;
19
+ private static readonly SEED = 0xabcd; // Arbitrary seed for hashing
20
+ private isDestroyed: boolean = false;
21
+ private metrics: CacheMetrics = {
22
+ recordCounts: {},
23
+ };
24
+
25
+ constructor(options?: HashedCacheManagerOptions) {
26
+ this.options = {
27
+ ttl: options?.ttl || CoreConfiguration.inst().cacheTTL,
28
+ tableName: options?.tableName || HashedCacheManager.TABLE_NAME,
29
+ };
30
+ this.db = knex({
31
+ ...config.local,
32
+ pool: {
33
+ min: 0,
34
+ max: 10,
35
+ },
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Checks if the cache manager is still valid
41
+ * @throws Error if the cache manager has been destroyed
42
+ */
43
+ private checkValid(): void {
44
+ if (this.isDestroyed) {
45
+ throw new Error("Cache manager has been destroyed");
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Generates a stable hash of a record using JSON stringify + xxhash
51
+ */
52
+ public static hashRecord(record: object): string {
53
+ try {
54
+ const serialized = stringify(record);
55
+ if (!serialized) {
56
+ throw new Error("Failed to serialize record for hashing");
57
+ }
58
+ const hash = XXH.h64(serialized, HashedCacheManager.SEED).toString(16);
59
+ return hash;
60
+ } catch (error) {
61
+ if (error instanceof Error && error.message.includes("circular")) {
62
+ throw new Error("Failed to serialize record for hashing");
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Gets a record from the cache
70
+ * @param type The type of record
71
+ * @param hash The hash of the record
72
+ * @returns The record if it exists, null otherwise
73
+ */
74
+ private async getRecord(
75
+ type: ERPObjType,
76
+ hash: string
77
+ ): Promise<{ key: string } | null> {
78
+ this.checkValid();
79
+ return this.db(this.options.tableName)
80
+ .select("key")
81
+ .where({ type, key: hash })
82
+ .first();
83
+ }
84
+
85
+ /**
86
+ * Stores a record in the cache
87
+ * @param type The type of record
88
+ * @param record The record to store
89
+ * @returns true if a new record was created, false if an existing record was updated
90
+ */
91
+ public async store(type: ERPObjType, record: object): Promise<boolean> {
92
+ if (!this.isDestroyed && record) {
93
+ try {
94
+ const hash = HashedCacheManager.hashRecord(record);
95
+ const now = new Date();
96
+
97
+ // First check if record exists with same type and hash
98
+ const existing = await this.db(this.options.tableName)
99
+ .where({
100
+ type,
101
+ key: hash,
102
+ })
103
+ .first();
104
+
105
+ if (existing) {
106
+ return false; // No need to update, hash hasn't changed
107
+ } else {
108
+ // Insert new record with minimal data
109
+ const result = await this.db(this.options.tableName)
110
+ .insert({
111
+ type,
112
+ key: hash,
113
+ created_at: now,
114
+ })
115
+ .returning("id");
116
+ return result.length > 0;
117
+ }
118
+ } catch (error) {
119
+ logger.error("Error storing record:", error);
120
+ throw error;
121
+ }
122
+ }
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Checks if a record has changed since last seen
128
+ * @param type The type of record
129
+ * @param record The record to check
130
+ * @returns true if the record has changed or is new
131
+ */
132
+ async hasChanged(type: ERPObjType, record: object): Promise<boolean> {
133
+ this.checkValid();
134
+ const newHash = HashedCacheManager.hashRecord(record);
135
+ const existing = await this.getRecord(type, newHash);
136
+ return !existing;
137
+ }
138
+
139
+ /**
140
+ * Checks if a record has changed and stores it if it has
141
+ * @param type The type of record
142
+ * @param record The record to check and store
143
+ * @returns true if the record was changed or is new
144
+ */
145
+ async upsert(type: ERPObjType, record: object): Promise<boolean> {
146
+ this.checkValid();
147
+ const hasChanged = await this.hasChanged(type, record);
148
+ if (hasChanged) {
149
+ await this.store(type, record as Record<string, unknown>);
150
+ }
151
+ return hasChanged;
152
+ }
153
+
154
+ /**
155
+ * Removes expired records based on TTL
156
+ */
157
+ async removeExpiredObjects(): Promise<void> {
158
+ this.checkValid();
159
+ const ttl = this.options.ttl;
160
+ if (!ttl) return;
161
+
162
+ const ttlMilliseconds = ttl * 1000;
163
+ const expirationLimitDate = new Date(Date.now() - ttlMilliseconds);
164
+ const expirationLimit = expirationLimitDate
165
+ .toISOString()
166
+ .slice(0, 19)
167
+ .replace("T", " ");
168
+
169
+ await this.db(this.options.tableName)
170
+ .where("created_at", "<", expirationLimit)
171
+ .del();
172
+ }
173
+
174
+ /**
175
+ * Gets all records of a specific type
176
+ */
177
+ async getRecordsByType(type: ERPObjType): Promise<string[]> {
178
+ this.checkValid();
179
+ const records = await this.db(this.options.tableName)
180
+ .select("key")
181
+ .where({ type });
182
+
183
+ return records.map((record) => record.key);
184
+ }
185
+
186
+ /**
187
+ * Removes all records of a specific type
188
+ */
189
+ async removeRecordsByType(type: ERPObjType): Promise<void> {
190
+ this.checkValid();
191
+ await this.db(this.options.tableName).where({ type }).del();
192
+ }
193
+
194
+ /**
195
+ * Removes a specific record
196
+ */
197
+ public async removeRecord(type: ERPObjType, record: object): Promise<void> {
198
+ if (!this.isDestroyed) {
199
+ try {
200
+ const hash = HashedCacheManager.hashRecord(record);
201
+ await this.db(this.options.tableName)
202
+ .where({ type, key: hash }) // Use key for deletion
203
+ .del();
204
+ } catch (error) {
205
+ logger.error("Error removing record:", error);
206
+ throw error;
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Clears all records from the cache
213
+ */
214
+ async clear(): Promise<void> {
215
+ this.checkValid();
216
+ await this.db(this.options.tableName).del();
217
+ }
218
+
219
+ /**
220
+ * Cleans up database connection and marks the cache manager as destroyed
221
+ */
222
+ async destroy(): Promise<void> {
223
+ if (!this.isDestroyed) {
224
+ await this.db.destroy();
225
+ this.isDestroyed = true;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Gets the current cache metrics
231
+ * @returns The current cache metrics
232
+ */
233
+ async getMetrics(): Promise<CacheMetrics> {
234
+ this.checkValid();
235
+
236
+ // Get counts for each type
237
+ const counts = (await this.db(this.options.tableName)
238
+ .select("type")
239
+ .count("* as count")
240
+ .groupBy("type")) as Array<{ type: string; count: string }>;
241
+
242
+ // Update metrics
243
+ this.metrics.recordCounts = counts.reduce(
244
+ (acc, row) => {
245
+ acc[row.type] = parseInt(row.count, 10);
246
+ return acc;
247
+ },
248
+ {} as Record<string, number>
249
+ );
250
+
251
+ return this.metrics;
252
+ }
253
+ }
@@ -0,0 +1,114 @@
1
+ import { ERPObjType, ERPObject } from "../../types/erp-types";
2
+
3
+ export interface CacheServiceConfig {
4
+ tableName: string;
5
+ ttl?: number;
6
+ }
7
+
8
+ export interface CacheMetrics {
9
+ /**
10
+ * Count of records by type in the cache
11
+ * Key is the ERPObjType string value
12
+ */
13
+ recordCounts: {
14
+ [type: string]: number;
15
+ };
16
+ }
17
+
18
+ export interface CacheService {
19
+ /**
20
+ * Checks if a record has changed since last seen
21
+ * @param type The type of record
22
+ * @param key The unique key for the record
23
+ * @param record The record to check
24
+ * @returns true if the record has changed or is new
25
+ */
26
+ hasChanged(
27
+ type: ERPObjType,
28
+ key: string,
29
+ record: ERPObject
30
+ ): Promise<boolean>;
31
+
32
+ /**
33
+ * Stores a record in the cache
34
+ * @param type The type of record
35
+ * @param key The unique key for the record
36
+ * @param record The record to store
37
+ */
38
+ store(type: ERPObjType, key: string, record: ERPObject): Promise<void>;
39
+
40
+ /**
41
+ * Checks if a record has changed and stores it if it has
42
+ * @param type The type of record
43
+ * @param key The unique key for the record
44
+ * @param record The record to check and store
45
+ * @returns true if the record was changed or is new
46
+ */
47
+ upsert(type: ERPObjType, key: string, record: ERPObject): Promise<boolean>;
48
+
49
+ /**
50
+ * Checks a batch of objects against the cache and separates them into duplicates and non-duplicates
51
+ * @param type The type of objects
52
+ * @param objects Array of objects to check
53
+ * @param idField The field to use as the unique identifier
54
+ * @returns Object containing arrays of duplicate and non-duplicate objects
55
+ */
56
+ checkBatch(
57
+ type: ERPObjType,
58
+ objects: ERPObject[],
59
+ idField: string
60
+ ): Promise<{ duplicates: ERPObject[]; nonDuplicates: ERPObject[] }>;
61
+
62
+ /**
63
+ * Stores a batch of objects in the cache
64
+ * @param type The type of objects
65
+ * @param objects Array of objects to store
66
+ * @param idField The field to use as the unique identifier
67
+ */
68
+ storeBatch(
69
+ type: ERPObjType,
70
+ objects: ERPObject[],
71
+ idField: string
72
+ ): Promise<void>;
73
+
74
+ /**
75
+ * Gets all records of a specific type
76
+ * @param type The type of records to get
77
+ * @returns Array of records with their values
78
+ */
79
+ getRecordsByType(type: ERPObjType): Promise<string[]>;
80
+
81
+ /**
82
+ * Removes all records of a specific type
83
+ * @param type The type of records to remove
84
+ */
85
+ removeRecordsByType(type: ERPObjType): Promise<void>;
86
+
87
+ /**
88
+ * Removes a specific record
89
+ * @param type The type of record
90
+ * @param key The unique key for the record
91
+ */
92
+ removeRecord(type: ERPObjType, key: string): Promise<void>;
93
+
94
+ /**
95
+ * Removes expired records based on TTL
96
+ */
97
+ removeExpiredObjects(): Promise<void>;
98
+
99
+ /**
100
+ * Clears all records from the cache
101
+ */
102
+ clear(): Promise<void>;
103
+
104
+ /**
105
+ * Gets the current cache metrics
106
+ * @returns The current cache metrics
107
+ */
108
+ getMetrics(): CacheMetrics;
109
+
110
+ /**
111
+ * Cleans up resources and marks the service as destroyed
112
+ */
113
+ destroy(): Promise<void>;
114
+ }
@@ -0,0 +1,41 @@
1
+ import knex, { Knex } from "knex";
2
+ import config from "../../knexfile";
3
+ import { ERPObjType } from "../../types/erp-types";
4
+
5
+ const DEFAULT_RECORD_TRACKING_TABLE_NAME = "record_tracking";
6
+
7
+ export type RecordTrackingObject = {
8
+ entityType?: ERPObjType;
9
+ lastValue: string; // Typically date/time string
10
+ recordId: string; // A value to disambiguate records with the same lastValue
11
+ };
12
+
13
+ export class RecordTrackingManager {
14
+ db: Knex;
15
+
16
+ constructor() {
17
+ this.db = knex(config.local);
18
+ }
19
+
20
+ async updateRecord(record: RecordTrackingObject) {
21
+ if (record.lastValue && record.recordId) {
22
+ const recordUpdated = await this.db(DEFAULT_RECORD_TRACKING_TABLE_NAME)
23
+ .where({ entityType: record.entityType })
24
+ .update(record);
25
+
26
+ if (recordUpdated < 1) {
27
+ await this.db(DEFAULT_RECORD_TRACKING_TABLE_NAME).insert(record);
28
+ }
29
+ }
30
+ }
31
+
32
+ async getTrackingRecord(type: ERPObjType) {
33
+ return await this.db(DEFAULT_RECORD_TRACKING_TABLE_NAME)
34
+ .select("*")
35
+ .where({ entityType: type.toString() });
36
+ }
37
+
38
+ async destroy() {
39
+ return this.db.destroy();
40
+ }
41
+ }
@@ -0,0 +1,153 @@
1
+ import "dotenv/config";
2
+ import { configureLogger } from "../reporting-service/logger";
3
+ import { SQLServerConfiguration } from "../sql-server-erp-service/configuration";
4
+
5
+ export class CoreConfiguration {
6
+ private static instance: CoreConfiguration;
7
+
8
+ // General Configuration
9
+ public readonly logLevel: string;
10
+ public readonly erpSystem: string;
11
+ public readonly nodeEnv: string;
12
+
13
+ // MM API (aka "Mapping") Service
14
+ public readonly mmERPSvcApiBaseUrl: string;
15
+ public readonly mmApiBaseUrl: string;
16
+ public readonly mmApiAuthToken: string;
17
+ public readonly mmApiRetryAttempts: number;
18
+
19
+ // Caching (optionally used for interacting with the MM API)
20
+ public readonly cacheTTL: number;
21
+
22
+ // ERP API Service
23
+ public readonly erpApiRetryAttemptsDef: number; //Retry attempts for ERP API
24
+ public readonly erpApiPagingLimit: number; //Pagination limit for ERP API
25
+
26
+ // Job timing Intervals
27
+ public readonly fromErpInterval: string;
28
+ public readonly toErpInterval: string;
29
+ public readonly retryLaborTicketsInterval: string;
30
+ public readonly cacheExpirationCheckInterval: string;
31
+
32
+ private constructor() {
33
+ this.logLevel = process.env.LOG_LEVEL || "info";
34
+ this.erpSystem = process.env.ERP_SYSTEM || "template";
35
+ this.nodeEnv = process.env.NODE_ENV || "development";
36
+
37
+ //#region MM API (aka "Mapping") Service
38
+ /**
39
+ * MM ERP Service REST API URL (typically https://erp-api.svc.machinemetrics.com)
40
+ */
41
+ this.mmERPSvcApiBaseUrl = process.env.MM_MAPPING_SERVICE_URL || "";
42
+
43
+ /**
44
+ * MM REST API URL (typically https://api.machinemetrics.com)
45
+ */
46
+ this.mmApiBaseUrl = process.env.MM_MAPPING_AUTH_SERVICE_URL || "";
47
+
48
+ /**
49
+ * Company Auth Token
50
+ */
51
+ this.mmApiAuthToken = process.env.MM_MAPPING_SERVICE_TOKEN || "";
52
+
53
+ /**
54
+ * Number of retry attempts for MM API calls
55
+ */
56
+ this.mmApiRetryAttempts = parseInt(process.env.RETRY_ATTEMPTS || "0");
57
+ //#endregion MM API (aka "Mapping") Service
58
+
59
+ //#region ERP API Service
60
+ /**
61
+ * Number of retry attempts for ERP API calls
62
+ */
63
+ this.erpApiRetryAttemptsDef = parseInt(
64
+ process.env.ERP_API_RETRY_ATTEMPTS_DEF || "3"
65
+ );
66
+
67
+ /**
68
+ * Default pagination limit for ERP API
69
+ */
70
+ this.erpApiPagingLimit = parseInt(process.env.ERP_PAGINATION_LIMIT || "0");
71
+ //#endregion ERP API Service
72
+
73
+ /**
74
+ * For how to define the intervals, see Bree's documentation: https://github.com/breejs/bree
75
+ */
76
+ this.fromErpInterval =
77
+ process.env.FROM_ERP_INTERVAL || process.env.POLL_INTERVAL || "5 min";
78
+ this.toErpInterval = process.env.TO_ERP_INTERVAL || "5 min";
79
+ this.retryLaborTicketsInterval =
80
+ process.env.RETRY_LABOR_TICKETS_INTERVAL || "30 min";
81
+ this.cacheExpirationCheckInterval =
82
+ process.env.CACHE_EXPIRATION_CHECK_INTERVAL || "5 min";
83
+
84
+ /**
85
+ * Cache TTL (in seconds)
86
+ */
87
+ const cacheTTLDef = 7 * 24 * 60 * 60; // 7 days
88
+ this.cacheTTL = parseInt(process.env.CACHE_TTL || cacheTTLDef.toString());
89
+
90
+ // Configure the logger with our settings
91
+ configureLogger(this.logLevel, this.nodeEnv);
92
+ }
93
+
94
+ public static inst(): CoreConfiguration {
95
+ if (!CoreConfiguration.instance) {
96
+ CoreConfiguration.instance = new CoreConfiguration();
97
+ }
98
+ return CoreConfiguration.instance;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Helper function to get the SQL Server Configuration for collectors that use SQL Server to interact with the ERP
104
+ */
105
+ export const getSQLServerConfiguration = (): SQLServerConfiguration => {
106
+ return {
107
+ username: process.env.ERP_SQLSERVER_USERNAME || "",
108
+ password: process.env.ERP_SQLSERVER_PASSWORD || "",
109
+ database: process.env.ERP_SQLSERVER_DATABASE || "",
110
+ host:
111
+ process.env.ERP_SQLSERVER_HOST || process.env.ERP_SQLSERVER_SERVER || "",
112
+ port: process.env.ERP_SQLSERVER_PORT || "1433",
113
+ connectionTimeout: process.env.ERP_SQLSERVER_CONNECTION_TIMEOUT || "30000",
114
+ requestTimeout: process.env.ERP_SQLSERVER_REQUEST_TIMEOUT || "60000",
115
+ poolMax: process.env.ERP_SQLSERVER_MAX || "10",
116
+ poolMin: process.env.ERP_SQLSERVER_MIN || "0",
117
+ idleTimeoutMillis:
118
+ process.env.ERP_SQLSERVER_IDLE_TIMEOUT_MMILLIS || "30000",
119
+ encrypt: process.env.ERP_SQLSERVER_ENCRYPT === "true",
120
+ trustServer: process.env.ERP_SQLSERVER_TRUST_SERVER === "true",
121
+ };
122
+ };
123
+
124
+ /**
125
+ * Parameters required to connect to an ERP system via its API.
126
+ * Contains all the necessary settings to establish a connection and authenticate with an ERP system's API.
127
+ */
128
+ export class ErpApiConnectionParams {
129
+ constructor(
130
+ public readonly erpApiUrl: string, // Base url of ERP
131
+ public readonly erpApiClientId: string, // Client ID to authenticate with ERP
132
+ public readonly erpApiClientSecret: string, // Client Secret to authenticate with ERP
133
+ public readonly erpApiOrganizationId: string, // Organization / tenant Id
134
+ public readonly erpAuthBaseUrl: string, // Auth base url
135
+ public readonly retryAttempts: number = 3 // Number of retry attempts for API calls
136
+ ) {}
137
+ }
138
+
139
+ /**
140
+ * Helper function to get the ERP API Connection Parameters
141
+ * Not all connectors use these, but keeping these commonly values in one place may
142
+ * make it easier to set and understand env var names set in App.
143
+ */
144
+ export const getErpApiConnectionParams = (): ErpApiConnectionParams => {
145
+ return new ErpApiConnectionParams(
146
+ process.env.ERP_API_URL || "",
147
+ process.env.ERP_API_CLIENT_ID || "",
148
+ process.env.ERP_API_CLIENT_SECRET || "",
149
+ process.env.ERP_API_ORGANIZATION_ID || "",
150
+ process.env.ERP_AUTH_BASE_URL || "",
151
+ parseInt(process.env.ERP_API_RETRY_ATTEMPTS || "3")
152
+ );
153
+ };