@whaly/connector-sdk 0.1.0 → 0.2.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.
package/dist/index.js CHANGED
@@ -34,34 +34,32 @@ __export(index_exports, {
34
34
  API_CALLS_METRIC_NAME: () => API_CALLS_METRIC_NAME,
35
35
  Authenticator: () => Authenticator,
36
36
  BATCH_INTERVAL_MS: () => BATCH_INTERVAL_MS,
37
- Catalog: () => Catalog,
37
+ BigQueryTarget: () => BigQueryTarget,
38
38
  CounterMetric: () => CounterMetric,
39
39
  DEFAULT_MAX_CONCURRENT_STREAMS: () => DEFAULT_MAX_CONCURRENT_STREAMS,
40
40
  EXECUTION_TIME_METRIC_NAME: () => EXECUTION_TIME_METRIC_NAME,
41
+ GCSStateProvider: () => GCSStateProvider,
41
42
  ITarget: () => ITarget,
42
43
  MissingFieldInSchemaError: () => MissingFieldInSchemaError,
43
44
  MissingSchemaError: () => MissingSchemaError,
44
- RESTStreamV2: () => RESTStreamV2,
45
+ RESTStream: () => RESTStream,
45
46
  ROWS_SYNCED_METRIC_NAME: () => ROWS_SYNCED_METRIC_NAME,
46
47
  RecordMessage: () => RecordMessage,
47
48
  RenameColumnStore: () => RenameColumnStore,
49
+ ReplicationMethod: () => ReplicationMethod,
48
50
  ReplicationMethodMessage: () => ReplicationMethodMessage,
49
- Resolver: () => Resolver,
50
51
  SchemaMessage: () => SchemaMessage,
51
52
  SchemaValidationError: () => SchemaValidationError,
52
53
  StateMessage: () => StateMessage,
53
54
  StateService: () => StateService,
54
- StreamMetadata: () => StreamMetadata,
55
- StreamV2: () => StreamV2,
55
+ Stream: () => Stream,
56
56
  StreamWarehouseSyncService: () => StreamWarehouseSyncService,
57
57
  Tap: () => Tap,
58
58
  _greaterThan: () => _greaterThan,
59
59
  addWhalyFields: () => addWhalyFields,
60
- checkRequiredConfigKeys: () => checkRequiredConfigKeys,
61
60
  createTemporaryFileStream: () => createTemporaryFileStream,
62
61
  extractStateForStream: () => extractStateForStream,
63
62
  finalizeStateProgressMarkers: () => finalizeStateProgressMarkers,
64
- findRelatedRelationships: () => findRelatedRelationships,
65
63
  flattenSchema: () => flattenSchema,
66
64
  getAllMetrics: () => getAllMetrics,
67
65
  getAxiosInstance: () => getAxiosInstance,
@@ -76,260 +74,24 @@ __export(index_exports, {
76
74
  incrementStreamState: () => incrementStreamState,
77
75
  loadJson: () => loadJson,
78
76
  loadSchema: () => loadSchema,
79
- loadShoreConfig: () => loadShoreConfig,
80
77
  logger: () => logger,
81
78
  postFormDataApiCall: () => postFormDataApiCall,
82
79
  postJSONApiCall: () => postJSONApiCall,
83
80
  postUrlEncodedApiCall: () => postUrlEncodedApiCall,
84
81
  printMemoryFootprint: () => printMemoryFootprint,
85
82
  removeParasiteProperties: () => removeParasiteProperties,
86
- safePath: () => safePath,
87
- writeCatalogFile: () => writeCatalogFile,
88
- writeStateFile: () => writeStateFile
83
+ safePath: () => safePath
89
84
  });
90
85
  module.exports = __toCommonJS(index_exports);
91
86
 
92
87
  // src/sdk/utils.ts
93
- var import_fs2 = require("fs");
94
-
95
- // src/sdk/service/logger.ts
96
- var winston2 = __toESM(require("winston"));
97
-
98
- // src/sdk/models/resolver.ts
99
- var Resolver = class {
100
- constructor() {
101
- }
102
- };
103
-
104
- // src/resolvers/local-file/services/files.ts
105
- var import_fs = __toESM(require("fs"));
106
- var writeFile = (path, content) => {
107
- return Promise.resolve(import_fs.default.writeFileSync(path, content));
108
- };
109
- var readAndParseJSONFile = (path) => {
110
- if (!import_fs.default.existsSync(path)) {
111
- throw new Error(`File \`${path}\` wasn't found at: ${process.cwd()}`);
112
- }
113
- const raw = import_fs.default.readFileSync(path);
114
- const parsed = JSON.parse(raw.toString());
115
- return parsed;
116
- };
117
-
118
- // src/resolvers/local-file/hook/schemaHook.ts
119
- var import_fs_extra = require("fs-extra");
120
- var LocalSchemaHook = class {
121
- async writeSchema(input) {
122
- await (0, import_fs_extra.appendFile)("./schema.tmp", JSON.stringify(input) + "\n");
123
- }
124
- };
125
-
126
- // src/resolvers/local-file/resolver.ts
127
- var import_winston = __toESM(require("winston"));
128
-
129
- // src/sdk/service/metric.ts
130
- var metricStore = {};
131
- var ALL_STREAM_ID_KEYWORD = "all";
132
- var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
133
- var API_CALLS_METRIC_NAME = "api_calls_count";
134
- var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
135
- var getCounterMetrics = (name, streamIds) => {
136
- return streamIds.map((streamId) => {
137
- return getCounterMetric(name, streamId);
138
- });
139
- };
140
- var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
141
- if (!metricStore[streamId]) {
142
- metricStore[streamId] = {};
143
- }
144
- const streamMetricStore = metricStore[streamId];
145
- if (!streamMetricStore[name]) {
146
- streamMetricStore[name] = new CounterMetric(name, streamId);
147
- }
148
- return streamMetricStore[name];
149
- };
150
- var getAllMetrics = () => {
151
- return Object.keys(metricStore).flatMap((streamId) => {
152
- const streamMetricStore = metricStore[streamId];
153
- if (!streamMetricStore) {
154
- return [];
155
- }
156
- return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
157
- });
158
- };
159
- var CounterMetric = class {
160
- value = 0;
161
- name;
162
- streamId;
163
- constructor(name, streamId) {
164
- this.name = name;
165
- this.streamId = streamId;
166
- }
167
- increment(inc = 1) {
168
- this.value = this.value + inc;
169
- }
170
- getStreamId() {
171
- return this.streamId;
172
- }
173
- getName() {
174
- return this.name;
175
- }
176
- getValue() {
177
- return this.value;
178
- }
179
- };
180
-
181
- // src/resolvers/local-file/resolver.ts
182
- var { combine, prettyPrint, timestamp, errors, splat } = import_winston.default.format;
183
- var configPath = "./config.json";
184
- var targetConfigPath = "./target-config.json";
185
- var catalogPath = "./catalog.json";
186
- var statePath = "./state.json";
187
- var metricsPath = "./metrics.json";
188
- var LocalFilesResolver = class extends Resolver {
189
- startVPNIfNeeded() {
190
- return Promise.resolve();
191
- }
192
- checkIfCanSync() {
193
- return Promise.resolve(true);
194
- }
195
- markSyncStarted() {
196
- return Promise.resolve();
197
- }
198
- onError(errorType, errorText, errorDebugText) {
199
- const path = `./error.json`;
200
- return writeFile(path, JSON.stringify({
201
- errorType,
202
- errorText,
203
- errorDebugText
204
- }));
205
- }
206
- getSchemaHooks() {
207
- const wlyHook = new LocalSchemaHook();
208
- return [wlyHook];
209
- }
210
- writeSchema() {
211
- return Promise.resolve();
212
- }
213
- constructor() {
214
- super();
215
- }
216
- getConfig(command) {
217
- logger.info(`\u{1F4C1} Using the LOCAL resolver`);
218
- const config = readAndParseJSONFile(configPath);
219
- const targetConfig = readAndParseJSONFile(targetConfigPath);
220
- const destination = process.env.SHORE__DESTINATION;
221
- if (!destination) {
222
- throw new Error(`\u274C Env variable \`SHORE__DESTINATION\` is not set.
223
- Did you forget to configure it?
224
-
225
- Possible values are: \`GOOGLE_BIGQUERY\`, \`SNOWFLAKE\``);
226
- }
227
- if (command === `READ`) {
228
- if (destination === `GOOGLE_BIGQUERY`) {
229
- const googleCredentials = process.env.GOOGLE_APPLICATION_CREDENTIALS;
230
- if (!googleCredentials) {
231
- throw new Error(`Env variable \`GOOGLE_APPLICATION_CREDENTIALS\` is not set.
232
- Did you forget to configure it?
233
-
234
- This variable should link to a file containing the proper Google Service Account credentials.
235
- `);
236
- }
237
- }
238
- const catalog = readAndParseJSONFile(catalogPath);
239
- const state = readAndParseJSONFile(statePath);
240
- return Promise.resolve({
241
- command,
242
- destination,
243
- config,
244
- targetConfig,
245
- catalog,
246
- state
247
- });
248
- } else {
249
- return Promise.resolve({
250
- command,
251
- destination,
252
- config,
253
- targetConfig
254
- });
255
- }
256
- }
257
- writeCatalog(catalog) {
258
- return writeFile(catalogPath, JSON.stringify(catalog));
259
- }
260
- writeState(state) {
261
- return writeFile(statePath, state);
262
- }
263
- markSyncFailed() {
264
- logger.info(`\u{1F622} Sync has failed.`);
265
- return Promise.resolve();
266
- }
267
- markSyncComplete() {
268
- logger.info(`\u{1F389} Sync is complete.`);
269
- return Promise.resolve();
270
- }
271
- markDiscoveryComplete() {
272
- logger.info(`\u{1F389} Discovery is complete.`);
273
- return Promise.resolve();
274
- }
275
- async flushMetrics() {
276
- const allMetrics = getAllMetrics();
277
- const metricsOuput = allMetrics.map((metric) => {
278
- return {
279
- streamId: metric.getStreamId(),
280
- name: metric.getName(),
281
- value: metric.getValue()
282
- };
283
- }).map((obj) => JSON.stringify(obj)).join(`
284
- `);
285
- return writeFile(metricsPath, metricsOuput);
286
- }
287
- updateSourceValue(optionKey, optionValue) {
288
- logger.info(`Updating source value ${optionKey} with value ${optionValue}`, { private: true });
289
- const config = readAndParseJSONFile(configPath);
290
- config[optionKey] = optionValue;
291
- return writeFile(configPath, JSON.stringify(config));
292
- }
293
- getLogFormat() {
294
- return combine(
295
- timestamp(),
296
- errors({ stack: true }),
297
- splat(),
298
- prettyPrint()
299
- );
300
- }
301
- };
302
-
303
- // src/sdk/service/resolver.ts
304
- function initResolver(resolverType) {
305
- if (resolverType === "LOCAL") {
306
- return new LocalFilesResolver();
307
- } else {
308
- throw new Error(`\u274C Unsupported configResolver: ${resolverType}`);
309
- }
310
- }
311
- var loadResolver = () => {
312
- const configResolver = process.env.SHORE__CONFIG_RESOLVER;
313
- if (!configResolver) {
314
- throw new Error(`Env variable \`SHORE__CONFIG_RESOLVER\` is not set.
315
- Did you forget to configure it?
316
-
317
- Possible values are \`LOCAL\`, \`WHALY\`.
318
- Please read the documentation to get the full description of the behavior of each.
319
- `);
320
- }
321
- if (configResolver === "WHALY" || configResolver === "LOCAL") {
322
- return initResolver(configResolver);
323
- } else {
324
- throw new Error(`Resolver: ${configResolver} is not supported.
325
- Did you properly configure \`SHORE__CONFIG_RESOLVER\`?`);
326
- }
327
- };
88
+ var import_fs = require("fs");
328
89
 
329
90
  // src/sdk/service/logger.ts
91
+ var winston = __toESM(require("winston"));
330
92
  var BATCH_INTERVAL_MS = 100;
331
93
  var transports2 = {
332
- console: new winston2.transports.Console({
94
+ console: new winston.transports.Console({
333
95
  stderrLevels: [],
334
96
  level: "info"
335
97
  })
@@ -339,8 +101,14 @@ var getTransports = () => {
339
101
  transports2.console
340
102
  ];
341
103
  };
342
- var winstonLogger = winston2.createLogger({
343
- format: loadResolver().getLogFormat(),
104
+ var { combine, prettyPrint, timestamp, errors, splat } = winston.format;
105
+ var winstonLogger = winston.createLogger({
106
+ format: combine(
107
+ timestamp(),
108
+ errors({ stack: true }),
109
+ splat(),
110
+ prettyPrint()
111
+ ),
344
112
  transports: getTransports()
345
113
  });
346
114
  var isWinstonOpen = true;
@@ -369,37 +137,9 @@ var logger = {
369
137
  };
370
138
 
371
139
  // src/sdk/utils.ts
372
- var writeStateFile = (resolver, state) => {
373
- logger.info(`\u{1F4DD} Writing the state file.`);
374
- return resolver.writeState(state);
375
- };
376
- var writeCatalogFile = (resolver, catalog) => {
377
- logger.info(`\u{1F4DD} Writing the catalog`);
378
- return resolver.writeCatalog(catalog);
379
- };
380
- var loadShoreConfig = (resolver) => {
381
- const command = process.env.SHORE__COMMAND;
382
- if (!command) {
383
- throw new Error(`Env variable \`SHORE__COMMAND\` is not set.
384
- Did you forget to configure it?
385
-
386
- Possible values are: \`DISCOVER\`, \`READ\``);
387
- }
388
- if (command === `DISCOVER`) {
389
- logger.info(`\u{1F50E} Mode: discovery
390
- Shore will detect the available streams in the source and their configuration.`);
391
- } else if (command === `READ`) {
392
- logger.info(`\u{1F501} Mode: Synchronizing data
393
- Shore will extract and push data from the source to the destination.`);
394
- } else {
395
- throw new Error(`Command: \`${command}\` is not supported.
396
- Did you properly configured env variable \`SHORE__COMMAND\`?`);
397
- }
398
- return resolver.getConfig(command);
399
- };
400
140
  function readFile(fileName, filePath) {
401
141
  try {
402
- return (0, import_fs2.readFileSync)(filePath);
142
+ return (0, import_fs.readFileSync)(filePath);
403
143
  } catch (err) {
404
144
  logger.error(`Can't open file: \`${fileName}\` at path: \`${filePath}\`. Is it at the proper place?`);
405
145
  throw err;
@@ -422,18 +162,11 @@ var loadSchema = (streamId) => {
422
162
  const schema = loadJson(`schemas/${streamId}.json`);
423
163
  return schema;
424
164
  };
425
- var checkRequiredConfigKeys = (args, requiredConfigKeys) => {
426
- requiredConfigKeys.forEach((configKey) => {
427
- if (!args[configKey]) {
428
- throw new Error(`Config is missing required key: ${configKey}`);
429
- }
430
- });
431
- };
432
165
 
433
166
  // src/sdk/service/network.ts
434
167
  var import_axios = __toESM(require("axios"));
435
168
  var import_url = require("url");
436
- var fs2 = __toESM(require("fs"));
169
+ var fs = __toESM(require("fs"));
437
170
  var qs = __toESM(require("qs"));
438
171
  var import_axios_retry = __toESM(require("axios-retry"));
439
172
  var shouldRetry = (error) => {
@@ -546,7 +279,7 @@ var getJSONApiCall = async (endpoint, config, privateLogging) => {
546
279
  };
547
280
  var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) => {
548
281
  const instance = getAxiosInstance(config.retryCount);
549
- const writer = fs2.createWriteStream(output);
282
+ const writer = fs.createWriteStream(output);
550
283
  const paramsSerializer = (params) => {
551
284
  return qs.stringify(params, { arrayFormat: "repeat" });
552
285
  };
@@ -640,6 +373,58 @@ var postUrlEncodedApiCall = async (endpoint, config, payload) => {
640
373
  }
641
374
  };
642
375
 
376
+ // src/sdk/service/metric.ts
377
+ var metricStore = {};
378
+ var ALL_STREAM_ID_KEYWORD = "all";
379
+ var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
380
+ var API_CALLS_METRIC_NAME = "api_calls_count";
381
+ var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
382
+ var getCounterMetrics = (name, streamIds) => {
383
+ return streamIds.map((streamId) => {
384
+ return getCounterMetric(name, streamId);
385
+ });
386
+ };
387
+ var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
388
+ if (!metricStore[streamId]) {
389
+ metricStore[streamId] = {};
390
+ }
391
+ const streamMetricStore = metricStore[streamId];
392
+ if (!streamMetricStore[name]) {
393
+ streamMetricStore[name] = new CounterMetric(name, streamId);
394
+ }
395
+ return streamMetricStore[name];
396
+ };
397
+ var getAllMetrics = () => {
398
+ return Object.keys(metricStore).flatMap((streamId) => {
399
+ const streamMetricStore = metricStore[streamId];
400
+ if (!streamMetricStore) {
401
+ return [];
402
+ }
403
+ return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
404
+ });
405
+ };
406
+ var CounterMetric = class {
407
+ value = 0;
408
+ name;
409
+ streamId;
410
+ constructor(name, streamId) {
411
+ this.name = name;
412
+ this.streamId = streamId;
413
+ }
414
+ increment(inc = 1) {
415
+ this.value = this.value + inc;
416
+ }
417
+ getStreamId() {
418
+ return this.streamId;
419
+ }
420
+ getName() {
421
+ return this.name;
422
+ }
423
+ getValue() {
424
+ return this.value;
425
+ }
426
+ };
427
+
643
428
  // src/sdk/service/memory.ts
644
429
  var formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
645
430
  var printMemoryFootprint = (prefix) => {
@@ -655,9 +440,6 @@ var printMemoryFootprint = (prefix) => {
655
440
 
656
441
  // src/sdk/service/exit.ts
657
442
  async function gracefulExit(exitCode) {
658
- if (exitCode !== 0) {
659
- await loadResolver().markSyncFailed();
660
- }
661
443
  const loggerFinish = new Promise((resolve, reject) => {
662
444
  logger.closeWinston(resolve);
663
445
  });
@@ -677,207 +459,15 @@ var haltAndCatchFire = async (errorType, errorText, errorDebugText) => {
677
459
  errorText,
678
460
  errorDebugText
679
461
  );
680
- const resolver = loadResolver();
681
- await resolver.onError(errorType, errorText, errorDebugText);
682
462
  await gracefulExit(0);
683
463
  };
684
464
 
685
- // src/sdk/service/relationship.ts
686
- var findRelatedRelationships = (streamId, allRels) => {
687
- return allRels.reduce((acc, rel) => {
688
- const base = {
689
- type: rel.type,
690
- from: rel.from,
691
- to: rel.to
692
- };
693
- if (rel.left === streamId) {
694
- acc.right.push({
695
- streamId: rel.right,
696
- ...base
697
- });
698
- }
699
- if (rel.right === streamId) {
700
- acc.left.push({
701
- streamId: rel.left,
702
- ...base
703
- });
704
- }
705
- return acc;
706
- }, { left: [], right: [] });
707
- };
708
-
709
- // src/sdk/models/metadata.ts
710
- var MetadataMap = class extends Map {
711
- areEquals(array1, array2) {
712
- return array1.length === array2.length && array1.every(function(value, index) {
713
- return value === array2[index];
714
- });
715
- }
716
- findExistingEntry(key) {
717
- return Array.from(super.entries()).find((entry) => {
718
- if (this.areEquals(entry[0], key)) {
719
- return true;
720
- }
721
- });
722
- }
723
- get(key) {
724
- const existingEntry = this.findExistingEntry(key);
725
- return existingEntry?.[1];
726
- }
727
- set(key, value) {
728
- const existingKey = this.findExistingEntry(key);
729
- if (existingKey) {
730
- return super.set(existingKey?.[0], value);
731
- } else {
732
- return super.set(key, value);
733
- }
734
- }
735
- };
736
- var StreamMetadata = class {
737
- metadataByBreadcrumb;
738
- constructor() {
739
- this.metadataByBreadcrumb = new MetadataMap();
740
- }
741
- fromCatalogStreamMetadata(metadataArr) {
742
- metadataArr.forEach((metadata) => {
743
- this.metadataByBreadcrumb.set(metadata.breadcrumb, metadata.metadata);
744
- });
745
- return this;
746
- }
747
- toString() {
748
- return this.metadataByBreadcrumb;
749
- }
750
- get(breadcrumb) {
751
- return this.metadataByBreadcrumb.get(breadcrumb);
752
- }
753
- write(breadcrumb, key, value) {
754
- if (value === void 0) {
755
- throw new Error(`The value you're trying to write on the metadata at breadcrumb: \`${breadcrumb}\` and key: \`${key}\` is undefined.
756
-
757
- Writing an undefined value is not a valid operation. Is there something wrong with your value?`);
758
- }
759
- if (!this.metadataByBreadcrumb.get(breadcrumb)) {
760
- this.metadataByBreadcrumb.set(breadcrumb, {});
761
- }
762
- const previousValue = this.metadataByBreadcrumb.get(breadcrumb);
763
- const newValue = {};
764
- newValue[key] = value;
765
- this.metadataByBreadcrumb.set(breadcrumb, { ...previousValue, ...newValue });
766
- return this;
767
- }
768
- getRootBreadcrumbMetadata() {
769
- const rootBreadcrumbMetadata = this.metadataByBreadcrumb.get([]);
770
- if (!rootBreadcrumbMetadata) {
771
- throw new Error(`No metadata was attached to the root breadcrumb \`[\`] while we need one to store metadata at the table level.
772
-
773
- Is your catalog properly generated?`);
774
- }
775
- return rootBreadcrumbMetadata;
776
- }
777
- isStreamSelected() {
778
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
779
- if (rootBreadcrumbMetadata.selected === false) {
780
- return false;
781
- }
782
- if (rootBreadcrumbMetadata.selected === true || rootBreadcrumbMetadata["selected-by-default"] === true) {
783
- return true;
784
- }
785
- return false;
786
- }
787
- getKeyProperties() {
788
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
789
- if (!rootBreadcrumbMetadata["table-key-properties"]) {
790
- return [];
791
- }
792
- return rootBreadcrumbMetadata["table-key-properties"];
793
- }
794
- getReplicationKey() {
795
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
796
- if (!rootBreadcrumbMetadata["replication-key"]) {
797
- return void 0;
798
- }
799
- return rootBreadcrumbMetadata["replication-key"];
800
- }
801
- getReplicationMethod() {
802
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
803
- if (!rootBreadcrumbMetadata["replication-method"]) {
804
- return void 0;
805
- }
806
- return rootBreadcrumbMetadata["replication-method"];
807
- }
808
- getForcedReplicationMethod() {
809
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
810
- if (!rootBreadcrumbMetadata["forced-replication-method"]) {
811
- return void 0;
812
- }
813
- return rootBreadcrumbMetadata["forced-replication-method"];
814
- }
815
- toList() {
816
- return Array.from(this.metadataByBreadcrumb.keys()).map((breadcrumb) => {
817
- const metadata = this.metadataByBreadcrumb.get(breadcrumb);
818
- if (!metadata) {
819
- throw new Error(`There was no metadata entry for breadcrumb: \`${breadcrumb}\` in metadata map: ${this.metadataByBreadcrumb.toString()}`);
820
- }
821
- return {
822
- breadcrumb,
823
- metadata
824
- };
825
- });
826
- }
827
- };
828
-
829
- // src/sdk/models/catalog.ts
830
- var _ = __toESM(require("lodash"));
831
- var Catalog = class _Catalog {
832
- streams;
833
- static fromFile(fileName) {
834
- const catalogFile = loadJson(fileName);
835
- const catalog = new _Catalog();
836
- catalog.streams = catalogFile.streams;
837
- return catalog;
838
- }
839
- static fromCatalogFile(catalogFile) {
840
- const catalog = new _Catalog();
841
- catalog.streams = catalogFile.streams;
842
- return catalog;
843
- }
844
- getSelectedStreams() {
845
- if (!this.streams) {
846
- return [];
847
- }
848
- const directSelectedStreams = this.streams.filter((stream) => {
849
- const metadata = new StreamMetadata();
850
- metadata.fromCatalogStreamMetadata(stream.metadata);
851
- return metadata.isStreamSelected();
852
- });
853
- logger.debug(`Directly selected streams: ${directSelectedStreams.map((stream) => stream.stream).join(",")}`);
854
- const getIndirectSelectedStreams = (stream, ancestorsIds) => {
855
- const currAncestorsIds = ancestorsIds || [];
856
- const rootBreadcrumbMetadata = stream.metadata.find((metadata) => metadata.breadcrumb.length === 0);
857
- const parentStreamId = rootBreadcrumbMetadata?.metadata?.["whaly-parent-stream"];
858
- if (parentStreamId) {
859
- const parentStream = this.streams?.find((stream2) => stream2.stream === parentStreamId);
860
- if (parentStream) {
861
- currAncestorsIds.push(parentStreamId);
862
- return getIndirectSelectedStreams(parentStream, currAncestorsIds);
863
- }
864
- }
865
- return currAncestorsIds;
866
- };
867
- const indirectSelectedStreamIds = _.uniq(
868
- directSelectedStreams.flatMap((stream) => {
869
- return getIndirectSelectedStreams(stream);
870
- })
871
- );
872
- logger.debug(`Indirectly selected streams: ${indirectSelectedStreamIds.join(",")}`);
873
- const indirectSelectedStreams = indirectSelectedStreamIds.map((streamId) => {
874
- if (!directSelectedStreams.find((stream) => stream.stream === streamId)) {
875
- return this.streams?.find((stream) => stream.stream === streamId);
876
- }
877
- }).filter((stream) => !!stream);
878
- return directSelectedStreams.concat(indirectSelectedStreams);
879
- }
880
- };
465
+ // src/sdk/models/replication.ts
466
+ var ReplicationMethod = /* @__PURE__ */ ((ReplicationMethod2) => {
467
+ ReplicationMethod2["INCREMENTAL"] = "INCREMENTAL";
468
+ ReplicationMethod2["FULL_TABLE"] = "FULL_TABLE";
469
+ return ReplicationMethod2;
470
+ })(ReplicationMethod || {});
881
471
 
882
472
  // src/sdk/models/messages.ts
883
473
  var RecordMessage = class {
@@ -937,7 +527,6 @@ var SchemaMessage = class {
937
527
  displayLabel;
938
528
  description;
939
529
  propertiesMetadata;
940
- relationships;
941
530
  constructor(opts) {
942
531
  const {
943
532
  stream,
@@ -946,7 +535,6 @@ var SchemaMessage = class {
946
535
  bookmarkProperties,
947
536
  displayLabel,
948
537
  description,
949
- relationships,
950
538
  propertiesMetadata
951
539
  } = opts;
952
540
  this.stream = stream;
@@ -955,7 +543,6 @@ var SchemaMessage = class {
955
543
  this.bookmarkProperties = bookmarkProperties;
956
544
  this.displayLabel = displayLabel;
957
545
  this.description = description;
958
- this.relationships = relationships;
959
546
  this.propertiesMetadata = propertiesMetadata;
960
547
  }
961
548
  toString() {
@@ -965,8 +552,7 @@ var SchemaMessage = class {
965
552
  schema: this.schema,
966
553
  key_properties: this.keyProperties,
967
554
  label: this.displayLabel,
968
- description: this.description,
969
- relationships: this.relationships
555
+ description: this.description
970
556
  };
971
557
  if (this.bookmarkProperties) {
972
558
  result["bookmark_properties"] = this.bookmarkProperties;
@@ -976,7 +562,7 @@ var SchemaMessage = class {
976
562
  };
977
563
 
978
564
  // src/sdk/models/state.ts
979
- var import_moment = __toESM(require("moment"));
565
+ var import_dayjs = __toESM(require("dayjs"));
980
566
 
981
567
  // src/sdk/constants/date.ts
982
568
  var defaultDateTimeFormat = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ";
@@ -991,12 +577,12 @@ var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
991
577
  if (!newRkValue) {
992
578
  throw new Error((0, import_util.format)(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
993
579
  }
994
- let newRkValueMoment = (0, import_moment.default)(newRkValue);
580
+ let newRkValueMoment = (0, import_dayjs.default)(newRkValue);
995
581
  let prevRkValue;
996
582
  let prevRkValueMoment;
997
583
  if (isSorted) {
998
584
  prevRkValue = state["replicationKeyValue"];
999
- prevRkValueMoment = (0, import_moment.default)(prevRkValue);
585
+ prevRkValueMoment = (0, import_dayjs.default)(prevRkValue);
1000
586
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
1001
587
  throw new Error(
1002
588
  `Unsorted data detected in stream. Latest value '${newRkValue}' is
@@ -1011,24 +597,24 @@ var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
1011
597
  state[PROGRESS_MARKERS] = marker;
1012
598
  }
1013
599
  prevRkValue = state[PROGRESS_MARKERS]["replicationKeyValue"];
1014
- prevRkValueMoment = (0, import_moment.default)(prevRkValue || state["replicationKeyValue"]);
600
+ prevRkValueMoment = (0, import_dayjs.default)(prevRkValue || state["replicationKeyValue"]);
1015
601
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
1016
602
  newRkValueMoment = prevRkValueMoment;
1017
603
  newRkValue = prevRkValue;
1018
604
  }
1019
605
  }
1020
606
  const signpostMarker = state[SIGNPOST_MARKER];
1021
- const replicationKeySignpostMoment = (0, import_moment.default)(signpostMarker);
607
+ const replicationKeySignpostMoment = (0, import_dayjs.default)(signpostMarker);
1022
608
  if (replicationKeySignpostMoment && replicationKeySignpostMoment.isBefore(newRkValueMoment)) {
1023
609
  newRkValueMoment = replicationKeySignpostMoment;
1024
610
  newRkValue = replicationKeySignpostMoment.format(defaultDateTimeFormat);
1025
611
  }
1026
612
  if (isSorted === false) {
1027
613
  state[PROGRESS_MARKERS]["replicationKey"] = replicationKey;
1028
- state[PROGRESS_MARKERS]["replicationKeyValue"] = (0, import_moment.default)(newRkValueMoment).format(defaultDateTimeFormat);
614
+ state[PROGRESS_MARKERS]["replicationKeyValue"] = (0, import_dayjs.default)(newRkValueMoment).format(defaultDateTimeFormat);
1029
615
  } else {
1030
616
  state["replicationKey"] = replicationKey;
1031
- state["replicationKeyValue"] = (0, import_moment.default)(newRkValueMoment).format(defaultDateTimeFormat);
617
+ state["replicationKeyValue"] = (0, import_dayjs.default)(newRkValueMoment).format(defaultDateTimeFormat);
1032
618
  }
1033
619
  return state;
1034
620
  };
@@ -1050,7 +636,7 @@ var finalizeStateProgressMarkers = (state) => {
1050
636
  return state;
1051
637
  };
1052
638
  var _greaterThan = (aValue, bValue) => {
1053
- return (0, import_moment.default)(aValue).isAfter((0, import_moment.default)(bValue));
639
+ return (0, import_dayjs.default)(aValue).isAfter((0, import_dayjs.default)(bValue));
1054
640
  };
1055
641
  var extractStateForStream = (state, streamId) => {
1056
642
  if (!state.bookmarks) {
@@ -1073,26 +659,16 @@ var StateService = class {
1073
659
  }
1074
660
  return this.instance;
1075
661
  }
1076
- // To be deprecated
1077
- setBookmark(streamId, ts) {
1078
- this.bookmarks[streamId] = ts.format(defaultDateTimeFormat);
1079
- }
1080
- setBookmarkV2(streamId, streamState) {
662
+ setBookmark(streamId, streamState) {
1081
663
  this.bookmarks[streamId] = streamState;
1082
664
  }
1083
- setBookmarkSignpostV2(streamId, signpostValue) {
665
+ setBookmarkSignpost(streamId, signpostValue) {
1084
666
  if (!this.bookmarks[streamId]) {
1085
667
  this.bookmarks[streamId] = {};
1086
668
  }
1087
669
  this.bookmarks[streamId][SIGNPOST_MARKER] = signpostValue;
1088
670
  }
1089
671
  getBookmark(streamId) {
1090
- if (!this.bookmarks[streamId]) {
1091
- return (0, import_moment.default)(0);
1092
- }
1093
- return (0, import_moment.default)(this.bookmarks[streamId]);
1094
- }
1095
- getBookmarkV2(streamId) {
1096
672
  if (this.bookmarks[streamId]) {
1097
673
  return this.bookmarks[streamId];
1098
674
  } else {
@@ -1132,7 +708,7 @@ var import_axios2 = __toESM(require("axios"));
1132
708
  var qs2 = __toESM(require("qs"));
1133
709
 
1134
710
  // src/sdk/models/tap/stream.ts
1135
- var import_moment2 = __toESM(require("moment"));
711
+ var import_dayjs2 = __toESM(require("dayjs"));
1136
712
 
1137
713
  // src/sdk/helpers/typing.ts
1138
714
  var isDatetimeType = (typeDef) => {
@@ -1155,186 +731,56 @@ var isDatetimeType = (typeDef) => {
1155
731
  };
1156
732
 
1157
733
  // src/sdk/models/tap/stream.ts
1158
- var _2 = __toESM(require("lodash"));
1159
- var import_bluebird2 = require("bluebird");
734
+ var import_cloneDeep2 = __toESM(require("lodash/cloneDeep.js"));
735
+ var import_bluebird2 = __toESM(require("bluebird"));
1160
736
 
1161
737
  // src/sdk/models/tap/tap.ts
1162
- var import_bluebird = require("bluebird");
1163
- var import_dependency_graph = require("dependency-graph");
738
+ var import_cloneDeep = __toESM(require("lodash/cloneDeep.js"));
739
+ var import_bluebird = __toESM(require("bluebird"));
1164
740
  var DEFAULT_MAX_CONCURRENT_STREAMS = 5;
1165
741
  var Tap = class {
1166
- _target;
1167
- _config;
1168
- _streams;
1169
- _concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
1170
- constructor(target, config) {
1171
- this._streams = [];
1172
- this._target = target;
1173
- this._config = config;
1174
- }
1175
- discover = async (existingCatalog) => {
1176
- logger.debug("Loading schemas");
1177
- const streamDepGraph = new import_dependency_graph.DepGraph();
1178
- const buildDependencyGraph = (streams2, parentId) => {
1179
- streams2.forEach((s) => {
1180
- streamDepGraph.addNode(s.streamId, s);
1181
- if (parentId) {
1182
- streamDepGraph.addDependency(s.streamId, parentId);
1183
- }
1184
- if (s.children) {
1185
- return buildDependencyGraph(s.children, s.streamId);
1186
- }
1187
- });
1188
- };
1189
- buildDependencyGraph(this._streams);
1190
- const discoveredStreamCatalogEntries = await import_bluebird.Promise.map(
1191
- streamDepGraph.overallOrder(),
1192
- async (streamId) => {
1193
- logger.info(`\u2728 Loading schema for ${streamId}`);
1194
- const stream = streamDepGraph.getNodeData(streamId);
1195
- const schema = await stream.getSchema();
1196
- const parentStreamIds = streamDepGraph.directDependenciesOf(streamId);
1197
- if (parentStreamIds.length > 1) {
1198
- throw new Error(`StreamId=${streamId} directly depends on more than 1 stream, e.g. ${parentStreamIds.join(",")} which is not supported!`);
1199
- }
1200
- const parentStreamId = parentStreamIds?.[0];
1201
- const { metadata } = await this.generateMetadataFromStream(stream, parentStreamId);
1202
- return {
1203
- schema,
1204
- metadata,
1205
- stream: stream.streamId,
1206
- tap_stream_id: stream.streamId
1207
- };
1208
- },
1209
- { concurrency: this._concurrency }
1210
- );
1211
- const existingCatalogStreamIds = (existingCatalog?.streams || []).map((entry) => {
1212
- return entry.stream;
1213
- });
1214
- const newlyDiscoveredStreamCatalogEntries = discoveredStreamCatalogEntries.filter((entry) => {
1215
- if (existingCatalogStreamIds.includes(entry.stream)) {
1216
- return false;
1217
- } else {
1218
- return true;
1219
- }
1220
- });
1221
- logger.debug(`Newly discovered streams: %j`, newlyDiscoveredStreamCatalogEntries);
1222
- const allDiscoveredStreamIds = discoveredStreamCatalogEntries.map((entry) => {
1223
- return entry.stream;
1224
- });
1225
- const existingCatalogMinusDeletedStreams = (existingCatalog?.streams || []).filter((entry) => {
1226
- if (!allDiscoveredStreamIds.includes(entry.stream)) {
1227
- logger.debug(`Stream %s was deleted, removing it from the catalog`, entry.stream);
1228
- return false;
1229
- } else {
1230
- return true;
1231
- }
1232
- });
1233
- const updatedExistingCatalog = existingCatalogMinusDeletedStreams.map((existingCatalogEntry) => {
1234
- const matchingDiscoveredCatalogEntry = discoveredStreamCatalogEntries.find((entry) => entry.stream === existingCatalogEntry.stream);
1235
- if (matchingDiscoveredCatalogEntry) {
1236
- const existingRootMetadata = existingCatalogEntry.metadata.find((metadata) => metadata.breadcrumb.length === 0);
1237
- const discoveredRootMetadata = matchingDiscoveredCatalogEntry.metadata.find((metadata) => metadata.breadcrumb.length === 0);
1238
- if (existingRootMetadata) {
1239
- const parentStreamVal = discoveredRootMetadata?.metadata["whaly-parent-stream"];
1240
- if (parentStreamVal !== void 0) {
1241
- existingRootMetadata.metadata["whaly-parent-stream"] = parentStreamVal;
1242
- }
1243
- const forcedReplicationMethodVal = discoveredRootMetadata?.metadata["forced-replication-method"];
1244
- if (forcedReplicationMethodVal !== void 0) {
1245
- existingRootMetadata.metadata["forced-replication-method"] = forcedReplicationMethodVal;
1246
- }
1247
- const forcedReplicationKeyVal = discoveredRootMetadata?.metadata["forced-replication-key"];
1248
- if (forcedReplicationKeyVal !== void 0) {
1249
- existingRootMetadata.metadata["forced-replication-key"] = forcedReplicationKeyVal;
1250
- }
1251
- const validReplicationKeysVal = discoveredRootMetadata?.metadata["valid-replication-keys"];
1252
- if (validReplicationKeysVal !== void 0) {
1253
- existingRootMetadata.metadata["valid-replication-keys"] = validReplicationKeysVal;
1254
- }
1255
- const rowCountVal = discoveredRootMetadata?.metadata["row-count"];
1256
- if (rowCountVal !== void 0) {
1257
- existingRootMetadata.metadata["row-count"] = rowCountVal;
1258
- }
1259
- }
1260
- }
1261
- return existingCatalogEntry;
1262
- });
1263
- const streams = updatedExistingCatalog.concat(newlyDiscoveredStreamCatalogEntries);
1264
- return { streams };
1265
- };
1266
- sync = async (catalog) => {
1267
- await this.initiateSync(catalog);
1268
- logger.debug(`[TAP] All streams have finished syncing. We send the [complete] signal to the target.`);
1269
- await this._target.complete();
1270
- return Promise.resolve();
1271
- };
1272
- async generateMetadataFromStream(stream, parentStreamId) {
1273
- const metadata = new StreamMetadata();
1274
- metadata.write([], "table-key-properties", stream.primaryKey);
1275
- const defaultReplicationConfig = stream.defaultReplicationConfig();
1276
- metadata.write([], "replication-method", defaultReplicationConfig.replicationMethod);
1277
- const validReplicationkeys = await stream.getValidReplicationKeys();
1278
- metadata.write([], "valid-replication-keys", validReplicationkeys);
1279
- if (defaultReplicationConfig.isForced) {
1280
- metadata.write([], "forced-replication-method", defaultReplicationConfig.replicationMethod);
1281
- if (stream.forcedReplicationKey) {
1282
- metadata.write([], "forced-replication-key", stream.forcedReplicationKey);
1283
- }
1284
- }
1285
- const rowCount = await stream.getRowCount();
1286
- if (rowCount !== void 0) {
1287
- metadata.write([], "row-count", rowCount);
1288
- }
1289
- if (stream.isSilent === true) {
1290
- metadata.write([], "whaly-is-hidden", stream.isSilent);
1291
- }
1292
- if (stream.isSilent === false) {
1293
- metadata.write([], "selected-by-default", stream.selectedByDefault);
1294
- }
1295
- if (stream.displayLabel) {
1296
- metadata.write([], "whaly-display-label", stream.displayLabel);
1297
- }
1298
- if (stream.description) {
1299
- metadata.write([], "whaly-description", stream.description);
1300
- }
1301
- if (parentStreamId !== void 0) {
1302
- metadata.write([], "whaly-parent-stream", parentStreamId);
1303
- }
1304
- return { metadata: metadata.toList() };
742
+ target;
743
+ config;
744
+ streams;
745
+ concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
746
+ stateProvider;
747
+ tapState;
748
+ constructor(target, config, stateProvider) {
749
+ this.streams = [];
750
+ this.target = target;
751
+ this.config = config;
752
+ this.stateProvider = stateProvider;
753
+ this.tapState = { bookmarks: {} };
1305
754
  }
1306
- async initiateSync(catalog) {
755
+ sync = async (options) => {
756
+ const initialState = await this.stateProvider.getState();
757
+ if (initialState.state && initialState.state.bookmarks) {
758
+ this.tapState = (0, import_cloneDeep.default)(initialState.state);
759
+ }
760
+ await this.init();
1307
761
  logger.info(`\u{1F680} Start syncing`);
1308
762
  const state = StateService.getInstance().get();
1309
763
  logger.info(`\u{1F4CD} Received state: %j`, state);
1310
- logger.debug(`\u{1F4DA} Received catalog: %j`, catalog);
1311
- const selectedStreams = catalog.getSelectedStreams();
1312
- if (selectedStreams.length === 0) {
1313
- logger.info(`\u{1F631} There is no stream to sync.
1314
-
1315
- Did you update the \`catalog.json\` to add \`"selected": true\` in the table metadata associated with the root breadcrumb (e.g. \`[]\`)`);
1316
- return;
1317
- }
1318
- const selectedStreamIds = selectedStreams.map((stream) => stream.stream);
1319
- logger.info(`\u{1F973} Syncing selected streams: %s`, selectedStreamIds.join(","));
1320
- await import_bluebird.Promise.map(
1321
- this._streams,
1322
- (stream) => {
1323
- if (selectedStreamIds.includes(stream.streamId)) {
1324
- const selectedStream = selectedStreams.find(((selectedStream2) => selectedStream2.stream === stream.streamId));
1325
- const rootMetadataEntry = selectedStream?.metadata.find((streamMdt) => streamMdt.breadcrumb.length === 0);
1326
- if (!rootMetadataEntry) {
1327
- throw new Error(`Can't find a catalog root metadata entry for StreamId=${stream.streamId}`);
1328
- }
1329
- stream.setRootMetadataEntry(rootMetadataEntry.metadata);
1330
- return stream.sync();
1331
- }
1332
- },
1333
- {
1334
- concurrency: this._concurrency
1335
- }
1336
- );
1337
- }
764
+ const envInclude = process.env.TAP_STREAMS ? process.env.TAP_STREAMS.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
765
+ const include = options?.include && options.include.length > 0 ? options.include : envInclude;
766
+ const exclude = new Set((options?.exclude || []).map((s) => s.trim()));
767
+ let streamsToRun = this.streams.filter((s) => !s.isSilent);
768
+ if (include && include.length > 0) {
769
+ const includeSet = new Set(include);
770
+ streamsToRun = streamsToRun.filter((s) => includeSet.has(s.streamId));
771
+ }
772
+ if (exclude.size > 0) {
773
+ streamsToRun = streamsToRun.filter((s) => !exclude.has(s.streamId));
774
+ }
775
+ const selectedStreamIds = streamsToRun.map((s) => s.streamId);
776
+ logger.info(`\u{1F973} Syncing streams: %s`, selectedStreamIds.join(","));
777
+ await import_bluebird.default.map(streamsToRun, (stream) => {
778
+ return stream.sync();
779
+ }, { concurrency: this.concurrency });
780
+ logger.debug(`[TAP] All streams have finished syncing. We send the [complete] signal to the target.`);
781
+ await this.target.complete();
782
+ return Promise.resolve();
783
+ };
1338
784
  // Used to clean up any ressource before exit
1339
785
  end() {
1340
786
  return Promise.resolve();
@@ -1343,18 +789,12 @@ var Tap = class {
1343
789
 
1344
790
  // src/sdk/models/tap/stream.ts
1345
791
  var import_util2 = require("util");
1346
- var StreamV2 = class {
792
+ var Stream = class {
1347
793
  // Runtime values, can't be overriden
1348
- _config;
1349
- _tapName = "default";
1350
- _tapState;
1351
- _target;
1352
- // Contains the root metadata entry of the catalog stream configuration
1353
- // contains some end user schema configuration
1354
- rootMetadataEntry;
1355
- // Compile time values, can be overriden
1356
- // Replication method that can be forced in the stream implemenation. Will prevail over any user/catalog choice
1357
- forcedReplicationMethod = void 0;
794
+ config;
795
+ tapState;
796
+ target;
797
+ replicationMethod = void 0;
1358
798
  selectedByDefault = true;
1359
799
  STATE_MSG_FREQUENCY = 10;
1360
800
  streamId = "default";
@@ -1362,10 +802,9 @@ var StreamV2 = class {
1362
802
  displayLabel = void 0;
1363
803
  description = void 0;
1364
804
  primaryKey = [];
1365
- forcedReplicationKey = void 0;
805
+ replicationKey = void 0;
1366
806
  // When set, use the state from another stream for this stream
1367
807
  useStateFromStreamId = void 0;
1368
- relationships = [];
1369
808
  children = [];
1370
809
  // If true, it means that the records in this stream are sorted by replicationKey.
1371
810
  // If false, then the records are coming in an unsorted manner.
@@ -1377,11 +816,10 @@ var StreamV2 = class {
1377
816
  // Metrics configuration
1378
817
  rowsSyncedMetricsConf;
1379
818
  executionTimeMetricsConf;
1380
- constructor(tapName, config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1381
- this._tapName = tapName;
1382
- this._config = config;
1383
- this._tapState = _2.cloneDeep(state);
1384
- this._target = target;
819
+ constructor(config, tapState, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
820
+ this.config = config;
821
+ this.tapState = (0, import_cloneDeep2.default)(tapState);
822
+ this.target = target;
1385
823
  this.childConcurrency = childConcurrency;
1386
824
  this.rowsSyncedMetricsConf = {
1387
825
  isEnabled: () => !this.isSilent,
@@ -1392,9 +830,7 @@ var StreamV2 = class {
1392
830
  getStreamIds: () => [this.streamId]
1393
831
  };
1394
832
  }
1395
- setRootMetadataEntry = (rootMetadataEntry) => {
1396
- this.rootMetadataEntry = rootMetadataEntry;
1397
- };
833
+ // No root metadata: replication config is derived from stream defaults only
1398
834
  /**
1399
835
  * Sync this stream
1400
836
  * Called only for streams with no parents, aka. "root tap streams"
@@ -1425,7 +861,7 @@ var StreamV2 = class {
1425
861
  `\u{1F6BD} Flushing ${replicationMethod} sync of stream '${this.streamId}' + all of its children...`
1426
862
  );
1427
863
  await this._flush();
1428
- await import_bluebird2.Promise.map(this.children, (child) => {
864
+ await import_bluebird2.default.map(this.children, (child) => {
1429
865
  return child.flush();
1430
866
  }, { concurrency: 3 });
1431
867
  };
@@ -1435,7 +871,7 @@ var StreamV2 = class {
1435
871
  */
1436
872
  _writeStateMessage = () => {
1437
873
  const message = new StateMessage(StateService.getInstance().get());
1438
- return this._target.state(message);
874
+ return this.target.state(message);
1439
875
  };
1440
876
  /**
1441
877
  * Write out a ACTIVATE_VERSION message.
@@ -1443,10 +879,10 @@ var StreamV2 = class {
1443
879
  _writeReplicationMethodMessage = async () => {
1444
880
  const method = this.configuredReplicationConfig().replicationMethod;
1445
881
  const message = new ReplicationMethodMessage(this.streamId, method);
1446
- await import_bluebird2.Promise.map(this.children, async (c) => {
882
+ await import_bluebird2.default.map(this.children, async (c) => {
1447
883
  return c._writeReplicationMethodMessage();
1448
884
  }, { concurrency: 3 });
1449
- await this._target.replicationMethod(message);
885
+ await this.target.replicationMethod(message);
1450
886
  };
1451
887
  /**
1452
888
  * Write out a SCHEMA message with the stream schema.
@@ -1455,23 +891,21 @@ var StreamV2 = class {
1455
891
  if (!this.isSilent) {
1456
892
  const schema = await this.getSchema();
1457
893
  if (schema !== void 0) {
1458
- const relatedRels = findRelatedRelationships(this.streamId, this.relationships);
1459
894
  const replicationConfig = this.configuredReplicationConfig();
1460
895
  const message = new SchemaMessage({
1461
896
  keyProperties: this.primaryKey,
1462
897
  stream: this.streamId,
1463
898
  schema: schema.jsonSchema,
1464
- relationships: relatedRels,
1465
899
  ...replicationConfig.replicationKey ? { bookmarkProperties: [replicationConfig.replicationKey] } : {},
1466
900
  ...this.displayLabel ? { displayLabel: this.displayLabel } : {},
1467
901
  ...this.description ? { description: this.description } : {},
1468
902
  ...schema.propertiesMetadata ? { propertiesMetadata: schema.propertiesMetadata } : {}
1469
903
  });
1470
- await this._target.schema(message);
904
+ await this.target.schema(message);
1471
905
  }
1472
906
  }
1473
907
  if (skipChildrenSchema !== true) {
1474
- await import_bluebird2.Promise.map(this.children, async (child) => {
908
+ await import_bluebird2.default.map(this.children, async (child) => {
1475
909
  await child._writeSchemaMessage();
1476
910
  }, { concurrency: 3 });
1477
911
  }
@@ -1481,7 +915,7 @@ var StreamV2 = class {
1481
915
  const signpostMoment = await this.getReplicationKeySignpost();
1482
916
  const signpostValue = signpostMoment?.format(defaultDateTimeFormat);
1483
917
  if (signpostValue) {
1484
- StateService.getInstance().setBookmarkSignpostV2(this.streamId, signpostValue);
918
+ StateService.getInstance().setBookmarkSignpost(this.streamId, signpostValue);
1485
919
  }
1486
920
  };
1487
921
  /**
@@ -1497,7 +931,7 @@ var StreamV2 = class {
1497
931
  if (this.useStateFromStreamId) {
1498
932
  return;
1499
933
  }
1500
- if (this.forcedReplicationMethod && !this.forcedReplicationKey) {
934
+ if (this.replicationMethod && !this.replicationKey) {
1501
935
  return;
1502
936
  }
1503
937
  if (!replicationConfig.replicationKey) {
@@ -1507,14 +941,14 @@ var StreamV2 = class {
1507
941
  );
1508
942
  }
1509
943
  const stateServiceInst = StateService.getInstance();
1510
- const state = stateServiceInst.getBookmarkV2(this.streamId);
944
+ const state = stateServiceInst.getBookmark(this.streamId);
1511
945
  const newState = incrementStreamState(
1512
946
  state,
1513
947
  replicationConfig.replicationKey,
1514
948
  latestRecord,
1515
949
  this.isSorted
1516
950
  );
1517
- StateService.getInstance().setBookmarkV2(this.streamId, newState);
951
+ StateService.getInstance().setBookmark(this.streamId, newState);
1518
952
  }
1519
953
  }
1520
954
  };
@@ -1546,27 +980,27 @@ var StreamV2 = class {
1546
980
  **/
1547
981
  getStartingTimestamp() {
1548
982
  const streamIdToReadFrom = this.useStateFromStreamId ? this.useStateFromStreamId : this.streamId;
1549
- const state = extractStateForStream(this._tapState, streamIdToReadFrom);
1550
- const startDate = this._config.start_date;
983
+ const state = extractStateForStream(this.tapState, streamIdToReadFrom);
984
+ const startDate = this.config.start_date;
1551
985
  const replicationConfig = this.configuredReplicationConfig();
1552
986
  logger.debug(`${this.streamId}: state.replicationKeyValue: ${state.replicationKeyValue},
1553
987
  state.replicationKey: ${state.replicationKey},
1554
988
  this.replicationKey: ${replicationConfig.replicationKey}`);
1555
989
  if (state.replicationKeyValue) {
1556
990
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning replication key value: ${state.replicationKeyValue}`);
1557
- return (0, import_moment2.default)(state.replicationKeyValue);
991
+ return (0, import_dayjs2.default)(state.replicationKeyValue);
1558
992
  } else if (startDate) {
1559
993
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning config start date: ${startDate}`);
1560
- return (0, import_moment2.default)(startDate);
994
+ return (0, import_dayjs2.default)(startDate);
1561
995
  } else {
1562
996
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning EPOCH (1970)`);
1563
- return (0, import_moment2.default)(0);
997
+ return (0, import_dayjs2.default)(0);
1564
998
  }
1565
999
  }
1566
1000
  /**
1567
1001
  * Return the max allowable bookmark value for this stream's replication key.
1568
1002
  *
1569
- * For timestamp-based replication keys, this defaults to `moment()`. For
1003
+ * For timestamp-based replication keys, this defaults to `dayjs()`. For
1570
1004
  * non-timestamp replication keys, default to `undefined`.
1571
1005
  *
1572
1006
  * Override this value to prevent bookmarks from being advanced in cases where we
@@ -1574,7 +1008,7 @@ var StreamV2 = class {
1574
1008
  */
1575
1009
  getReplicationKeySignpost = async () => {
1576
1010
  if (await this.isTimestampReplicationKey()) {
1577
- return (0, import_moment2.default)();
1011
+ return (0, import_dayjs2.default)();
1578
1012
  }
1579
1013
  return void 0;
1580
1014
  };
@@ -1582,22 +1016,22 @@ var StreamV2 = class {
1582
1016
  * Return the default replication method for the stream that will be used to write the catalog.
1583
1017
  */
1584
1018
  defaultReplicationConfig = () => {
1585
- if (this.forcedReplicationMethod) {
1019
+ if (this.replicationMethod) {
1586
1020
  return {
1587
- replicationMethod: this.forcedReplicationMethod,
1588
- replicationKey: this.forcedReplicationKey,
1021
+ replicationMethod: this.replicationMethod,
1022
+ replicationKey: this.replicationKey,
1589
1023
  isForced: true
1590
1024
  };
1591
1025
  }
1592
- if (this.forcedReplicationKey || this.useStateFromStreamId) {
1026
+ if (this.replicationKey || this.useStateFromStreamId) {
1593
1027
  return {
1594
- replicationMethod: "INCREMENTAL",
1595
- replicationKey: this.forcedReplicationKey,
1028
+ replicationMethod: "INCREMENTAL" /* INCREMENTAL */,
1029
+ replicationKey: this.replicationKey,
1596
1030
  isForced: true
1597
1031
  };
1598
1032
  }
1599
1033
  return {
1600
- replicationMethod: "FULL_TABLE",
1034
+ replicationMethod: "FULL_TABLE" /* FULL_TABLE */,
1601
1035
  replicationKey: void 0,
1602
1036
  isForced: false
1603
1037
  };
@@ -1606,16 +1040,10 @@ var StreamV2 = class {
1606
1040
  * Return the configured replication method that will be used for the sync.
1607
1041
  */
1608
1042
  configuredReplicationConfig = () => {
1609
- const defaultReplicationMethod = this.defaultReplicationConfig();
1610
- if (defaultReplicationMethod.isForced) {
1611
- return {
1612
- replicationMethod: defaultReplicationMethod.replicationMethod,
1613
- replicationKey: defaultReplicationMethod.replicationKey
1614
- };
1615
- }
1043
+ const defaults = this.defaultReplicationConfig();
1616
1044
  return {
1617
- replicationMethod: this.rootMetadataEntry?.["replication-method"] || defaultReplicationMethod.replicationMethod,
1618
- replicationKey: this.rootMetadataEntry?.["replication-key"] || defaultReplicationMethod.replicationKey
1045
+ replicationMethod: defaults.replicationMethod,
1046
+ replicationKey: defaults.replicationKey
1619
1047
  };
1620
1048
  };
1621
1049
  // Private sync methods:
@@ -1645,9 +1073,9 @@ var StreamV2 = class {
1645
1073
  this.streamId
1646
1074
  );
1647
1075
  if (!this.isSilent) {
1648
- await this._target.record(recordMessage);
1076
+ await this.target.record(recordMessage);
1649
1077
  }
1650
- await import_bluebird2.Promise.map(
1078
+ await import_bluebird2.default.map(
1651
1079
  this.children,
1652
1080
  async (child) => {
1653
1081
  await child.asyncInit(row);
@@ -1664,9 +1092,9 @@ var StreamV2 = class {
1664
1092
  rowsSent += 1;
1665
1093
  }
1666
1094
  const stateServiceInst = StateService.getInstance();
1667
- const state = stateServiceInst.getBookmarkV2(this.streamId);
1095
+ const state = stateServiceInst.getBookmark(this.streamId);
1668
1096
  const newState = finalizeStateProgressMarkers(state);
1669
- stateServiceInst.setBookmarkV2(this.streamId, newState);
1097
+ stateServiceInst.setBookmark(this.streamId, newState);
1670
1098
  if (!parent) {
1671
1099
  logger.info(`\u2705 Completed sync for stream: ${this.streamId} (${rowsSent} records)`);
1672
1100
  } else {
@@ -1691,7 +1119,7 @@ var StreamV2 = class {
1691
1119
  getSchema() {
1692
1120
  logger.debug(`stream: ${this.streamId} - getSchema() is called`);
1693
1121
  if (this.schemaPath) {
1694
- const schema = loadJson(`schemas/${this._tapName}/${this.schemaPath}`);
1122
+ const schema = loadJson(`schemas/${this.schemaPath}`);
1695
1123
  return Promise.resolve({ jsonSchema: schema });
1696
1124
  } else {
1697
1125
  if (this.isSilent) {
@@ -1728,7 +1156,7 @@ var StreamV2 = class {
1728
1156
  };
1729
1157
 
1730
1158
  // src/sdk/helpers/qs-logger.ts
1731
- var _3 = __toESM(require("lodash"));
1159
+ var import_isPlainObject = __toESM(require("lodash/isPlainObject.js"));
1732
1160
  var MAX_QS_KEYS = 10;
1733
1161
  var MAX_QS_STRING_LENGTH = 200;
1734
1162
  var MAX_QS_ARRAY_ITEMS = 5;
@@ -1749,7 +1177,7 @@ function truncateQueryParams(value, depth = 0) {
1749
1177
  }
1750
1178
  return items;
1751
1179
  }
1752
- if (_3.isPlainObject(value)) {
1180
+ if ((0, import_isPlainObject.default)(value)) {
1753
1181
  const keys = Object.keys(value);
1754
1182
  const result = {};
1755
1183
  for (const key of keys.slice(0, MAX_QS_KEYS)) {
@@ -1772,11 +1200,12 @@ function getTruncatedParamsForLog(params) {
1772
1200
  }
1773
1201
 
1774
1202
  // src/sdk/models/tap/restStream.ts
1775
- var _4 = __toESM(require("lodash"));
1776
- var RESTStreamV2 = class extends StreamV2 {
1203
+ var import_cloneDeep3 = __toESM(require("lodash/cloneDeep.js"));
1204
+ var import_isEqual = __toESM(require("lodash/isEqual.js"));
1205
+ var RESTStream = class extends Stream {
1777
1206
  axiosInstance;
1778
- constructor(tapName, config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1779
- super(tapName, config, state, target, childConcurrency);
1207
+ constructor(config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1208
+ super(config, state, target, childConcurrency);
1780
1209
  this.apiCallsMetricsConf = {
1781
1210
  isEnabled: () => !this.isSilent,
1782
1211
  getStreamIds: () => [this.streamId]
@@ -1792,14 +1221,25 @@ var RESTStreamV2 = class extends StreamV2 {
1792
1221
  */
1793
1222
  _prepareRequest = async (nextPageToken, parent) => {
1794
1223
  const method = this.httpMethod;
1795
- const url = this.getUrl(parent);
1224
+ const url = this.getNextUrl(nextPageToken, parent);
1796
1225
  if (!url) {
1797
1226
  throw new Error(`StreamId: ${this.streamId} - URL is undefined. Did your properly define the URL for this stream?`);
1798
1227
  }
1799
1228
  const authenticator = this.authenticator;
1229
+ let finalUrl = url;
1230
+ let urlQueryParams = {};
1231
+ const queryIndex = url.indexOf("?");
1232
+ if (queryIndex !== -1) {
1233
+ finalUrl = url.substring(0, queryIndex);
1234
+ const rawQueryString = url.substring(queryIndex + 1);
1235
+ urlQueryParams = qs2.parse(rawQueryString);
1236
+ }
1800
1237
  const params = {
1801
1238
  ...this.getNextUrlParams(nextPageToken, parent),
1802
- ...await authenticator?.getAuthQS(this._config)
1239
+ // Query params embedded in URL should override computed pagination params
1240
+ // but should not override auth params which are appended last
1241
+ ...urlQueryParams,
1242
+ ...await authenticator?.getAuthQS(this.config)
1803
1243
  };
1804
1244
  const requestBody = this.getRequestBodyForNextCall(nextPageToken, parent);
1805
1245
  let headers = this.getCustomHTTPHeaders(parent);
@@ -1807,7 +1247,7 @@ var RESTStreamV2 = class extends StreamV2 {
1807
1247
  headers = {};
1808
1248
  }
1809
1249
  if (authenticator) {
1810
- const authHeaders = await authenticator.getAuthHeaders(this._config);
1250
+ const authHeaders = await authenticator.getAuthHeaders(this.config);
1811
1251
  headers = {
1812
1252
  ...headers,
1813
1253
  ...authHeaders
@@ -1817,7 +1257,7 @@ var RESTStreamV2 = class extends StreamV2 {
1817
1257
  return qs2.stringify(params2, { arrayFormat: "repeat" });
1818
1258
  };
1819
1259
  const request = {
1820
- url,
1260
+ url: finalUrl,
1821
1261
  method,
1822
1262
  data: requestBody,
1823
1263
  params,
@@ -1890,9 +1330,9 @@ var RESTStreamV2 = class extends StreamV2 {
1890
1330
  for await (const row of rows) {
1891
1331
  yield row;
1892
1332
  }
1893
- const previousToken = _4.cloneDeep(nextPageToken);
1333
+ const previousToken = (0, import_cloneDeep3.default)(nextPageToken);
1894
1334
  nextPageToken = this.getNextPageToken(resp, previousToken);
1895
- if (nextPageToken && _4.isEqual(nextPageToken, previousToken)) {
1335
+ if (nextPageToken && (0, import_isEqual.default)(nextPageToken, previousToken)) {
1896
1336
  throw new Error(
1897
1337
  `${this.streamId} - Loop detected in pagination. "
1898
1338
  Pagination token: \`${JSON.stringify(nextPageToken)}\` is identical to prior token \`${JSON.stringify(previousToken)}\`.`
@@ -1969,7 +1409,7 @@ var RESTStreamV2 = class extends StreamV2 {
1969
1409
  * TODO: Make URL + Path templatisable and replace those with Extractor (or Tap) config
1970
1410
  * @returns
1971
1411
  */
1972
- getUrl(parent) {
1412
+ getNextUrl(previousToken, parent) {
1973
1413
  const urlPattern = [this.baseUrl, this.path].join("");
1974
1414
  return urlPattern;
1975
1415
  }
@@ -2093,20 +1533,20 @@ var StreamWarehouseSyncService = class {
2093
1533
  const columnNamesFromWarehouse = columnsFromWarehouse.map((col) => col.name);
2094
1534
  const columnsMappingStore = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2095
1535
  const columnsToAdd = Object.keys(columnsMappingStore).filter((unsafeColumnKey) => {
2096
- const safeColumnName = columnsMappingStore[unsafeColumnKey];
2097
- if (!safeColumnName) {
1536
+ const safeColumnName2 = columnsMappingStore[unsafeColumnKey];
1537
+ if (!safeColumnName2) {
2098
1538
  return false;
2099
1539
  }
2100
- return !columnNamesFromWarehouse.includes(safeColumnName);
1540
+ return !columnNamesFromWarehouse.includes(safeColumnName2);
2101
1541
  }).map((unsafeColumnKey) => {
2102
- const safeColumnName = columnsMappingStore[unsafeColumnKey];
1542
+ const safeColumnName2 = columnsMappingStore[unsafeColumnKey];
2103
1543
  const definition = this.flattenedSchema[unsafeColumnKey];
2104
- if (!safeColumnName || !definition) {
1544
+ if (!safeColumnName2 || !definition) {
2105
1545
  return void 0;
2106
1546
  }
2107
1547
  return {
2108
1548
  unsafeName: unsafeColumnKey,
2109
- name: safeColumnName,
1549
+ name: safeColumnName2,
2110
1550
  definition
2111
1551
  };
2112
1552
  }).filter((item) => !!item);
@@ -2321,18 +1761,18 @@ var flattenSchema = (streamId, schema) => {
2321
1761
 
2322
1762
  // src/sdk/models/target/target.ts
2323
1763
  var import_jsonschema = require("jsonschema");
2324
- var import_moment4 = __toESM(require("moment"));
1764
+ var import_dayjs4 = __toESM(require("dayjs"));
2325
1765
 
2326
1766
  // src/sdk/models/target/streamDbState.ts
2327
- var import_moment3 = __toESM(require("moment"));
1767
+ var import_dayjs3 = __toESM(require("dayjs"));
2328
1768
 
2329
1769
  // src/sdk/models/target/temporaryFile.ts
2330
- var import_fs_extra2 = __toESM(require("fs-extra"));
2331
- var import_lodash = require("lodash");
1770
+ var import_fs_extra = __toESM(require("fs-extra"));
1771
+ var import_uniqueId = __toESM(require("lodash/uniqueId.js"));
2332
1772
  var createTemporaryFileStream = (streamId) => {
2333
- import_fs_extra2.default.ensureDirSync("./tmp");
2334
- const path = `./tmp/${safePath(streamId)}-${(0, import_lodash.uniqueId)()}.tmp`;
2335
- const writeStream = import_fs_extra2.default.createWriteStream(path, { flags: "w" });
1773
+ import_fs_extra.default.ensureDirSync("./tmp");
1774
+ const path = `./tmp/${safePath(streamId)}-${(0, import_uniqueId.default)()}.tmp`;
1775
+ const writeStream = import_fs_extra.default.createWriteStream(path, { flags: "w" });
2336
1776
  return {
2337
1777
  stream: writeStream,
2338
1778
  path
@@ -2380,7 +1820,7 @@ function replaceSymbols(str) {
2380
1820
  }
2381
1821
 
2382
1822
  // src/sdk/models/target/streamDbState.ts
2383
- var fs4 = __toESM(require("fs"));
1823
+ var fs3 = __toESM(require("fs"));
2384
1824
  var StreamState = class {
2385
1825
  streamId;
2386
1826
  schema;
@@ -2405,7 +1845,7 @@ var StreamState = class {
2405
1845
  this.fileToLoad = createTemporaryFileStream(streamId);
2406
1846
  this.syncedRowCount = 0;
2407
1847
  this.keyProperties = [];
2408
- this.batchDate = (0, import_moment3.default)();
1848
+ this.batchDate = (0, import_dayjs3.default)();
2409
1849
  this.replicationMethod = replicationMethod;
2410
1850
  this._hasBeenLoadedYetDuringThisSync = false;
2411
1851
  }
@@ -2430,7 +1870,7 @@ var StreamState = class {
2430
1870
  resetFileToLoad() {
2431
1871
  this.fileToLoad.stream.close();
2432
1872
  const oldPath = this.fileToLoad.path;
2433
- fs4.unlinkSync(oldPath);
1873
+ fs3.unlinkSync(oldPath);
2434
1874
  this.fileToLoad = createTemporaryFileStream(this.streamId);
2435
1875
  this.batchedRowCount = 0;
2436
1876
  }
@@ -2456,13 +1896,13 @@ var StreamState = class {
2456
1896
 
2457
1897
  // src/sdk/models/target/target.ts
2458
1898
  var import_async_mutex = require("async-mutex");
2459
- var import_bluebird3 = require("bluebird");
1899
+ var import_bluebird3 = __toESM(require("bluebird"));
2460
1900
  var semaphore = new import_async_mutex.Semaphore(1);
2461
1901
  var ITarget = class _ITarget {
2462
1902
  config;
2463
1903
  schemaHooks;
2464
- resolver;
2465
1904
  syncTime;
1905
+ stateProvider;
2466
1906
  // Latest state message received from the Tap
2467
1907
  // Not yet flushed as we didn't upload the stream data since receiving it
2468
1908
  batchedState;
@@ -2473,15 +1913,15 @@ var ITarget = class _ITarget {
2473
1913
  streams;
2474
1914
  // JSON Schema validator
2475
1915
  validator;
2476
- constructor(config, resolver) {
1916
+ constructor(config, stateProvider) {
2477
1917
  this.config = config;
2478
- this.resolver = resolver;
2479
- this.schemaHooks = resolver.getSchemaHooks();
1918
+ this.stateProvider = stateProvider;
1919
+ this.schemaHooks = [];
2480
1920
  this.streams = {};
2481
1921
  this.batchedState = {};
2482
1922
  this.flushedState = {};
2483
1923
  this.validator = new import_jsonschema.Validator();
2484
- this.syncTime = (0, import_moment4.default)();
1924
+ this.syncTime = (0, import_dayjs4.default)();
2485
1925
  this.renameColumnStore = new RenameColumnStore();
2486
1926
  }
2487
1927
  ///////////////////////////////////////////
@@ -2590,7 +2030,7 @@ var ITarget = class _ITarget {
2590
2030
  this.streams[streamId] = new StreamState(
2591
2031
  streamId,
2592
2032
  dbSyncInstance,
2593
- replicationMethod || "FULL_TABLE"
2033
+ replicationMethod || "FULL_TABLE" /* FULL_TABLE */
2594
2034
  );
2595
2035
  }
2596
2036
  replicationMethod = (message) => {
@@ -2667,41 +2107,9 @@ var ITarget = class _ITarget {
2667
2107
  return translation;
2668
2108
  }
2669
2109
  return k;
2670
- }),
2671
- relationships: {
2672
- left: message.relationships.left.flatMap((l) => {
2673
- if (this.renameColumnStore.isReady(l.streamId) && this.renameColumnStore.isReady(message.stream)) {
2674
- const from = this.renameColumnStore.getColumnTranslation(l.streamId, l.from);
2675
- const to = this.renameColumnStore.getColumnTranslation(message.stream, l.to);
2676
- if (from && to) {
2677
- return [{
2678
- type: l.type,
2679
- streamId: l.streamId,
2680
- from,
2681
- to
2682
- }];
2683
- }
2684
- }
2685
- return [];
2686
- }),
2687
- right: message.relationships.right.flatMap((r) => {
2688
- if (this.renameColumnStore.isReady(message.stream) && this.renameColumnStore.isReady(r.streamId)) {
2689
- const from = this.renameColumnStore.getColumnTranslation(message.stream, r.from);
2690
- const to = this.renameColumnStore.getColumnTranslation(r.streamId, r.to);
2691
- if (from && to) {
2692
- return [{
2693
- type: r.type,
2694
- streamId: r.streamId,
2695
- from,
2696
- to
2697
- }];
2698
- }
2699
- }
2700
- return [];
2701
- })
2702
- }
2110
+ })
2703
2111
  };
2704
- await import_bluebird3.Promise.map(this.schemaHooks, async (hook) => {
2112
+ await import_bluebird3.default.map(this.schemaHooks, async (hook) => {
2705
2113
  const input = {
2706
2114
  databaseName,
2707
2115
  schemaName,
@@ -2732,9 +2140,6 @@ var ITarget = class _ITarget {
2732
2140
  try {
2733
2141
  logger.info(`\u{1F44D} Data Extraction is complete. Will load remaining batched stream records.`);
2734
2142
  await this.loadAllStreamsInWarehouse({ isFinalLoad: true });
2735
- const configResolver = loadResolver();
2736
- await configResolver.markSyncComplete();
2737
- await configResolver.flushMetrics();
2738
2143
  return Promise.resolve();
2739
2144
  } catch (err) {
2740
2145
  logger.error(`Error while handling complete event.`);
@@ -2777,12 +2182,11 @@ var ITarget = class _ITarget {
2777
2182
  }
2778
2183
  };
2779
2184
  emitState = (state) => {
2780
- const configResolver = loadResolver();
2781
- return writeStateFile(configResolver, JSON.stringify(state));
2185
+ return this.stateProvider.writeState(JSON.stringify(state));
2782
2186
  };
2783
2187
  uploadStreamsToWarehouse = async (streamsToUpload) => {
2784
2188
  try {
2785
- await import_bluebird3.Promise.map(streamsToUpload, async (streamId) => {
2189
+ await import_bluebird3.default.map(streamsToUpload, async (streamId) => {
2786
2190
  logger.info(`\u{1F4E4} Upload stream: ${streamId} to Warehouse`);
2787
2191
  const streamState = this.streams[streamId];
2788
2192
  if (streamState) {
@@ -2816,40 +2220,732 @@ var ITarget = class _ITarget {
2816
2220
  }
2817
2221
  };
2818
2222
  };
2223
+
2224
+ // src/targets/bigquery/helpers.ts
2225
+ var safeColumnName = (key) => {
2226
+ let returnString = key;
2227
+ const shouldNotStartWith = ["_TABLE_", "_FILE_", "_PARTITION"];
2228
+ shouldNotStartWith.forEach((snm) => {
2229
+ if (returnString.startsWith(snm)) {
2230
+ returnString = returnString.replace(snm, "");
2231
+ }
2232
+ });
2233
+ returnString = returnString.replace(/^[-\d\s]*/g, "");
2234
+ returnString = returnString.toLowerCase();
2235
+ returnString = returnString.replace("`", "");
2236
+ returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2237
+ if (returnString.length > 300) {
2238
+ returnString = returnString.substr(0, 300);
2239
+ }
2240
+ const pattern = /[^a-zA-Z0-9_]/gm;
2241
+ return returnString.replace(pattern, "_");
2242
+ };
2243
+ var safeTableName = (key) => {
2244
+ let returnString = key;
2245
+ returnString = returnString.toLowerCase();
2246
+ returnString = returnString.replace("`", "");
2247
+ returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2248
+ if (returnString.length > 1024) {
2249
+ returnString = returnString.substr(0, 1024);
2250
+ }
2251
+ const pattern = /[^a-zA-Z0-9_]/gm;
2252
+ return returnString.replace(pattern, "_");
2253
+ };
2254
+
2255
+ // src/targets/bigquery/service/record.ts
2256
+ var import_decimal = __toESM(require("decimal.js"));
2257
+ var import_dayjs5 = __toESM(require("dayjs"));
2258
+ var validateDateRange = (record, schema) => {
2259
+ Object.keys(schema).forEach((key) => {
2260
+ const { format: format6 } = schema[key];
2261
+ if (format6 === "date-time") {
2262
+ if (record[key]) {
2263
+ const formattedDate = (0, import_dayjs5.default)(record[key]).format(defaultDateTimeFormat);
2264
+ const isNotTooMuchInTheFuture = (0, import_dayjs5.default)(record[key]).isBefore((0, import_dayjs5.default)("9999-12-31 23:59:59.999999"));
2265
+ const isNotTooMuchInThePast = (0, import_dayjs5.default)(record[key]).isAfter((0, import_dayjs5.default)("0001-01-01 00:00:00"));
2266
+ if (formattedDate !== "Invalid date" && isNotTooMuchInTheFuture && isNotTooMuchInThePast) {
2267
+ record[key] = formattedDate;
2268
+ } else {
2269
+ record[key] = void 0;
2270
+ logger.info("Skipping date for key %s for being invalid", key);
2271
+ }
2272
+ }
2273
+ }
2274
+ });
2275
+ return record;
2276
+ };
2277
+ var maxBQNumericValue = new import_decimal.default("9.9999999999999999999999999999999999999E+28");
2278
+ var convertNumberIntoDecimal = (record, schema) => {
2279
+ Object.keys(record).forEach((key) => {
2280
+ if (schema[key]) {
2281
+ const { type, items } = schema[key];
2282
+ const types = type instanceof Array ? type : [type];
2283
+ if (types.includes("number")) {
2284
+ if (typeof record[key] === "number" || record[key] instanceof import_decimal.default) {
2285
+ const decimal = new import_decimal.default(record[key]).toDecimalPlaces(9);
2286
+ if (decimal.greaterThanOrEqualTo(maxBQNumericValue)) {
2287
+ record[key] = null;
2288
+ } else {
2289
+ record[key] = decimal;
2290
+ }
2291
+ } else {
2292
+ record[key] = null;
2293
+ }
2294
+ } else if (types.includes("array") && items?.type.includes("number")) {
2295
+ if (Array.isArray(record[key])) {
2296
+ const rawArr = record[key];
2297
+ const newArr = rawArr.map((item) => {
2298
+ if (typeof item === "number" || item instanceof import_decimal.default) {
2299
+ const parsed = new import_decimal.default(item).toDecimalPlaces(9);
2300
+ if (parsed.greaterThanOrEqualTo(maxBQNumericValue)) {
2301
+ return void 0;
2302
+ } else {
2303
+ return parsed;
2304
+ }
2305
+ } else {
2306
+ return void 0;
2307
+ }
2308
+ }).filter((item) => !!item);
2309
+ record[key] = newArr;
2310
+ } else {
2311
+ record[key] = null;
2312
+ }
2313
+ }
2314
+ }
2315
+ });
2316
+ return record;
2317
+ };
2318
+
2319
+ // src/targets/bigquery/service/dbSync.ts
2320
+ var import_dayjs6 = __toESM(require("dayjs"));
2321
+
2322
+ // src/targets/bigquery/service/bigquery.ts
2323
+ var import_bigquery = require("@google-cloud/bigquery");
2324
+ var BqClientHolder = class {
2325
+ static instance;
2326
+ client;
2327
+ constructor(config) {
2328
+ const retryOptions = {
2329
+ autoRetry: true,
2330
+ maxRetries: 3
2331
+ };
2332
+ this.client = new import_bigquery.BigQuery({
2333
+ projectId: config.database.trim(),
2334
+ ...retryOptions
2335
+ });
2336
+ }
2337
+ static client(config) {
2338
+ if (!this.instance) {
2339
+ this.instance = new this(config);
2340
+ }
2341
+ return this.instance.client;
2342
+ }
2343
+ };
2344
+
2345
+ // src/targets/bigquery/service/cloudStorage.ts
2346
+ var import_storage = require("@google-cloud/storage");
2347
+
2348
+ // node_modules/uuid/dist/esm-node/rng.js
2349
+ var import_crypto = __toESM(require("crypto"));
2350
+ var rnds8Pool = new Uint8Array(256);
2351
+ var poolPtr = rnds8Pool.length;
2352
+ function rng() {
2353
+ if (poolPtr > rnds8Pool.length - 16) {
2354
+ import_crypto.default.randomFillSync(rnds8Pool);
2355
+ poolPtr = 0;
2356
+ }
2357
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
2358
+ }
2359
+
2360
+ // node_modules/uuid/dist/esm-node/regex.js
2361
+ var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
2362
+
2363
+ // node_modules/uuid/dist/esm-node/validate.js
2364
+ function validate(uuid) {
2365
+ return typeof uuid === "string" && regex_default.test(uuid);
2366
+ }
2367
+ var validate_default = validate;
2368
+
2369
+ // node_modules/uuid/dist/esm-node/stringify.js
2370
+ var byteToHex = [];
2371
+ for (let i = 0; i < 256; ++i) {
2372
+ byteToHex.push((i + 256).toString(16).substr(1));
2373
+ }
2374
+ function stringify3(arr, offset = 0) {
2375
+ const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
2376
+ if (!validate_default(uuid)) {
2377
+ throw TypeError("Stringified UUID is invalid");
2378
+ }
2379
+ return uuid;
2380
+ }
2381
+ var stringify_default = stringify3;
2382
+
2383
+ // node_modules/uuid/dist/esm-node/v4.js
2384
+ function v4(options, buf, offset) {
2385
+ options = options || {};
2386
+ const rnds = options.random || (options.rng || rng)();
2387
+ rnds[6] = rnds[6] & 15 | 64;
2388
+ rnds[8] = rnds[8] & 63 | 128;
2389
+ if (buf) {
2390
+ offset = offset || 0;
2391
+ for (let i = 0; i < 16; ++i) {
2392
+ buf[offset + i] = rnds[i];
2393
+ }
2394
+ return buf;
2395
+ }
2396
+ return stringify_default(rnds);
2397
+ }
2398
+ var v4_default = v4;
2399
+
2400
+ // src/targets/bigquery/service/cloudStorage.ts
2401
+ var uploadFileInBucket = async (streamId, gcsBuckerName, connectorId, filePath) => {
2402
+ try {
2403
+ const retryOptions = { autoRetry: true, maxRetries: 20 };
2404
+ let storage = new import_storage.Storage({ retryOptions });
2405
+ const bucketRef = storage.bucket(gcsBuckerName);
2406
+ await bucketRef.get({ autoCreate: false });
2407
+ const destinationFileName = `${connectorId}/${streamId}-${v4_default()}.jsonnl`;
2408
+ await bucketRef.upload(filePath, {
2409
+ destination: destinationFileName
2410
+ });
2411
+ logger.info(`\u{1F5F3} Uploaded ${filePath} into ${gcsBuckerName}/${destinationFileName} GCS File`);
2412
+ return bucketRef.file(destinationFileName);
2413
+ } catch (err) {
2414
+ logger.error(`Issue when uploading file into GCS bucket for stream: ${streamId}
2415
+
2416
+ Error: ${err.message}
2417
+ Stack: ${err.stack}
2418
+ Code: ${err.code}
2419
+ `);
2420
+ if (err.code < 500) {
2421
+ await haltAndCatchFire(
2422
+ `unauthorized`,
2423
+ `We couldn't connect to your Cloud Storage bucket \u{1F614}
2424
+
2425
+ The error from Google is: '${err.message}' \u{1F440}
2426
+
2427
+ Could you troubleshoot your Cloud Storage configuration in Google Cloud and sync again the source? \u{1F64F}`,
2428
+ `Got error from cloud storage lib`
2429
+ );
2430
+ }
2431
+ throw err;
2432
+ }
2433
+ };
2434
+
2435
+ // src/targets/bigquery/service/dbSync.ts
2436
+ var import_chunk = __toESM(require("lodash/chunk.js"));
2437
+ var import_async_mutex2 = require("async-mutex");
2438
+ var semaphore2 = new import_async_mutex2.Semaphore(1);
2439
+ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2440
+ config;
2441
+ bqClient;
2442
+ bqDataset;
2443
+ bqTable;
2444
+ bqStagingTable;
2445
+ tableName;
2446
+ stagingTableName;
2447
+ sqlTableId;
2448
+ sqlStagingTableId;
2449
+ constructor(config, streamId, database, schema, table, renameColumnStore) {
2450
+ super(
2451
+ streamId,
2452
+ database,
2453
+ schema,
2454
+ table,
2455
+ renameColumnStore
2456
+ );
2457
+ this.config = config;
2458
+ this.bqClient = BqClientHolder.client(config);
2459
+ this.bqDataset = this.bqClient.dataset(schema, { location: config.location || "EU" });
2460
+ this.bqTable = this.bqDataset.table(table, { location: config.location || "EU" });
2461
+ this.tableName = table;
2462
+ this.stagingTableName = `${table}_temp`;
2463
+ this.bqStagingTable = this.bqDataset.table(this.stagingTableName, { location: config.location || "EU" });
2464
+ this.sqlTableId = `${schema}.${table}`;
2465
+ this.sqlStagingTableId = `${this.sqlTableId}_temp`;
2466
+ }
2467
+ // Implementation of abstract classes
2468
+ getSerializedRecord = (record) => {
2469
+ return JSON.stringify(record) + `
2470
+ `;
2471
+ };
2472
+ safeColumnName = safeColumnName;
2473
+ getWarehouseTypeFromJSONSchema = (definition) => {
2474
+ const type = definition.type;
2475
+ const format6 = definition.format;
2476
+ let typeArr;
2477
+ if (type instanceof Array) {
2478
+ typeArr = type;
2479
+ } else {
2480
+ typeArr = [type];
2481
+ }
2482
+ if (format6) {
2483
+ switch (format6) {
2484
+ case "date-time":
2485
+ return "TIMESTAMP";
2486
+ case "json":
2487
+ return "STRING";
2488
+ default:
2489
+ throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${format6}`);
2490
+ }
2491
+ } else if (typeArr.includes("number")) {
2492
+ return "NUMERIC";
2493
+ } else if (typeArr.includes("integer") && type.includes("string")) {
2494
+ return "NUMERIC";
2495
+ } else if (typeArr.includes("integer")) {
2496
+ return "INTEGER";
2497
+ } else if (typeArr.includes("boolean")) {
2498
+ return "BOOLEAN";
2499
+ } else if (typeArr.includes("string")) {
2500
+ return "STRING";
2501
+ } else if (typeArr.includes("array")) {
2502
+ const items = definition.items;
2503
+ if (items) {
2504
+ return this.getWarehouseTypeFromJSONSchema(items);
2505
+ }
2506
+ throw new Error(`StreamId: ${this.streamId} - Unsupported array schema field definition: ${JSON.stringify(definition)}`);
2507
+ } else {
2508
+ throw new Error(`StreamId: ${this.streamId} - Unsupported schema field definition: ${JSON.stringify(definition)}`);
2509
+ }
2510
+ };
2511
+ getMergeQueries = () => {
2512
+ const primaryKeyCondition = this.primaryKeys.map((pk) => {
2513
+ return `\`sourced\`.\`${pk}\` = \`destination\`.\`${pk}\``;
2514
+ }).join(` AND `);
2515
+ const primaryKeys = this.primaryKeys.map((pk) => `\`${pk}\``).join(` ,`);
2516
+ const columnUnsafeToSafeMapping = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2517
+ const columns = Object.keys(columnUnsafeToSafeMapping).map((column) => {
2518
+ return {
2519
+ isUnsafe: true,
2520
+ colName: column
2521
+ };
2522
+ });
2523
+ const columnsChunks = (0, import_chunk.default)(columns, 300).map((chunk2) => {
2524
+ this.primaryKeys.forEach((pk) => {
2525
+ if (!chunk2.map((c) => columnUnsafeToSafeMapping[c.colName]).includes(pk)) {
2526
+ chunk2.push({
2527
+ isUnsafe: false,
2528
+ colName: pk
2529
+ });
2530
+ }
2531
+ });
2532
+ return chunk2;
2533
+ });
2534
+ return columnsChunks.map((chunk2) => {
2535
+ const setValues = chunk2.map((column) => {
2536
+ if (column.isUnsafe) {
2537
+ return `\`destination\`.\`${columnUnsafeToSafeMapping[column.colName]}\` = \`sourced\`.\`${columnUnsafeToSafeMapping[column.colName]}\``;
2538
+ } else {
2539
+ return `\`destination\`.\`${column.colName}\` = \`sourced\`.\`${column.colName}\``;
2540
+ }
2541
+ }).join(`, `);
2542
+ const renamedCols = chunk2.map((column) => {
2543
+ if (column.isUnsafe) {
2544
+ return `\`${columnUnsafeToSafeMapping[column.colName]}\``;
2545
+ } else {
2546
+ return `\`${column.colName}\``;
2547
+ }
2548
+ }).join(`, `);
2549
+ const query = `
2550
+ MERGE ${this.sqlTableId} destination
2551
+ USING (
2552
+ WITH all_staging_rows AS (
2553
+ SELECT *
2554
+ FROM ${this.sqlStagingTableId}
2555
+ ),
2556
+ numbered_rows AS (
2557
+ SELECT *,
2558
+ ROW_NUMBER() OVER (PARTITION BY ${primaryKeys}) as _wly_row_nb,
2559
+ COUNT(1) OVER (PARTITION BY ${primaryKeys}) AS _wly_partition_size,
2560
+ FROM all_staging_rows
2561
+ ),
2562
+ staging_deduped AS (
2563
+ SELECT * EXCEPT(_wly_partition_size, _wly_row_nb)
2564
+ FROM numbered_rows
2565
+ WHERE _wly_partition_size = _wly_row_nb
2566
+ )
2567
+ SELECT *
2568
+ FROM staging_deduped
2569
+ ) AS sourced
2570
+ ON ${primaryKeyCondition}
2571
+ WHEN MATCHED THEN
2572
+ UPDATE SET ${setValues}
2573
+ WHEN NOT MATCHED THEN
2574
+ INSERT (${renamedCols}) VALUES (${renamedCols});
2575
+ `;
2576
+ return query;
2577
+ });
2578
+ };
2579
+ getReplaceQueries = () => {
2580
+ return [
2581
+ `TRUNCATE TABLE ${this.sqlTableId};`,
2582
+ ...this.getMergeQueries()
2583
+ ];
2584
+ };
2585
+ createDatabaseAndSchemaIfNotExists = async (retryCount) => {
2586
+ try {
2587
+ await semaphore2.runExclusive(async () => {
2588
+ const [exists] = await this.bqDataset.exists();
2589
+ if (!exists) {
2590
+ logger.info(`\u{1F423} Creating dataset: ${this.bqDataset.id} on BigQuery`);
2591
+ await this.bqDataset.create();
2592
+ } else {
2593
+ const [metadata] = await this.bqDataset.getMetadata();
2594
+ const alreadyExistinglocation = metadata.location;
2595
+ if (this.config.location && this.config.location !== alreadyExistinglocation) {
2596
+ await haltAndCatchFire(
2597
+ `unknown`,
2598
+ `A BigQuery dataset called '${this.bqDataset.id}' already exists in location: '${alreadyExistinglocation}' \u{1F605}
2599
+
2600
+ This connector is configured to load data into the location '${this.config.location}' which is not possible as 2 datasets sharing the same name can't exist in different location on Google Cloud \u{1F63F}
2601
+
2602
+ To fix this issue, could you either: a. Delete the already existing dataset? b. Change the configured destination schema name to avoid the name conflict? \u{1F64F}
2603
+
2604
+ Please relaunch the connector once it's done to fix the issue \u{1F917}
2605
+ `,
2606
+ `
2607
+ We have a conflict between an already existing dataset in location ${alreadyExistinglocation} and the configured location: ${this.config.location}
2608
+ `
2609
+ );
2610
+ throw Error();
2611
+ }
2612
+ logger.info(`\u{1F44B} Dataset: ${this.bqDataset.id} already exists on BigQuery, reusing it.`);
2613
+ }
2614
+ });
2615
+ } catch (err) {
2616
+ if (err.code === 400) {
2617
+ await haltAndCatchFire(
2618
+ `unauthorized`,
2619
+ `We can't connect to your BigQuery account \u{1F614}
2620
+
2621
+ Here is the message from BigQuery: ${err.message}
2622
+
2623
+ Can you check that your Warehouse credentials and Source target schema are properly configured and try to sync again the source? \u{1F64F}
2624
+ `,
2625
+ `Got error when trying to get Dataset from BigQuery: ${err.message} - ${err.code}`
2626
+ );
2627
+ return;
2628
+ }
2629
+ if (retryCount && retryCount > this.maxRetryCount) {
2630
+ logger.error(`StreamId: ${this.streamId} - Error when checking if dataset already exists. ${err.message} - ${err.code}`);
2631
+ gracefulExit(1);
2632
+ } else {
2633
+ logger.error(`StreamId: ${this.streamId} - Error when checking if dataset already exists. Retrying... ${err.message} - ${err.code}`);
2634
+ await new Promise((resolve, reject) => setTimeout(resolve, 1e3 * retryCount));
2635
+ await this.createDatabaseAndSchemaIfNotExists(retryCount + 1);
2636
+ }
2637
+ }
2638
+ return Promise.resolve();
2639
+ };
2640
+ createTable = async () => {
2641
+ try {
2642
+ const tableName = this.tableName;
2643
+ const schema = this.generateBigquerySchema();
2644
+ logger.info(`\u{1F423} Creating table: ${tableName} on BigQuery`);
2645
+ const createTableOptions = {
2646
+ schema
2647
+ };
2648
+ await this.bqDataset.createTable(
2649
+ tableName,
2650
+ createTableOptions
2651
+ );
2652
+ return;
2653
+ } catch (err) {
2654
+ throw new Error(`StreamId: ${this.streamId} - Issue when creating table in BigQuery
2655
+
2656
+ Error: ${err.message}
2657
+ Stack: ${err.stack}`);
2658
+ }
2659
+ };
2660
+ addColumns = async (tableFields) => {
2661
+ if (tableFields.length > 0) {
2662
+ logger.info(`\u{1F195} Stream: ${this.streamId} - Adding following columns in BigQuery schema: ${JSON.stringify(tableFields)}`);
2663
+ const [metadata] = await this.bqTable.getMetadata();
2664
+ const bgTableFields = tableFields.map((tblField) => {
2665
+ return this.getBigqueryTableFieldFromInternalSchemaFieldDefinition(tblField.name, tblField.definition);
2666
+ }).filter((fld) => {
2667
+ return !!fld;
2668
+ });
2669
+ const schema = metadata.schema;
2670
+ const new_schema = {
2671
+ ...schema,
2672
+ fields: schema.fields.concat(bgTableFields)
2673
+ };
2674
+ metadata.schema = new_schema;
2675
+ await this.bqTable.setMetadata(metadata);
2676
+ return;
2677
+ }
2678
+ };
2679
+ createStagingArea = async () => {
2680
+ try {
2681
+ const tableName = this.stagingTableName;
2682
+ const [exists] = await this.bqDataset.table(tableName).exists();
2683
+ if (exists) {
2684
+ logger.info(`\u{1F474} Temporary table ${tableName} already exists, it should be a relic form the past.
2685
+ Dropping it.`);
2686
+ await this.deleteStagingArea();
2687
+ }
2688
+ const schema = this.generateBigquerySchema();
2689
+ const expirationTime = (0, import_dayjs6.default)().add(1, "day").valueOf().toString();
2690
+ const createTableOptions = {
2691
+ schema,
2692
+ expirationTime
2693
+ };
2694
+ logger.info(`\u{1F423} Creating staging table: ${tableName} on BigQuery`);
2695
+ await this.bqDataset.createTable(
2696
+ tableName,
2697
+ createTableOptions
2698
+ );
2699
+ return;
2700
+ } catch (err) {
2701
+ throw new Error(`StreamId: ${this.streamId} - Issue when creating table in BigQuery
2702
+
2703
+ Error: ${err.message}
2704
+ Stack: ${err.stack}`);
2705
+ }
2706
+ };
2707
+ loadStreamInStagingArea = async (localFilePath) => {
2708
+ try {
2709
+ const file = await uploadFileInBucket(
2710
+ this.streamId,
2711
+ this.config.loading_deck_gcs_bucket_name,
2712
+ this.config.connector_id,
2713
+ localFilePath
2714
+ );
2715
+ const metadata = {
2716
+ sourceFormat: "NEWLINE_DELIMITED_JSON",
2717
+ writeDisposition: "WRITE_TRUNCATE"
2718
+ };
2719
+ await this.loadGCSFileInTable(this.sqlStagingTableId, file, metadata);
2720
+ } catch (err) {
2721
+ logger.error(`StreamId: ${this.streamId} - Error while uploading stream.`);
2722
+ throw err;
2723
+ }
2724
+ };
2725
+ deleteStagingArea = () => {
2726
+ const query = `DROP TABLE IF EXISTS ${this.sqlStagingTableId};`;
2727
+ return this.runQueriesWithRetry([query]);
2728
+ };
2729
+ getTablesInSchema = async () => {
2730
+ const [bqTables] = await this.bqDataset.getTables();
2731
+ const foundTables = bqTables.filter((tbl) => {
2732
+ return !!tbl.id;
2733
+ }).map((bqTable) => {
2734
+ return {
2735
+ tableName: bqTable.id
2736
+ };
2737
+ });
2738
+ return foundTables;
2739
+ };
2740
+ getTableColumnsFromWarehouse = async () => {
2741
+ const [table] = await this.bqTable.getMetadata();
2742
+ return table.schema.fields;
2743
+ };
2744
+ runQueries = async (queries) => {
2745
+ const sqlQuery = `
2746
+ BEGIN
2747
+ ${queries.join(`
2748
+ `)}
2749
+ END;
2750
+ `;
2751
+ await this.bqClient.query(sqlQuery);
2752
+ };
2753
+ // Specific methods
2754
+ /**
2755
+ *
2756
+ * Generate a TableField definition object that can be passed to BigQuery APIs to define a column on a table
2757
+ *
2758
+ * @param colName
2759
+ * @param schemaFieldDefinition
2760
+ * @returns
2761
+ */
2762
+ getBigqueryTableFieldFromInternalSchemaFieldDefinition = (colName, schemaFieldDefinition) => {
2763
+ const name = colName;
2764
+ if (!schemaFieldDefinition) {
2765
+ throw new Error(`StreamId: ${this.streamId} - Schema is unknown for colName=${colName}, so we can't infer the proper BigQuery definition for this column.`);
2766
+ }
2767
+ const type = schemaFieldDefinition.type;
2768
+ let typeArr;
2769
+ if (type instanceof Array) {
2770
+ typeArr = type;
2771
+ } else {
2772
+ typeArr = [type];
2773
+ }
2774
+ if (typeArr.includes("array")) {
2775
+ return {
2776
+ name,
2777
+ mode: "REPEATED",
2778
+ type: this.getWarehouseTypeFromJSONSchema(schemaFieldDefinition)
2779
+ };
2780
+ } else {
2781
+ let nullableMode = "NULLABLE";
2782
+ return {
2783
+ name,
2784
+ mode: nullableMode,
2785
+ type: this.getWarehouseTypeFromJSONSchema(schemaFieldDefinition)
2786
+ };
2787
+ }
2788
+ };
2789
+ generateBigquerySchema = () => {
2790
+ const unsafeToSafeMappingColumnsFromColumnStore = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2791
+ if (!unsafeToSafeMappingColumnsFromColumnStore) {
2792
+ throw new Error(`StreamId: ${this.streamId} - There is no unsafe->safe column mapping`);
2793
+ }
2794
+ return Object.keys(unsafeToSafeMappingColumnsFromColumnStore).reduce((acc, unsafeKey) => {
2795
+ const renamedColName = this.renamedColumnStore.getColumnTranslation(this.streamId, unsafeKey);
2796
+ const bigqueryTableField = this.getBigqueryTableFieldFromInternalSchemaFieldDefinition(renamedColName, this.flattenedSchema[unsafeKey]);
2797
+ if (bigqueryTableField) {
2798
+ acc.push(bigqueryTableField);
2799
+ }
2800
+ return acc;
2801
+ }, []);
2802
+ };
2803
+ loadGCSFileInTable = async (tableName, file, metadata) => {
2804
+ try {
2805
+ const [job] = await this.bqStagingTable.load(file, metadata);
2806
+ logger.info(`\u{1F69A} Loaded GCS file \`${file.id}\` in BigQuery table: ${tableName} with Job: \`${job.id}\``);
2807
+ logger.debug(`Upload Stats: ${JSON.stringify(job.statistics)}`);
2808
+ const errors2 = job.status?.errors;
2809
+ if (errors2 && errors2.length > 0) {
2810
+ throw errors2;
2811
+ }
2812
+ } catch (err) {
2813
+ logger.error(err);
2814
+ throw new Error(`StreamId: ${this.streamId} - Issue when loading file in BigQuery table | fileName: ${file.name}, tableName: ${tableName}
2815
+
2816
+ Error: ${err.message}
2817
+ Stack: ${err.stack}`);
2818
+ }
2819
+ };
2820
+ };
2821
+
2822
+ // src/targets/bigquery/main.ts
2823
+ var BigQueryTarget = class extends ITarget {
2824
+ constructor(config, stateProvider) {
2825
+ super(config, stateProvider);
2826
+ this.renameColumnStore.setSafeColumnNameConverter(this.safeColumnNameConverter);
2827
+ }
2828
+ static requiredConfigKeys = [
2829
+ "schema",
2830
+ "project_id",
2831
+ "loading_deck_gcs_bucket_name"
2832
+ ];
2833
+ newDBSyncInstance = (streamId, database, schema, table) => {
2834
+ return new BigQueryDBSync(
2835
+ this.config,
2836
+ streamId,
2837
+ database,
2838
+ schema,
2839
+ table,
2840
+ this.renameColumnStore
2841
+ );
2842
+ };
2843
+ genTableName = safeTableName;
2844
+ safeColumnNameConverter = safeColumnName;
2845
+ validateDateRange = validateDateRange;
2846
+ convertNumberIntoDecimal = convertNumberIntoDecimal;
2847
+ };
2848
+
2849
+ // src/sdk/service/gcs.ts
2850
+ var import_storage2 = require("@google-cloud/storage");
2851
+ var import_util3 = require("util");
2852
+ var getStorage = () => new import_storage2.Storage({ retryOptions: { autoRetry: true, maxRetries: 20 } });
2853
+ var readObjectAsString = async (bucketName, objectPath) => {
2854
+ try {
2855
+ const storage = getStorage();
2856
+ const bucketRef = storage.bucket(bucketName);
2857
+ await bucketRef.get({ autoCreate: false });
2858
+ const fileRef = bucketRef.file(objectPath);
2859
+ const [exists] = await fileRef.exists();
2860
+ if (!exists) {
2861
+ throw new Error(`GCS object not found: gs://${bucketName}/${objectPath}`);
2862
+ }
2863
+ const [contents] = await fileRef.download();
2864
+ return contents.toString("utf8");
2865
+ } catch (err) {
2866
+ throw new Error((0, import_util3.format)(`error reading GCS object gs://${bucketName}/${objectPath}, err: %s`, err?.message));
2867
+ }
2868
+ };
2869
+ var writeStringObject = async (bucketName, objectPath, contents) => {
2870
+ try {
2871
+ const storage = getStorage();
2872
+ const bucketRef = storage.bucket(bucketName);
2873
+ await bucketRef.get({ autoCreate: false });
2874
+ const fileRef = bucketRef.file(objectPath);
2875
+ await fileRef.save(contents, { contentType: "application/json" });
2876
+ logger.info(`\u{1F5F3} Uploaded object to gs://${bucketName}/${objectPath}`);
2877
+ } catch (err) {
2878
+ throw new Error((0, import_util3.format)(`error writing GCS object gs://${bucketName}/${objectPath}, err: %s`, err?.message));
2879
+ }
2880
+ };
2881
+
2882
+ // src/state-providers/gcs/main.ts
2883
+ var import_util4 = require("util");
2884
+ var GCSStateProvider = class {
2885
+ bucketName;
2886
+ connectorId;
2887
+ constructor(connectorId, bucketName) {
2888
+ this.connectorId = connectorId;
2889
+ this.bucketName = bucketName;
2890
+ }
2891
+ getObjectPath() {
2892
+ return `${this.connectorId}/state.json`;
2893
+ }
2894
+ async getState() {
2895
+ const objectPath = this.getObjectPath();
2896
+ try {
2897
+ logger.info(`\u{1F4C1} Loading state from gs://${this.bucketName}/${objectPath}`);
2898
+ const raw = await readObjectAsString(this.bucketName, objectPath);
2899
+ const parsed = JSON.parse(raw);
2900
+ return { state: parsed };
2901
+ } catch (err) {
2902
+ logger.warn((0, import_util4.format)(`error loading state from gs://${this.bucketName}/${objectPath}, will start with empty state, err: %s`, err?.message));
2903
+ return { state: void 0 };
2904
+ }
2905
+ }
2906
+ async writeState(state) {
2907
+ const objectPath = this.getObjectPath();
2908
+ try {
2909
+ logger.info(`\u{1F4DD} Writing state to gs://${this.bucketName}/${objectPath}`);
2910
+ await writeStringObject(this.bucketName, objectPath, state);
2911
+ } catch (err) {
2912
+ logger.error((0, import_util4.format)(`\u{1F4A5} Error while writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
2913
+ throw new Error((0, import_util4.format)(`error writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
2914
+ }
2915
+ }
2916
+ };
2819
2917
  // Annotate the CommonJS export names for ESM import in node:
2820
2918
  0 && (module.exports = {
2821
2919
  ALL_STREAM_ID_KEYWORD,
2822
2920
  API_CALLS_METRIC_NAME,
2823
2921
  Authenticator,
2824
2922
  BATCH_INTERVAL_MS,
2825
- Catalog,
2923
+ BigQueryTarget,
2826
2924
  CounterMetric,
2827
2925
  DEFAULT_MAX_CONCURRENT_STREAMS,
2828
2926
  EXECUTION_TIME_METRIC_NAME,
2927
+ GCSStateProvider,
2829
2928
  ITarget,
2830
2929
  MissingFieldInSchemaError,
2831
2930
  MissingSchemaError,
2832
- RESTStreamV2,
2931
+ RESTStream,
2833
2932
  ROWS_SYNCED_METRIC_NAME,
2834
2933
  RecordMessage,
2835
2934
  RenameColumnStore,
2935
+ ReplicationMethod,
2836
2936
  ReplicationMethodMessage,
2837
- Resolver,
2838
2937
  SchemaMessage,
2839
2938
  SchemaValidationError,
2840
2939
  StateMessage,
2841
2940
  StateService,
2842
- StreamMetadata,
2843
- StreamV2,
2941
+ Stream,
2844
2942
  StreamWarehouseSyncService,
2845
2943
  Tap,
2846
2944
  _greaterThan,
2847
2945
  addWhalyFields,
2848
- checkRequiredConfigKeys,
2849
2946
  createTemporaryFileStream,
2850
2947
  extractStateForStream,
2851
2948
  finalizeStateProgressMarkers,
2852
- findRelatedRelationships,
2853
2949
  flattenSchema,
2854
2950
  getAllMetrics,
2855
2951
  getAxiosInstance,
@@ -2864,15 +2960,12 @@ var ITarget = class _ITarget {
2864
2960
  incrementStreamState,
2865
2961
  loadJson,
2866
2962
  loadSchema,
2867
- loadShoreConfig,
2868
2963
  logger,
2869
2964
  postFormDataApiCall,
2870
2965
  postJSONApiCall,
2871
2966
  postUrlEncodedApiCall,
2872
2967
  printMemoryFootprint,
2873
2968
  removeParasiteProperties,
2874
- safePath,
2875
- writeCatalogFile,
2876
- writeStateFile
2969
+ safePath
2877
2970
  });
2878
2971
  //# sourceMappingURL=index.js.map