@memberjunction/integration-connectors 0.0.1 → 5.9.0

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.
@@ -0,0 +1,221 @@
1
+ import { BaseIntegrationConnector, } from '@memberjunction/integration-engine';
2
+ import sql from 'mssql';
3
+ /**
4
+ * Abstract base class for connectors that read from relational databases via mssql.
5
+ * Handles connection management, object/field discovery, and connection testing.
6
+ * Subclasses must implement FetchChanges to define record extraction logic.
7
+ */
8
+ export class RelationalDBConnector extends BaseIntegrationConnector {
9
+ constructor() {
10
+ super(...arguments);
11
+ /** Cache of open connection pools keyed by "server|database" */
12
+ this.poolCache = new Map();
13
+ }
14
+ /**
15
+ * Parses the Configuration JSON on a CompanyIntegration to extract connection details.
16
+ * @param companyIntegration - The entity containing Configuration JSON
17
+ * @returns Parsed connection configuration
18
+ * @throws Error if Configuration is missing or malformed
19
+ */
20
+ ParseConnectionConfig(companyIntegration) {
21
+ const configJson = companyIntegration.Get('Configuration');
22
+ if (!configJson) {
23
+ throw new Error('CompanyIntegration.Configuration is null or empty');
24
+ }
25
+ const parsed = JSON.parse(configJson);
26
+ const server = parsed['server'];
27
+ const database = parsed['database'];
28
+ const user = parsed['user'];
29
+ const password = parsed['password'];
30
+ const schema = parsed['schema'] ?? 'dbo';
31
+ if (!server || !database || !user || !password) {
32
+ throw new Error('Configuration JSON must contain server, database, user, and password fields');
33
+ }
34
+ return { Server: server, Database: database, User: user, Password: password, Schema: schema };
35
+ }
36
+ /**
37
+ * Opens or reuses a mssql ConnectionPool for the given configuration.
38
+ * Pools are cached by server+database to avoid redundant connections.
39
+ * @param config - Connection configuration
40
+ * @returns An open ConnectionPool
41
+ */
42
+ async GetPool(config) {
43
+ const cacheKey = `${config.Server}|${config.Database}`;
44
+ const existing = this.poolCache.get(cacheKey);
45
+ if (existing?.connected) {
46
+ return existing;
47
+ }
48
+ const pool = new sql.ConnectionPool({
49
+ server: config.Server,
50
+ database: config.Database,
51
+ user: config.User,
52
+ password: config.Password,
53
+ options: {
54
+ encrypt: false,
55
+ trustServerCertificate: true,
56
+ },
57
+ });
58
+ await pool.connect();
59
+ this.poolCache.set(cacheKey, pool);
60
+ return pool;
61
+ }
62
+ /**
63
+ * Tests connectivity to the source database by running SELECT 1.
64
+ * @param companyIntegration - The company integration with connection configuration
65
+ * @param _contextUser - User context for authorization
66
+ * @returns Connection test result with success/failure and message
67
+ */
68
+ async TestConnection(companyIntegration, _contextUser) {
69
+ try {
70
+ const config = this.ParseConnectionConfig(companyIntegration);
71
+ const pool = await this.GetPool(config);
72
+ const result = await pool.request().query('SELECT @@VERSION AS version');
73
+ const version = result.recordset[0]?.version ?? 'Unknown';
74
+ return {
75
+ Success: true,
76
+ Message: `Connected to ${config.Database} on ${config.Server}`,
77
+ ServerVersion: version.split('\n')[0],
78
+ };
79
+ }
80
+ catch (err) {
81
+ const message = err instanceof Error ? err.message : String(err);
82
+ return { Success: false, Message: `Connection failed: ${message}` };
83
+ }
84
+ }
85
+ /**
86
+ * Discovers all user tables in the source database via INFORMATION_SCHEMA.
87
+ * @param companyIntegration - The company integration with connection configuration
88
+ * @param _contextUser - User context for authorization
89
+ * @returns Array of object schemas for each table found
90
+ */
91
+ async DiscoverObjects(companyIntegration, _contextUser) {
92
+ const config = this.ParseConnectionConfig(companyIntegration);
93
+ const pool = await this.GetPool(config);
94
+ const result = await pool.request()
95
+ .input('schemaName', sql.NVarChar, config.Schema)
96
+ .query(`SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = @schemaName ORDER BY TABLE_NAME`);
97
+ return result.recordset.map((row) => ({
98
+ Name: row.TABLE_NAME,
99
+ Label: row.TABLE_NAME,
100
+ SupportsIncrementalSync: true,
101
+ SupportsWrite: false,
102
+ }));
103
+ }
104
+ /**
105
+ * Discovers columns on a specific table via INFORMATION_SCHEMA.COLUMNS.
106
+ * @param companyIntegration - The company integration with connection configuration
107
+ * @param objectName - Table name to inspect
108
+ * @param _contextUser - User context for authorization
109
+ * @returns Array of field schemas for the specified table
110
+ */
111
+ async DiscoverFields(companyIntegration, objectName, _contextUser) {
112
+ const config = this.ParseConnectionConfig(companyIntegration);
113
+ const pool = await this.GetPool(config);
114
+ const result = await pool
115
+ .request()
116
+ .input('tableName', sql.NVarChar, objectName)
117
+ .input('schemaName', sql.NVarChar, config.Schema)
118
+ .query(`SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'IsIdentity') AS IS_IDENTITY
119
+ FROM INFORMATION_SCHEMA.COLUMNS
120
+ WHERE TABLE_NAME = @tableName AND TABLE_SCHEMA = @schemaName
121
+ ORDER BY ORDINAL_POSITION`);
122
+ return result.recordset.map((row) => this.mapColumnToFieldSchema(row));
123
+ }
124
+ /**
125
+ * Converts a row from INFORMATION_SCHEMA.COLUMNS to an ExternalFieldSchema.
126
+ * @param row - Column metadata row
127
+ * @returns Mapped field schema
128
+ */
129
+ mapColumnToFieldSchema(row) {
130
+ return {
131
+ Name: row.COLUMN_NAME,
132
+ Label: row.COLUMN_NAME,
133
+ DataType: row.DATA_TYPE,
134
+ IsRequired: row.IS_NULLABLE === 'NO',
135
+ IsUniqueKey: false,
136
+ IsReadOnly: row.IS_IDENTITY === 1,
137
+ };
138
+ }
139
+ /**
140
+ * Builds an ExternalRecord from a SQL result row.
141
+ * @param row - Raw database row
142
+ * @param idField - Column name to use as ExternalID
143
+ * @param objectType - Object type name for the record
144
+ * @param modifiedAtField - Column name for the modification timestamp
145
+ * @param deletedField - Column name for the deleted flag (optional)
146
+ * @returns An ExternalRecord
147
+ */
148
+ BuildExternalRecord(row, idField, objectType, modifiedAtField, deletedField) {
149
+ const modifiedRaw = row[modifiedAtField];
150
+ const modifiedAt = modifiedRaw instanceof Date ? modifiedRaw : undefined;
151
+ const isDeleted = deletedField ? Boolean(row[deletedField]) : undefined;
152
+ return {
153
+ ExternalID: String(row[idField]),
154
+ ObjectType: objectType,
155
+ Fields: { ...row },
156
+ ModifiedAt: modifiedAt,
157
+ IsDeleted: isDeleted,
158
+ };
159
+ }
160
+ /**
161
+ * Fetches changed records from a table, ordered by a modification timestamp column.
162
+ * Applies watermark filtering and batch size limiting.
163
+ * @param ctx - Fetch context with integration, object, watermark, and batch details
164
+ * @param idField - Column name for the primary key
165
+ * @param modifiedAtField - Column name for the modification timestamp
166
+ * @param deletedField - Column name for the deleted flag (optional)
167
+ * @returns Batch of external records with pagination info
168
+ */
169
+ async FetchChangesFromTable(ctx, idField, modifiedAtField, deletedField) {
170
+ const config = this.ParseConnectionConfig(ctx.CompanyIntegration);
171
+ const pool = await this.GetPool(config);
172
+ const request = pool.request();
173
+ let whereClause = '';
174
+ if (ctx.WatermarkValue) {
175
+ whereClause = `WHERE [${modifiedAtField}] > CAST(@watermark AS datetimeoffset(7))`;
176
+ request.input('watermark', sql.NVarChar, ctx.WatermarkValue);
177
+ }
178
+ // Fetch one extra to detect HasMore
179
+ const fetchCount = ctx.BatchSize + 1;
180
+ request.input('fetchCount', sql.Int, fetchCount);
181
+ // Include full-precision timestamp (datetimeoffset has 7 decimal places, JS Date only has 3)
182
+ // to avoid sub-millisecond precision loss in watermark comparisons
183
+ const rawTsCol = `CONVERT(varchar(50), [${modifiedAtField}], 126) AS [__mj_raw_ts]`;
184
+ const query = `SELECT TOP (@fetchCount) *, ${rawTsCol} FROM [${config.Schema}].[${ctx.ObjectName}] ${whereClause} ORDER BY [${modifiedAtField}], [${idField}]`;
185
+ const result = await request.query(query);
186
+ const hasMore = result.recordset.length > ctx.BatchSize;
187
+ const rows = hasMore ? result.recordset.slice(0, ctx.BatchSize) : result.recordset;
188
+ const newWatermark = this.extractRawWatermark(rows);
189
+ // Remove internal column before building records
190
+ for (const row of rows) {
191
+ delete row['__mj_raw_ts'];
192
+ }
193
+ const records = rows.map((row) => this.BuildExternalRecord(row, idField, ctx.ObjectName, modifiedAtField, deletedField));
194
+ return { Records: records, HasMore: hasMore, NewWatermarkValue: newWatermark };
195
+ }
196
+ /**
197
+ * Extracts the full-precision watermark string from the last row's __mj_raw_ts column.
198
+ * Uses CONVERT(varchar, ..., 126) output which preserves datetimeoffset(7) precision,
199
+ * avoiding the sub-millisecond truncation that JavaScript Date introduces.
200
+ * @param rows - Array of database rows containing __mj_raw_ts
201
+ * @returns Full-precision datetimeoffset string, or undefined
202
+ */
203
+ extractRawWatermark(rows) {
204
+ if (rows.length === 0)
205
+ return undefined;
206
+ const rawTs = rows[rows.length - 1]['__mj_raw_ts'];
207
+ return rawTs != null ? String(rawTs) : undefined;
208
+ }
209
+ /**
210
+ * Closes all cached connection pools. Call during cleanup/shutdown.
211
+ */
212
+ async CloseAllPools() {
213
+ for (const pool of this.poolCache.values()) {
214
+ if (pool.connected) {
215
+ await pool.close();
216
+ }
217
+ }
218
+ this.poolCache.clear();
219
+ }
220
+ }
221
+ //# sourceMappingURL=RelationalDBConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationalDBConnector.js","sourceRoot":"","sources":["../src/RelationalDBConnector.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,wBAAwB,GAO3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,GAAG,MAAM,OAAO,CAAC;AAgBxB;;;;GAIG;AACH,MAAM,OAAgB,qBAAsB,SAAQ,wBAAwB;IAA5E;;QACI,gEAAgE;QACxD,cAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;IAyQ9D,CAAC;IAvQG;;;;;OAKG;IACO,qBAAqB,CAAC,kBAA8C;QAC1E,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAkB,CAAC;QAC5E,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAA2B,CAAC;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;QAEzC,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CACX,6EAA6E,CAChF,CAAC;QACN,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAClG,CAAC;IAED;;;;;OAKG;IACO,KAAK,CAAC,OAAO,CAAC,MAAwB;QAC5C,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,SAAS,EAAE,CAAC;YACtB,OAAO,QAAQ,CAAC;QACpB,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC;YAChC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,OAAO,EAAE;gBACL,OAAO,EAAE,KAAK;gBACd,sBAAsB,EAAE,IAAI;aAC/B;SACJ,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,cAAc,CACvB,kBAA8C,EAC9C,YAAsB;QAEtB,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CACrC,6BAA6B,CAChC,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,SAAS,CAAC;YAE1D,OAAO;gBACH,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,gBAAgB,MAAM,CAAC,QAAQ,OAAO,MAAM,CAAC,MAAM,EAAE;gBAC9D,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACxC,CAAC;QACN,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,sBAAsB,OAAO,EAAE,EAAE,CAAC;QACxE,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,eAAe,CACxB,kBAA8C,EAC9C,YAAsB;QAEtB,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;aAC9B,KAAK,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;aAChD,KAAK,CACF,qIAAqI,CACxI,CAAC;QAEN,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,GAAG,CAAC,UAAU;YACpB,KAAK,EAAE,GAAG,CAAC,UAAU;YACrB,uBAAuB,EAAE,IAAI;YAC7B,aAAa,EAAE,KAAK;SACvB,CAAC,CAAC,CAAC;IACR,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,cAAc,CACvB,kBAA8C,EAC9C,UAAkB,EAClB,YAAsB;QAEtB,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,IAAI;aACpB,OAAO,EAAE;aACT,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC;aAC5C,KAAK,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;aAChD,KAAK,CACF;;;2CAG2B,CAC9B,CAAC;QAEN,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACK,sBAAsB,CAAC,GAAoB;QAC/C,OAAO;YACH,IAAI,EAAE,GAAG,CAAC,WAAW;YACrB,KAAK,EAAE,GAAG,CAAC,WAAW;YACtB,QAAQ,EAAE,GAAG,CAAC,SAAS;YACvB,UAAU,EAAE,GAAG,CAAC,WAAW,KAAK,IAAI;YACpC,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,GAAG,CAAC,WAAW,KAAK,CAAC;SACpC,CAAC;IACN,CAAC;IAED;;;;;;;;OAQG;IACO,mBAAmB,CACzB,GAA4B,EAC5B,OAAe,EACf,UAAkB,EAClB,eAAuB,EACvB,YAAqB;QAErB,MAAM,WAAW,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,WAAW,YAAY,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAExE,OAAO;YACH,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChC,UAAU,EAAE,UAAU;YACtB,MAAM,EAAE,EAAE,GAAG,GAAG,EAAE;YAClB,UAAU,EAAE,UAAU;YACtB,SAAS,EAAE,SAAS;SACvB,CAAC;IACN,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,qBAAqB,CACjC,GAAiB,EACjB,OAAe,EACf,eAAuB,EACvB,YAAqB;QAErB,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,WAAW,GAAG,EAAE,CAAC;QACrB,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACrB,WAAW,GAAG,UAAU,eAAe,2CAA2C,CAAC;YACnF,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;QACjE,CAAC;QAED,oCAAoC;QACpC,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC;QACrC,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAEjD,6FAA6F;QAC7F,mEAAmE;QACnE,MAAM,QAAQ,GAAG,yBAAyB,eAAe,0BAA0B,CAAC;QACpF,MAAM,KAAK,GAAG,+BAA+B,QAAQ,UAAU,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,UAAU,KAAK,WAAW,cAAc,eAAe,OAAO,OAAO,GAAG,CAAC;QAC/J,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAA0B,KAAK,CAAC,CAAC;QAEnE,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC;QACxD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAEnF,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAEpD,iDAAiD;QACjD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC7B,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE,eAAe,EAAE,YAAY,CAAC,CACxF,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;IACnF,CAAC;IAED;;;;;;OAMG;IACK,mBAAmB,CAAC,IAA+B;QACvD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;QACnD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa;QACtB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;QACL,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACJ"}
@@ -0,0 +1,28 @@
1
+ import { type FetchContext, type FetchBatchResult, type DefaultFieldMapping } from '@memberjunction/integration-engine';
2
+ import { RelationalDBConnector } from './RelationalDBConnector.js';
3
+ /**
4
+ * Connector for Salesforce CRM data, backed by the mock_data database (sf schema).
5
+ * Reads from sf.Contact, sf.Account, and sf.Opportunity tables.
6
+ * Provides default field mappings for Contacts and Accounts to MJ entities.
7
+ */
8
+ export declare class SalesforceConnector extends RelationalDBConnector {
9
+ /**
10
+ * Fetches a batch of changed records from the specified Salesforce object table.
11
+ * Uses LastModifiedDate for watermark-based incremental sync.
12
+ * @param ctx - Fetch context with integration, object, watermark, and batch details
13
+ * @returns Batch of Salesforce records with pagination info
14
+ */
15
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
16
+ /**
17
+ * Returns suggested default field mappings for known Salesforce objects to MJ entities.
18
+ * @param objectName - Salesforce object name (e.g., "Contact")
19
+ * @param _entityName - Target MJ entity name (e.g., "Contacts")
20
+ * @returns Array of default field mappings
21
+ */
22
+ GetDefaultFieldMappings(objectName: string, _entityName: string): DefaultFieldMapping[];
23
+ /** Default field mappings: sf_Contact -> Contacts */
24
+ private getContactMappings;
25
+ /** Default field mappings: sf_Account -> Companies */
26
+ private getAccountMappings;
27
+ }
28
+ //# sourceMappingURL=SalesforceConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SalesforceConnector.d.ts","sourceRoot":"","sources":["../src/SalesforceConnector.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EAC3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AASnE;;;;GAIG;AACH,qBACa,mBAAoB,SAAQ,qBAAqB;IAC1D;;;;;OAKG;IACU,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIvE;;;;;OAKG;IACa,uBAAuB,CACnC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GACpB,mBAAmB,EAAE;IAWxB,qDAAqD;IACrD,OAAO,CAAC,kBAAkB;IAW1B,sDAAsD;IACtD,OAAO,CAAC,kBAAkB;CAS7B"}
@@ -0,0 +1,73 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { BaseIntegrationConnector, } from '@memberjunction/integration-engine';
9
+ import { RelationalDBConnector } from './RelationalDBConnector.js';
10
+ /** Primary key column used across all Salesforce mock tables */
11
+ const SF_ID_FIELD = 'Id';
12
+ /** Modification timestamp column used across Salesforce mock tables */
13
+ const SF_MODIFIED_FIELD = 'LastModifiedDate';
14
+ /** Soft-delete flag column used across all Salesforce mock tables */
15
+ const SF_DELETED_FIELD = 'IsDeleted';
16
+ /**
17
+ * Connector for Salesforce CRM data, backed by the mock_data database (sf schema).
18
+ * Reads from sf.Contact, sf.Account, and sf.Opportunity tables.
19
+ * Provides default field mappings for Contacts and Accounts to MJ entities.
20
+ */
21
+ let SalesforceConnector = class SalesforceConnector extends RelationalDBConnector {
22
+ /**
23
+ * Fetches a batch of changed records from the specified Salesforce object table.
24
+ * Uses LastModifiedDate for watermark-based incremental sync.
25
+ * @param ctx - Fetch context with integration, object, watermark, and batch details
26
+ * @returns Batch of Salesforce records with pagination info
27
+ */
28
+ async FetchChanges(ctx) {
29
+ return this.FetchChangesFromTable(ctx, SF_ID_FIELD, SF_MODIFIED_FIELD, SF_DELETED_FIELD);
30
+ }
31
+ /**
32
+ * Returns suggested default field mappings for known Salesforce objects to MJ entities.
33
+ * @param objectName - Salesforce object name (e.g., "Contact")
34
+ * @param _entityName - Target MJ entity name (e.g., "Contacts")
35
+ * @returns Array of default field mappings
36
+ */
37
+ GetDefaultFieldMappings(objectName, _entityName) {
38
+ switch (objectName) {
39
+ case 'Contact':
40
+ return this.getContactMappings();
41
+ case 'Account':
42
+ return this.getAccountMappings();
43
+ default:
44
+ return [];
45
+ }
46
+ }
47
+ /** Default field mappings: sf_Contact -> Contacts */
48
+ getContactMappings() {
49
+ return [
50
+ { SourceFieldName: 'Email', DestinationFieldName: 'Email', IsKeyField: true },
51
+ { SourceFieldName: 'FirstName', DestinationFieldName: 'FirstName' },
52
+ { SourceFieldName: 'LastName', DestinationFieldName: 'LastName' },
53
+ { SourceFieldName: 'Phone', DestinationFieldName: 'Phone' },
54
+ { SourceFieldName: 'Title', DestinationFieldName: 'Title' },
55
+ { SourceFieldName: 'Department', DestinationFieldName: 'Department' },
56
+ ];
57
+ }
58
+ /** Default field mappings: sf_Account -> Companies */
59
+ getAccountMappings() {
60
+ return [
61
+ { SourceFieldName: 'Name', DestinationFieldName: 'Name', IsKeyField: true },
62
+ { SourceFieldName: 'Industry', DestinationFieldName: 'Industry' },
63
+ { SourceFieldName: 'BillingCity', DestinationFieldName: 'City' },
64
+ { SourceFieldName: 'BillingState', DestinationFieldName: 'State' },
65
+ { SourceFieldName: 'Phone', DestinationFieldName: 'Phone' },
66
+ ];
67
+ }
68
+ };
69
+ SalesforceConnector = __decorate([
70
+ RegisterClass(BaseIntegrationConnector, 'SalesforceConnector')
71
+ ], SalesforceConnector);
72
+ export { SalesforceConnector };
73
+ //# sourceMappingURL=SalesforceConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SalesforceConnector.js","sourceRoot":"","sources":["../src/SalesforceConnector.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACH,wBAAwB,GAI3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,gEAAgE;AAChE,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,uEAAuE;AACvE,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAC7C,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAErC;;;;GAIG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,qBAAqB;IAC1D;;;;;OAKG;IACI,KAAK,CAAC,YAAY,CAAC,GAAiB;QACvC,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IAC7F,CAAC;IAED;;;;;OAKG;IACa,uBAAuB,CACnC,UAAkB,EAClB,WAAmB;QAEnB,QAAQ,UAAU,EAAE,CAAC;YACjB,KAAK,SAAS;gBACV,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACrC,KAAK,SAAS;gBACV,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACrC;gBACI,OAAO,EAAE,CAAC;QAClB,CAAC;IACL,CAAC;IAED,qDAAqD;IAC7C,kBAAkB;QACtB,OAAO;YACH,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE;YAC7E,EAAE,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE;YACnE,EAAE,eAAe,EAAE,UAAU,EAAE,oBAAoB,EAAE,UAAU,EAAE;YACjE,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE;YAC3D,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE;YAC3D,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE;SACxE,CAAC;IACN,CAAC;IAED,sDAAsD;IAC9C,kBAAkB;QACtB,OAAO;YACH,EAAE,eAAe,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE;YAC3E,EAAE,eAAe,EAAE,UAAU,EAAE,oBAAoB,EAAE,UAAU,EAAE;YACjE,EAAE,eAAe,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE;YAChE,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB,EAAE,OAAO,EAAE;YAClE,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE;SAC9D,CAAC;IACN,CAAC;CACJ,CAAA;AArDY,mBAAmB;IAD/B,aAAa,CAAC,wBAAwB,EAAE,qBAAqB,CAAC;GAClD,mBAAmB,CAqD/B"}
@@ -0,0 +1,143 @@
1
+ import { type UserInfo } from '@memberjunction/core';
2
+ import type { MJCompanyIntegrationEntity } from '@memberjunction/core-entities';
3
+ import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type FetchContext, type FetchBatchResult, type DefaultFieldMapping, type DefaultIntegrationConfig } from '@memberjunction/integration-engine';
4
+ import type { MJIntegrationObjectEntity } from '@memberjunction/core-entities';
5
+ /** Connection configuration parsed from CompanyIntegration.Configuration JSON */
6
+ export interface YMConnectionConfig {
7
+ /** YM client/site identifier (integer) */
8
+ ClientID: string;
9
+ /** YM API key (used as UserName for session auth) */
10
+ APIKey: string;
11
+ /** YM API password (used as Password for session auth) */
12
+ APIPassword: string;
13
+ }
14
+ /**
15
+ * Connector for the YourMembership (YM) AMS REST API.
16
+ *
17
+ * Extends BaseRESTIntegrationConnector to leverage metadata-driven object/field
18
+ * discovery from IntegrationEngineBase cache and generic pagination handling.
19
+ *
20
+ * Auth flow:
21
+ * 1. POST /Ams/Authenticate with credentials + ClientID
22
+ * 2. Receive SessionId
23
+ * 3. Pass SessionId as X-SS-ID header on all data requests
24
+ *
25
+ * Configuration JSON: { "ClientID": "25363", "APIKey": "...", "APIPassword": "..." }
26
+ */
27
+ export declare class YourMembershipConnector extends BaseRESTIntegrationConnector {
28
+ /** Session cache keyed by ClientID */
29
+ private sessionCache;
30
+ /** Timestamp of the last API request, used for throttling */
31
+ private lastRequestTime;
32
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity): Promise<RESTAuthContext>;
33
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
34
+ protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, _body?: unknown): Promise<RESTResponse>;
35
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
36
+ protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, currentPage: number, currentOffset: number, pageSize: number): PaginationState;
37
+ protected GetBaseURL(companyIntegration: MJCompanyIntegrationEntity): string;
38
+ /**
39
+ * Overrides base pagination URL building to use YM's expected parameter names:
40
+ * - PageNumber type: `PageNumber` / `PageSize` (not `page` / `pageSize`)
41
+ * - Offset type: `OffSet` (not `offset`); no explicit limit param — YM uses
42
+ * its own default batch size (~200 records)
43
+ */
44
+ protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, offset: number, cursor?: string): string;
45
+ /**
46
+ * Overrides base FetchChanges to handle YM-specific edge cases:
47
+ * - Groups/GroupTypes: nested GroupTypeList response needs custom flattening
48
+ * - Members: client-side watermark filtering + selective detail enrichment
49
+ *
50
+ * The MemberList API returns all members every time (no server-side date filter),
51
+ * but each record includes a `LastUpdated` field. We pull the full list, filter
52
+ * to only records changed since the watermark, and only call the expensive
53
+ * per-member detail endpoint for those changed records.
54
+ */
55
+ FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
56
+ /**
57
+ * Filters the full member list to only records changed since the watermark,
58
+ * then enriches only those changed records via the detail endpoint.
59
+ * Returns a new watermark based on the latest LastUpdated value seen.
60
+ */
61
+ private FetchMembersWithWatermark;
62
+ /**
63
+ * Filters records by comparing a date field against a watermark timestamp.
64
+ * Returns only records where the date field is newer than the watermark,
65
+ * plus the latest date value seen (to use as the next watermark).
66
+ */
67
+ private FilterByWatermark;
68
+ /** Tests connectivity by authenticating and fetching ClientConfig. */
69
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
70
+ /** Returns suggested default configuration for YM integration setup. */
71
+ GetDefaultConfiguration(): DefaultIntegrationConfig;
72
+ /** Returns suggested default field mappings for common YM objects to MJ entities. */
73
+ GetDefaultFieldMappings(objectName: string, _entityName: string): DefaultFieldMapping[];
74
+ /**
75
+ * Fetches Groups or GroupTypes, which have a nested structure:
76
+ * GroupTypeList → Groups. When objectName is 'GroupTypes', returns just
77
+ * the type entries. When 'Groups', flattens into individual group records.
78
+ */
79
+ private FetchGroups;
80
+ /** Builds ExternalRecords for GroupType entries only. */
81
+ private BuildGroupTypeRecords;
82
+ /** Flattens nested GroupTypeList → Groups into flat group records. */
83
+ private FlattenGroupRecords;
84
+ /**
85
+ * Enriches Members records with full profile data from the detail endpoint.
86
+ * The list endpoint returns sparse data (name, email); the detail endpoint
87
+ * returns full address, phone, custom fields, etc.
88
+ */
89
+ private EnrichMembersWithDetails;
90
+ /**
91
+ * Fetches full detail data for a single member and merges it.
92
+ * Detail endpoint returns camelCase + nested objects; we normalize to flat PascalCase.
93
+ */
94
+ private EnrichSingleMember;
95
+ /**
96
+ * Maps the Members detail endpoint response to our flat PascalCase schema.
97
+ * Detail endpoint returns: { firstName, lastName, emailAddress, primaryAddress: { ... } }
98
+ * We need: { FirstName, LastName, EmailAddr, Phone, Address1, ... }
99
+ */
100
+ private NormalizeMemberDetail;
101
+ /**
102
+ * Parses credentials from CompanyIntegration.CredentialID (preferred) or
103
+ * falls back to CompanyIntegration.Configuration JSON for backwards compat.
104
+ */
105
+ protected ParseConfig(companyIntegration: MJCompanyIntegrationEntity, contextUser?: UserInfo): Promise<YMConnectionConfig>;
106
+ private LoadFromCredential;
107
+ private ParseConfigJson;
108
+ /** Obtains or reuses a YM session for the given credentials. */
109
+ protected GetSession(config: YMConnectionConfig): Promise<string>;
110
+ /** Creates a new YM session via the Authenticate endpoint. */
111
+ private CreateSession;
112
+ /** Invalidates a cached session so the next request re-authenticates. */
113
+ protected InvalidateSession(clientId: string): void;
114
+ /**
115
+ * Makes a direct YM API request using the connector's auth and URL conventions.
116
+ * Used for YM-specific calls (Groups, detail enrichment, TestConnection)
117
+ * that bypass the base class's metadata-driven flow.
118
+ */
119
+ private MakeYMRequest;
120
+ /** Executes an HTTP request with a timeout. */
121
+ private FetchWithTimeout;
122
+ /** Wraps response.json() with a timeout to prevent indefinite hangs. */
123
+ private JsonWithTimeout;
124
+ /** Refreshes session after 401 or timeout, updating headers in-place. */
125
+ private RefreshSession;
126
+ /** Checks if an error is a timeout/abort error. */
127
+ private IsTimeoutError;
128
+ /** Calculates retry delay from Retry-After header or exponential backoff. */
129
+ private CalculateRetryDelay;
130
+ /** Converts a fetch Response + parsed body into a RESTResponse. */
131
+ private BuildRESTResponse;
132
+ private Sleep;
133
+ /** Checks for YM API-level errors in the response envelope. */
134
+ private CheckResponseError;
135
+ /** Filters out YM API metadata keys that shouldn't be stored as field data. */
136
+ private FilterMetadataKeys;
137
+ /**
138
+ * Estimates record count from a raw response body.
139
+ * Used by ExtractPaginationInfo to determine if more pages exist.
140
+ */
141
+ private EstimateRecordCount;
142
+ }
143
+ //# sourceMappingURL=YourMembershipConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"YourMembershipConnector.d.ts","sourceRoot":"","sources":["../src/YourMembershipConnector.ts"],"names":[],"mappings":"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"}