@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.mjs CHANGED
@@ -2,243 +2,10 @@
2
2
  import { readFileSync } from "fs";
3
3
 
4
4
  // src/sdk/service/logger.ts
5
- import * as winston2 from "winston";
6
-
7
- // src/sdk/models/resolver.ts
8
- var Resolver = class {
9
- constructor() {
10
- }
11
- };
12
-
13
- // src/resolvers/local-file/services/files.ts
14
- import fs from "fs";
15
- var writeFile = (path, content) => {
16
- return Promise.resolve(fs.writeFileSync(path, content));
17
- };
18
- var readAndParseJSONFile = (path) => {
19
- if (!fs.existsSync(path)) {
20
- throw new Error(`File \`${path}\` wasn't found at: ${process.cwd()}`);
21
- }
22
- const raw = fs.readFileSync(path);
23
- const parsed = JSON.parse(raw.toString());
24
- return parsed;
25
- };
26
-
27
- // src/resolvers/local-file/hook/schemaHook.ts
28
- import { appendFile } from "fs-extra";
29
- var LocalSchemaHook = class {
30
- async writeSchema(input) {
31
- await appendFile("./schema.tmp", JSON.stringify(input) + "\n");
32
- }
33
- };
34
-
35
- // src/resolvers/local-file/resolver.ts
36
- import winston from "winston";
37
-
38
- // src/sdk/service/metric.ts
39
- var metricStore = {};
40
- var ALL_STREAM_ID_KEYWORD = "all";
41
- var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
42
- var API_CALLS_METRIC_NAME = "api_calls_count";
43
- var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
44
- var getCounterMetrics = (name, streamIds) => {
45
- return streamIds.map((streamId) => {
46
- return getCounterMetric(name, streamId);
47
- });
48
- };
49
- var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
50
- if (!metricStore[streamId]) {
51
- metricStore[streamId] = {};
52
- }
53
- const streamMetricStore = metricStore[streamId];
54
- if (!streamMetricStore[name]) {
55
- streamMetricStore[name] = new CounterMetric(name, streamId);
56
- }
57
- return streamMetricStore[name];
58
- };
59
- var getAllMetrics = () => {
60
- return Object.keys(metricStore).flatMap((streamId) => {
61
- const streamMetricStore = metricStore[streamId];
62
- if (!streamMetricStore) {
63
- return [];
64
- }
65
- return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
66
- });
67
- };
68
- var CounterMetric = class {
69
- value = 0;
70
- name;
71
- streamId;
72
- constructor(name, streamId) {
73
- this.name = name;
74
- this.streamId = streamId;
75
- }
76
- increment(inc = 1) {
77
- this.value = this.value + inc;
78
- }
79
- getStreamId() {
80
- return this.streamId;
81
- }
82
- getName() {
83
- return this.name;
84
- }
85
- getValue() {
86
- return this.value;
87
- }
88
- };
89
-
90
- // src/resolvers/local-file/resolver.ts
91
- var { combine, prettyPrint, timestamp, errors, splat } = winston.format;
92
- var configPath = "./config.json";
93
- var targetConfigPath = "./target-config.json";
94
- var catalogPath = "./catalog.json";
95
- var statePath = "./state.json";
96
- var metricsPath = "./metrics.json";
97
- var LocalFilesResolver = class extends Resolver {
98
- startVPNIfNeeded() {
99
- return Promise.resolve();
100
- }
101
- checkIfCanSync() {
102
- return Promise.resolve(true);
103
- }
104
- markSyncStarted() {
105
- return Promise.resolve();
106
- }
107
- onError(errorType, errorText, errorDebugText) {
108
- const path = `./error.json`;
109
- return writeFile(path, JSON.stringify({
110
- errorType,
111
- errorText,
112
- errorDebugText
113
- }));
114
- }
115
- getSchemaHooks() {
116
- const wlyHook = new LocalSchemaHook();
117
- return [wlyHook];
118
- }
119
- writeSchema() {
120
- return Promise.resolve();
121
- }
122
- constructor() {
123
- super();
124
- }
125
- getConfig(command) {
126
- logger.info(`\u{1F4C1} Using the LOCAL resolver`);
127
- const config = readAndParseJSONFile(configPath);
128
- const targetConfig = readAndParseJSONFile(targetConfigPath);
129
- const destination = process.env.SHORE__DESTINATION;
130
- if (!destination) {
131
- throw new Error(`\u274C Env variable \`SHORE__DESTINATION\` is not set.
132
- Did you forget to configure it?
133
-
134
- Possible values are: \`GOOGLE_BIGQUERY\`, \`SNOWFLAKE\``);
135
- }
136
- if (command === `READ`) {
137
- if (destination === `GOOGLE_BIGQUERY`) {
138
- const googleCredentials = process.env.GOOGLE_APPLICATION_CREDENTIALS;
139
- if (!googleCredentials) {
140
- throw new Error(`Env variable \`GOOGLE_APPLICATION_CREDENTIALS\` is not set.
141
- Did you forget to configure it?
142
-
143
- This variable should link to a file containing the proper Google Service Account credentials.
144
- `);
145
- }
146
- }
147
- const catalog = readAndParseJSONFile(catalogPath);
148
- const state = readAndParseJSONFile(statePath);
149
- return Promise.resolve({
150
- command,
151
- destination,
152
- config,
153
- targetConfig,
154
- catalog,
155
- state
156
- });
157
- } else {
158
- return Promise.resolve({
159
- command,
160
- destination,
161
- config,
162
- targetConfig
163
- });
164
- }
165
- }
166
- writeCatalog(catalog) {
167
- return writeFile(catalogPath, JSON.stringify(catalog));
168
- }
169
- writeState(state) {
170
- return writeFile(statePath, state);
171
- }
172
- markSyncFailed() {
173
- logger.info(`\u{1F622} Sync has failed.`);
174
- return Promise.resolve();
175
- }
176
- markSyncComplete() {
177
- logger.info(`\u{1F389} Sync is complete.`);
178
- return Promise.resolve();
179
- }
180
- markDiscoveryComplete() {
181
- logger.info(`\u{1F389} Discovery is complete.`);
182
- return Promise.resolve();
183
- }
184
- async flushMetrics() {
185
- const allMetrics = getAllMetrics();
186
- const metricsOuput = allMetrics.map((metric) => {
187
- return {
188
- streamId: metric.getStreamId(),
189
- name: metric.getName(),
190
- value: metric.getValue()
191
- };
192
- }).map((obj) => JSON.stringify(obj)).join(`
193
- `);
194
- return writeFile(metricsPath, metricsOuput);
195
- }
196
- updateSourceValue(optionKey, optionValue) {
197
- logger.info(`Updating source value ${optionKey} with value ${optionValue}`, { private: true });
198
- const config = readAndParseJSONFile(configPath);
199
- config[optionKey] = optionValue;
200
- return writeFile(configPath, JSON.stringify(config));
201
- }
202
- getLogFormat() {
203
- return combine(
204
- timestamp(),
205
- errors({ stack: true }),
206
- splat(),
207
- prettyPrint()
208
- );
209
- }
210
- };
211
-
212
- // src/sdk/service/resolver.ts
213
- function initResolver(resolverType) {
214
- if (resolverType === "LOCAL") {
215
- return new LocalFilesResolver();
216
- } else {
217
- throw new Error(`\u274C Unsupported configResolver: ${resolverType}`);
218
- }
219
- }
220
- var loadResolver = () => {
221
- const configResolver = process.env.SHORE__CONFIG_RESOLVER;
222
- if (!configResolver) {
223
- throw new Error(`Env variable \`SHORE__CONFIG_RESOLVER\` is not set.
224
- Did you forget to configure it?
225
-
226
- Possible values are \`LOCAL\`, \`WHALY\`.
227
- Please read the documentation to get the full description of the behavior of each.
228
- `);
229
- }
230
- if (configResolver === "WHALY" || configResolver === "LOCAL") {
231
- return initResolver(configResolver);
232
- } else {
233
- throw new Error(`Resolver: ${configResolver} is not supported.
234
- Did you properly configure \`SHORE__CONFIG_RESOLVER\`?`);
235
- }
236
- };
237
-
238
- // src/sdk/service/logger.ts
5
+ import * as winston from "winston";
239
6
  var BATCH_INTERVAL_MS = 100;
240
7
  var transports2 = {
241
- console: new winston2.transports.Console({
8
+ console: new winston.transports.Console({
242
9
  stderrLevels: [],
243
10
  level: "info"
244
11
  })
@@ -248,8 +15,14 @@ var getTransports = () => {
248
15
  transports2.console
249
16
  ];
250
17
  };
251
- var winstonLogger = winston2.createLogger({
252
- format: loadResolver().getLogFormat(),
18
+ var { combine, prettyPrint, timestamp, errors, splat } = winston.format;
19
+ var winstonLogger = winston.createLogger({
20
+ format: combine(
21
+ timestamp(),
22
+ errors({ stack: true }),
23
+ splat(),
24
+ prettyPrint()
25
+ ),
253
26
  transports: getTransports()
254
27
  });
255
28
  var isWinstonOpen = true;
@@ -278,34 +51,6 @@ var logger = {
278
51
  };
279
52
 
280
53
  // src/sdk/utils.ts
281
- var writeStateFile = (resolver, state) => {
282
- logger.info(`\u{1F4DD} Writing the state file.`);
283
- return resolver.writeState(state);
284
- };
285
- var writeCatalogFile = (resolver, catalog) => {
286
- logger.info(`\u{1F4DD} Writing the catalog`);
287
- return resolver.writeCatalog(catalog);
288
- };
289
- var loadShoreConfig = (resolver) => {
290
- const command = process.env.SHORE__COMMAND;
291
- if (!command) {
292
- throw new Error(`Env variable \`SHORE__COMMAND\` is not set.
293
- Did you forget to configure it?
294
-
295
- Possible values are: \`DISCOVER\`, \`READ\``);
296
- }
297
- if (command === `DISCOVER`) {
298
- logger.info(`\u{1F50E} Mode: discovery
299
- Shore will detect the available streams in the source and their configuration.`);
300
- } else if (command === `READ`) {
301
- logger.info(`\u{1F501} Mode: Synchronizing data
302
- Shore will extract and push data from the source to the destination.`);
303
- } else {
304
- throw new Error(`Command: \`${command}\` is not supported.
305
- Did you properly configured env variable \`SHORE__COMMAND\`?`);
306
- }
307
- return resolver.getConfig(command);
308
- };
309
54
  function readFile(fileName, filePath) {
310
55
  try {
311
56
  return readFileSync(filePath);
@@ -331,18 +76,11 @@ var loadSchema = (streamId) => {
331
76
  const schema = loadJson(`schemas/${streamId}.json`);
332
77
  return schema;
333
78
  };
334
- var checkRequiredConfigKeys = (args, requiredConfigKeys) => {
335
- requiredConfigKeys.forEach((configKey) => {
336
- if (!args[configKey]) {
337
- throw new Error(`Config is missing required key: ${configKey}`);
338
- }
339
- });
340
- };
341
79
 
342
80
  // src/sdk/service/network.ts
343
81
  import axios from "axios";
344
82
  import { URLSearchParams } from "url";
345
- import * as fs2 from "fs";
83
+ import * as fs from "fs";
346
84
  import * as qs from "qs";
347
85
  import axiosRetry, { isNetworkOrIdempotentRequestError } from "axios-retry";
348
86
  var shouldRetry = (error) => {
@@ -455,7 +193,7 @@ var getJSONApiCall = async (endpoint, config, privateLogging) => {
455
193
  };
456
194
  var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) => {
457
195
  const instance = getAxiosInstance(config.retryCount);
458
- const writer = fs2.createWriteStream(output);
196
+ const writer = fs.createWriteStream(output);
459
197
  const paramsSerializer = (params) => {
460
198
  return qs.stringify(params, { arrayFormat: "repeat" });
461
199
  };
@@ -549,6 +287,58 @@ var postUrlEncodedApiCall = async (endpoint, config, payload) => {
549
287
  }
550
288
  };
551
289
 
290
+ // src/sdk/service/metric.ts
291
+ var metricStore = {};
292
+ var ALL_STREAM_ID_KEYWORD = "all";
293
+ var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
294
+ var API_CALLS_METRIC_NAME = "api_calls_count";
295
+ var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
296
+ var getCounterMetrics = (name, streamIds) => {
297
+ return streamIds.map((streamId) => {
298
+ return getCounterMetric(name, streamId);
299
+ });
300
+ };
301
+ var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
302
+ if (!metricStore[streamId]) {
303
+ metricStore[streamId] = {};
304
+ }
305
+ const streamMetricStore = metricStore[streamId];
306
+ if (!streamMetricStore[name]) {
307
+ streamMetricStore[name] = new CounterMetric(name, streamId);
308
+ }
309
+ return streamMetricStore[name];
310
+ };
311
+ var getAllMetrics = () => {
312
+ return Object.keys(metricStore).flatMap((streamId) => {
313
+ const streamMetricStore = metricStore[streamId];
314
+ if (!streamMetricStore) {
315
+ return [];
316
+ }
317
+ return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
318
+ });
319
+ };
320
+ var CounterMetric = class {
321
+ value = 0;
322
+ name;
323
+ streamId;
324
+ constructor(name, streamId) {
325
+ this.name = name;
326
+ this.streamId = streamId;
327
+ }
328
+ increment(inc = 1) {
329
+ this.value = this.value + inc;
330
+ }
331
+ getStreamId() {
332
+ return this.streamId;
333
+ }
334
+ getName() {
335
+ return this.name;
336
+ }
337
+ getValue() {
338
+ return this.value;
339
+ }
340
+ };
341
+
552
342
  // src/sdk/service/memory.ts
553
343
  var formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
554
344
  var printMemoryFootprint = (prefix) => {
@@ -564,9 +354,6 @@ var printMemoryFootprint = (prefix) => {
564
354
 
565
355
  // src/sdk/service/exit.ts
566
356
  async function gracefulExit(exitCode) {
567
- if (exitCode !== 0) {
568
- await loadResolver().markSyncFailed();
569
- }
570
357
  const loggerFinish = new Promise((resolve, reject) => {
571
358
  logger.closeWinston(resolve);
572
359
  });
@@ -586,207 +373,15 @@ var haltAndCatchFire = async (errorType, errorText, errorDebugText) => {
586
373
  errorText,
587
374
  errorDebugText
588
375
  );
589
- const resolver = loadResolver();
590
- await resolver.onError(errorType, errorText, errorDebugText);
591
376
  await gracefulExit(0);
592
377
  };
593
378
 
594
- // src/sdk/service/relationship.ts
595
- var findRelatedRelationships = (streamId, allRels) => {
596
- return allRels.reduce((acc, rel) => {
597
- const base = {
598
- type: rel.type,
599
- from: rel.from,
600
- to: rel.to
601
- };
602
- if (rel.left === streamId) {
603
- acc.right.push({
604
- streamId: rel.right,
605
- ...base
606
- });
607
- }
608
- if (rel.right === streamId) {
609
- acc.left.push({
610
- streamId: rel.left,
611
- ...base
612
- });
613
- }
614
- return acc;
615
- }, { left: [], right: [] });
616
- };
617
-
618
- // src/sdk/models/metadata.ts
619
- var MetadataMap = class extends Map {
620
- areEquals(array1, array2) {
621
- return array1.length === array2.length && array1.every(function(value, index) {
622
- return value === array2[index];
623
- });
624
- }
625
- findExistingEntry(key) {
626
- return Array.from(super.entries()).find((entry) => {
627
- if (this.areEquals(entry[0], key)) {
628
- return true;
629
- }
630
- });
631
- }
632
- get(key) {
633
- const existingEntry = this.findExistingEntry(key);
634
- return existingEntry?.[1];
635
- }
636
- set(key, value) {
637
- const existingKey = this.findExistingEntry(key);
638
- if (existingKey) {
639
- return super.set(existingKey?.[0], value);
640
- } else {
641
- return super.set(key, value);
642
- }
643
- }
644
- };
645
- var StreamMetadata = class {
646
- metadataByBreadcrumb;
647
- constructor() {
648
- this.metadataByBreadcrumb = new MetadataMap();
649
- }
650
- fromCatalogStreamMetadata(metadataArr) {
651
- metadataArr.forEach((metadata) => {
652
- this.metadataByBreadcrumb.set(metadata.breadcrumb, metadata.metadata);
653
- });
654
- return this;
655
- }
656
- toString() {
657
- return this.metadataByBreadcrumb;
658
- }
659
- get(breadcrumb) {
660
- return this.metadataByBreadcrumb.get(breadcrumb);
661
- }
662
- write(breadcrumb, key, value) {
663
- if (value === void 0) {
664
- throw new Error(`The value you're trying to write on the metadata at breadcrumb: \`${breadcrumb}\` and key: \`${key}\` is undefined.
665
-
666
- Writing an undefined value is not a valid operation. Is there something wrong with your value?`);
667
- }
668
- if (!this.metadataByBreadcrumb.get(breadcrumb)) {
669
- this.metadataByBreadcrumb.set(breadcrumb, {});
670
- }
671
- const previousValue = this.metadataByBreadcrumb.get(breadcrumb);
672
- const newValue = {};
673
- newValue[key] = value;
674
- this.metadataByBreadcrumb.set(breadcrumb, { ...previousValue, ...newValue });
675
- return this;
676
- }
677
- getRootBreadcrumbMetadata() {
678
- const rootBreadcrumbMetadata = this.metadataByBreadcrumb.get([]);
679
- if (!rootBreadcrumbMetadata) {
680
- throw new Error(`No metadata was attached to the root breadcrumb \`[\`] while we need one to store metadata at the table level.
681
-
682
- Is your catalog properly generated?`);
683
- }
684
- return rootBreadcrumbMetadata;
685
- }
686
- isStreamSelected() {
687
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
688
- if (rootBreadcrumbMetadata.selected === false) {
689
- return false;
690
- }
691
- if (rootBreadcrumbMetadata.selected === true || rootBreadcrumbMetadata["selected-by-default"] === true) {
692
- return true;
693
- }
694
- return false;
695
- }
696
- getKeyProperties() {
697
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
698
- if (!rootBreadcrumbMetadata["table-key-properties"]) {
699
- return [];
700
- }
701
- return rootBreadcrumbMetadata["table-key-properties"];
702
- }
703
- getReplicationKey() {
704
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
705
- if (!rootBreadcrumbMetadata["replication-key"]) {
706
- return void 0;
707
- }
708
- return rootBreadcrumbMetadata["replication-key"];
709
- }
710
- getReplicationMethod() {
711
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
712
- if (!rootBreadcrumbMetadata["replication-method"]) {
713
- return void 0;
714
- }
715
- return rootBreadcrumbMetadata["replication-method"];
716
- }
717
- getForcedReplicationMethod() {
718
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
719
- if (!rootBreadcrumbMetadata["forced-replication-method"]) {
720
- return void 0;
721
- }
722
- return rootBreadcrumbMetadata["forced-replication-method"];
723
- }
724
- toList() {
725
- return Array.from(this.metadataByBreadcrumb.keys()).map((breadcrumb) => {
726
- const metadata = this.metadataByBreadcrumb.get(breadcrumb);
727
- if (!metadata) {
728
- throw new Error(`There was no metadata entry for breadcrumb: \`${breadcrumb}\` in metadata map: ${this.metadataByBreadcrumb.toString()}`);
729
- }
730
- return {
731
- breadcrumb,
732
- metadata
733
- };
734
- });
735
- }
736
- };
737
-
738
- // src/sdk/models/catalog.ts
739
- import * as _ from "lodash";
740
- var Catalog = class _Catalog {
741
- streams;
742
- static fromFile(fileName) {
743
- const catalogFile = loadJson(fileName);
744
- const catalog = new _Catalog();
745
- catalog.streams = catalogFile.streams;
746
- return catalog;
747
- }
748
- static fromCatalogFile(catalogFile) {
749
- const catalog = new _Catalog();
750
- catalog.streams = catalogFile.streams;
751
- return catalog;
752
- }
753
- getSelectedStreams() {
754
- if (!this.streams) {
755
- return [];
756
- }
757
- const directSelectedStreams = this.streams.filter((stream) => {
758
- const metadata = new StreamMetadata();
759
- metadata.fromCatalogStreamMetadata(stream.metadata);
760
- return metadata.isStreamSelected();
761
- });
762
- logger.debug(`Directly selected streams: ${directSelectedStreams.map((stream) => stream.stream).join(",")}`);
763
- const getIndirectSelectedStreams = (stream, ancestorsIds) => {
764
- const currAncestorsIds = ancestorsIds || [];
765
- const rootBreadcrumbMetadata = stream.metadata.find((metadata) => metadata.breadcrumb.length === 0);
766
- const parentStreamId = rootBreadcrumbMetadata?.metadata?.["whaly-parent-stream"];
767
- if (parentStreamId) {
768
- const parentStream = this.streams?.find((stream2) => stream2.stream === parentStreamId);
769
- if (parentStream) {
770
- currAncestorsIds.push(parentStreamId);
771
- return getIndirectSelectedStreams(parentStream, currAncestorsIds);
772
- }
773
- }
774
- return currAncestorsIds;
775
- };
776
- const indirectSelectedStreamIds = _.uniq(
777
- directSelectedStreams.flatMap((stream) => {
778
- return getIndirectSelectedStreams(stream);
779
- })
780
- );
781
- logger.debug(`Indirectly selected streams: ${indirectSelectedStreamIds.join(",")}`);
782
- const indirectSelectedStreams = indirectSelectedStreamIds.map((streamId) => {
783
- if (!directSelectedStreams.find((stream) => stream.stream === streamId)) {
784
- return this.streams?.find((stream) => stream.stream === streamId);
785
- }
786
- }).filter((stream) => !!stream);
787
- return directSelectedStreams.concat(indirectSelectedStreams);
788
- }
789
- };
379
+ // src/sdk/models/replication.ts
380
+ var ReplicationMethod = /* @__PURE__ */ ((ReplicationMethod2) => {
381
+ ReplicationMethod2["INCREMENTAL"] = "INCREMENTAL";
382
+ ReplicationMethod2["FULL_TABLE"] = "FULL_TABLE";
383
+ return ReplicationMethod2;
384
+ })(ReplicationMethod || {});
790
385
 
791
386
  // src/sdk/models/messages.ts
792
387
  var RecordMessage = class {
@@ -846,7 +441,6 @@ var SchemaMessage = class {
846
441
  displayLabel;
847
442
  description;
848
443
  propertiesMetadata;
849
- relationships;
850
444
  constructor(opts) {
851
445
  const {
852
446
  stream,
@@ -855,7 +449,6 @@ var SchemaMessage = class {
855
449
  bookmarkProperties,
856
450
  displayLabel,
857
451
  description,
858
- relationships,
859
452
  propertiesMetadata
860
453
  } = opts;
861
454
  this.stream = stream;
@@ -864,7 +457,6 @@ var SchemaMessage = class {
864
457
  this.bookmarkProperties = bookmarkProperties;
865
458
  this.displayLabel = displayLabel;
866
459
  this.description = description;
867
- this.relationships = relationships;
868
460
  this.propertiesMetadata = propertiesMetadata;
869
461
  }
870
462
  toString() {
@@ -874,8 +466,7 @@ var SchemaMessage = class {
874
466
  schema: this.schema,
875
467
  key_properties: this.keyProperties,
876
468
  label: this.displayLabel,
877
- description: this.description,
878
- relationships: this.relationships
469
+ description: this.description
879
470
  };
880
471
  if (this.bookmarkProperties) {
881
472
  result["bookmark_properties"] = this.bookmarkProperties;
@@ -885,27 +476,27 @@ var SchemaMessage = class {
885
476
  };
886
477
 
887
478
  // src/sdk/models/state.ts
888
- import moment from "moment";
479
+ import dayjs from "dayjs";
889
480
 
890
481
  // src/sdk/constants/date.ts
891
482
  var defaultDateTimeFormat = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ";
892
483
 
893
484
  // src/sdk/models/state.ts
894
- import { format } from "util";
485
+ import { format as format2 } from "util";
895
486
  var PROGRESS_MARKERS = "progressMarkers";
896
487
  var PROGRESS_MARKER_NOTE = "Note";
897
488
  var SIGNPOST_MARKER = "replicationKeySignpost";
898
489
  var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
899
490
  let newRkValue = latestRecord[replicationKey];
900
491
  if (!newRkValue) {
901
- throw new Error(format(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
492
+ throw new Error(format2(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
902
493
  }
903
- let newRkValueMoment = moment(newRkValue);
494
+ let newRkValueMoment = dayjs(newRkValue);
904
495
  let prevRkValue;
905
496
  let prevRkValueMoment;
906
497
  if (isSorted) {
907
498
  prevRkValue = state["replicationKeyValue"];
908
- prevRkValueMoment = moment(prevRkValue);
499
+ prevRkValueMoment = dayjs(prevRkValue);
909
500
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
910
501
  throw new Error(
911
502
  `Unsorted data detected in stream. Latest value '${newRkValue}' is
@@ -920,24 +511,24 @@ var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
920
511
  state[PROGRESS_MARKERS] = marker;
921
512
  }
922
513
  prevRkValue = state[PROGRESS_MARKERS]["replicationKeyValue"];
923
- prevRkValueMoment = moment(prevRkValue || state["replicationKeyValue"]);
514
+ prevRkValueMoment = dayjs(prevRkValue || state["replicationKeyValue"]);
924
515
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
925
516
  newRkValueMoment = prevRkValueMoment;
926
517
  newRkValue = prevRkValue;
927
518
  }
928
519
  }
929
520
  const signpostMarker = state[SIGNPOST_MARKER];
930
- const replicationKeySignpostMoment = moment(signpostMarker);
521
+ const replicationKeySignpostMoment = dayjs(signpostMarker);
931
522
  if (replicationKeySignpostMoment && replicationKeySignpostMoment.isBefore(newRkValueMoment)) {
932
523
  newRkValueMoment = replicationKeySignpostMoment;
933
524
  newRkValue = replicationKeySignpostMoment.format(defaultDateTimeFormat);
934
525
  }
935
526
  if (isSorted === false) {
936
527
  state[PROGRESS_MARKERS]["replicationKey"] = replicationKey;
937
- state[PROGRESS_MARKERS]["replicationKeyValue"] = moment(newRkValueMoment).format(defaultDateTimeFormat);
528
+ state[PROGRESS_MARKERS]["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
938
529
  } else {
939
530
  state["replicationKey"] = replicationKey;
940
- state["replicationKeyValue"] = moment(newRkValueMoment).format(defaultDateTimeFormat);
531
+ state["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
941
532
  }
942
533
  return state;
943
534
  };
@@ -959,7 +550,7 @@ var finalizeStateProgressMarkers = (state) => {
959
550
  return state;
960
551
  };
961
552
  var _greaterThan = (aValue, bValue) => {
962
- return moment(aValue).isAfter(moment(bValue));
553
+ return dayjs(aValue).isAfter(dayjs(bValue));
963
554
  };
964
555
  var extractStateForStream = (state, streamId) => {
965
556
  if (!state.bookmarks) {
@@ -982,26 +573,16 @@ var StateService = class {
982
573
  }
983
574
  return this.instance;
984
575
  }
985
- // To be deprecated
986
- setBookmark(streamId, ts) {
987
- this.bookmarks[streamId] = ts.format(defaultDateTimeFormat);
988
- }
989
- setBookmarkV2(streamId, streamState) {
576
+ setBookmark(streamId, streamState) {
990
577
  this.bookmarks[streamId] = streamState;
991
578
  }
992
- setBookmarkSignpostV2(streamId, signpostValue) {
579
+ setBookmarkSignpost(streamId, signpostValue) {
993
580
  if (!this.bookmarks[streamId]) {
994
581
  this.bookmarks[streamId] = {};
995
582
  }
996
583
  this.bookmarks[streamId][SIGNPOST_MARKER] = signpostValue;
997
584
  }
998
585
  getBookmark(streamId) {
999
- if (!this.bookmarks[streamId]) {
1000
- return moment(0);
1001
- }
1002
- return moment(this.bookmarks[streamId]);
1003
- }
1004
- getBookmarkV2(streamId) {
1005
586
  if (this.bookmarks[streamId]) {
1006
587
  return this.bookmarks[streamId];
1007
588
  } else {
@@ -1041,7 +622,7 @@ import axios2 from "axios";
1041
622
  import * as qs2 from "qs";
1042
623
 
1043
624
  // src/sdk/models/tap/stream.ts
1044
- import moment2 from "moment";
625
+ import dayjs2 from "dayjs";
1045
626
 
1046
627
  // src/sdk/helpers/typing.ts
1047
628
  var isDatetimeType = (typeDef) => {
@@ -1064,186 +645,56 @@ var isDatetimeType = (typeDef) => {
1064
645
  };
1065
646
 
1066
647
  // src/sdk/models/tap/stream.ts
1067
- import * as _2 from "lodash";
1068
- import { Promise as BPromise2 } from "bluebird";
648
+ import cloneDeep2 from "lodash/cloneDeep.js";
649
+ import Bluebird2 from "bluebird";
1069
650
 
1070
651
  // src/sdk/models/tap/tap.ts
1071
- import { Promise as BPromise } from "bluebird";
1072
- import { DepGraph } from "dependency-graph";
652
+ import cloneDeep from "lodash/cloneDeep.js";
653
+ import Bluebird from "bluebird";
1073
654
  var DEFAULT_MAX_CONCURRENT_STREAMS = 5;
1074
655
  var Tap = class {
1075
- _target;
1076
- _config;
1077
- _streams;
1078
- _concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
1079
- constructor(target, config) {
1080
- this._streams = [];
1081
- this._target = target;
1082
- this._config = config;
1083
- }
1084
- discover = async (existingCatalog) => {
1085
- logger.debug("Loading schemas");
1086
- const streamDepGraph = new DepGraph();
1087
- const buildDependencyGraph = (streams2, parentId) => {
1088
- streams2.forEach((s) => {
1089
- streamDepGraph.addNode(s.streamId, s);
1090
- if (parentId) {
1091
- streamDepGraph.addDependency(s.streamId, parentId);
1092
- }
1093
- if (s.children) {
1094
- return buildDependencyGraph(s.children, s.streamId);
1095
- }
1096
- });
1097
- };
1098
- buildDependencyGraph(this._streams);
1099
- const discoveredStreamCatalogEntries = await BPromise.map(
1100
- streamDepGraph.overallOrder(),
1101
- async (streamId) => {
1102
- logger.info(`\u2728 Loading schema for ${streamId}`);
1103
- const stream = streamDepGraph.getNodeData(streamId);
1104
- const schema = await stream.getSchema();
1105
- const parentStreamIds = streamDepGraph.directDependenciesOf(streamId);
1106
- if (parentStreamIds.length > 1) {
1107
- throw new Error(`StreamId=${streamId} directly depends on more than 1 stream, e.g. ${parentStreamIds.join(",")} which is not supported!`);
1108
- }
1109
- const parentStreamId = parentStreamIds?.[0];
1110
- const { metadata } = await this.generateMetadataFromStream(stream, parentStreamId);
1111
- return {
1112
- schema,
1113
- metadata,
1114
- stream: stream.streamId,
1115
- tap_stream_id: stream.streamId
1116
- };
1117
- },
1118
- { concurrency: this._concurrency }
1119
- );
1120
- const existingCatalogStreamIds = (existingCatalog?.streams || []).map((entry) => {
1121
- return entry.stream;
1122
- });
1123
- const newlyDiscoveredStreamCatalogEntries = discoveredStreamCatalogEntries.filter((entry) => {
1124
- if (existingCatalogStreamIds.includes(entry.stream)) {
1125
- return false;
1126
- } else {
1127
- return true;
1128
- }
1129
- });
1130
- logger.debug(`Newly discovered streams: %j`, newlyDiscoveredStreamCatalogEntries);
1131
- const allDiscoveredStreamIds = discoveredStreamCatalogEntries.map((entry) => {
1132
- return entry.stream;
1133
- });
1134
- const existingCatalogMinusDeletedStreams = (existingCatalog?.streams || []).filter((entry) => {
1135
- if (!allDiscoveredStreamIds.includes(entry.stream)) {
1136
- logger.debug(`Stream %s was deleted, removing it from the catalog`, entry.stream);
1137
- return false;
1138
- } else {
1139
- return true;
1140
- }
1141
- });
1142
- const updatedExistingCatalog = existingCatalogMinusDeletedStreams.map((existingCatalogEntry) => {
1143
- const matchingDiscoveredCatalogEntry = discoveredStreamCatalogEntries.find((entry) => entry.stream === existingCatalogEntry.stream);
1144
- if (matchingDiscoveredCatalogEntry) {
1145
- const existingRootMetadata = existingCatalogEntry.metadata.find((metadata) => metadata.breadcrumb.length === 0);
1146
- const discoveredRootMetadata = matchingDiscoveredCatalogEntry.metadata.find((metadata) => metadata.breadcrumb.length === 0);
1147
- if (existingRootMetadata) {
1148
- const parentStreamVal = discoveredRootMetadata?.metadata["whaly-parent-stream"];
1149
- if (parentStreamVal !== void 0) {
1150
- existingRootMetadata.metadata["whaly-parent-stream"] = parentStreamVal;
1151
- }
1152
- const forcedReplicationMethodVal = discoveredRootMetadata?.metadata["forced-replication-method"];
1153
- if (forcedReplicationMethodVal !== void 0) {
1154
- existingRootMetadata.metadata["forced-replication-method"] = forcedReplicationMethodVal;
1155
- }
1156
- const forcedReplicationKeyVal = discoveredRootMetadata?.metadata["forced-replication-key"];
1157
- if (forcedReplicationKeyVal !== void 0) {
1158
- existingRootMetadata.metadata["forced-replication-key"] = forcedReplicationKeyVal;
1159
- }
1160
- const validReplicationKeysVal = discoveredRootMetadata?.metadata["valid-replication-keys"];
1161
- if (validReplicationKeysVal !== void 0) {
1162
- existingRootMetadata.metadata["valid-replication-keys"] = validReplicationKeysVal;
1163
- }
1164
- const rowCountVal = discoveredRootMetadata?.metadata["row-count"];
1165
- if (rowCountVal !== void 0) {
1166
- existingRootMetadata.metadata["row-count"] = rowCountVal;
1167
- }
1168
- }
1169
- }
1170
- return existingCatalogEntry;
1171
- });
1172
- const streams = updatedExistingCatalog.concat(newlyDiscoveredStreamCatalogEntries);
1173
- return { streams };
1174
- };
1175
- sync = async (catalog) => {
1176
- await this.initiateSync(catalog);
1177
- logger.debug(`[TAP] All streams have finished syncing. We send the [complete] signal to the target.`);
1178
- await this._target.complete();
1179
- return Promise.resolve();
1180
- };
1181
- async generateMetadataFromStream(stream, parentStreamId) {
1182
- const metadata = new StreamMetadata();
1183
- metadata.write([], "table-key-properties", stream.primaryKey);
1184
- const defaultReplicationConfig = stream.defaultReplicationConfig();
1185
- metadata.write([], "replication-method", defaultReplicationConfig.replicationMethod);
1186
- const validReplicationkeys = await stream.getValidReplicationKeys();
1187
- metadata.write([], "valid-replication-keys", validReplicationkeys);
1188
- if (defaultReplicationConfig.isForced) {
1189
- metadata.write([], "forced-replication-method", defaultReplicationConfig.replicationMethod);
1190
- if (stream.forcedReplicationKey) {
1191
- metadata.write([], "forced-replication-key", stream.forcedReplicationKey);
1192
- }
1193
- }
1194
- const rowCount = await stream.getRowCount();
1195
- if (rowCount !== void 0) {
1196
- metadata.write([], "row-count", rowCount);
1197
- }
1198
- if (stream.isSilent === true) {
1199
- metadata.write([], "whaly-is-hidden", stream.isSilent);
1200
- }
1201
- if (stream.isSilent === false) {
1202
- metadata.write([], "selected-by-default", stream.selectedByDefault);
1203
- }
1204
- if (stream.displayLabel) {
1205
- metadata.write([], "whaly-display-label", stream.displayLabel);
1206
- }
1207
- if (stream.description) {
1208
- metadata.write([], "whaly-description", stream.description);
1209
- }
1210
- if (parentStreamId !== void 0) {
1211
- metadata.write([], "whaly-parent-stream", parentStreamId);
1212
- }
1213
- return { metadata: metadata.toList() };
656
+ target;
657
+ config;
658
+ streams;
659
+ concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
660
+ stateProvider;
661
+ tapState;
662
+ constructor(target, config, stateProvider) {
663
+ this.streams = [];
664
+ this.target = target;
665
+ this.config = config;
666
+ this.stateProvider = stateProvider;
667
+ this.tapState = { bookmarks: {} };
1214
668
  }
1215
- async initiateSync(catalog) {
669
+ sync = async (options) => {
670
+ const initialState = await this.stateProvider.getState();
671
+ if (initialState.state && initialState.state.bookmarks) {
672
+ this.tapState = cloneDeep(initialState.state);
673
+ }
674
+ await this.init();
1216
675
  logger.info(`\u{1F680} Start syncing`);
1217
676
  const state = StateService.getInstance().get();
1218
677
  logger.info(`\u{1F4CD} Received state: %j`, state);
1219
- logger.debug(`\u{1F4DA} Received catalog: %j`, catalog);
1220
- const selectedStreams = catalog.getSelectedStreams();
1221
- if (selectedStreams.length === 0) {
1222
- logger.info(`\u{1F631} There is no stream to sync.
1223
-
1224
- Did you update the \`catalog.json\` to add \`"selected": true\` in the table metadata associated with the root breadcrumb (e.g. \`[]\`)`);
1225
- return;
1226
- }
1227
- const selectedStreamIds = selectedStreams.map((stream) => stream.stream);
1228
- logger.info(`\u{1F973} Syncing selected streams: %s`, selectedStreamIds.join(","));
1229
- await BPromise.map(
1230
- this._streams,
1231
- (stream) => {
1232
- if (selectedStreamIds.includes(stream.streamId)) {
1233
- const selectedStream = selectedStreams.find(((selectedStream2) => selectedStream2.stream === stream.streamId));
1234
- const rootMetadataEntry = selectedStream?.metadata.find((streamMdt) => streamMdt.breadcrumb.length === 0);
1235
- if (!rootMetadataEntry) {
1236
- throw new Error(`Can't find a catalog root metadata entry for StreamId=${stream.streamId}`);
1237
- }
1238
- stream.setRootMetadataEntry(rootMetadataEntry.metadata);
1239
- return stream.sync();
1240
- }
1241
- },
1242
- {
1243
- concurrency: this._concurrency
1244
- }
1245
- );
1246
- }
678
+ const envInclude = process.env.TAP_STREAMS ? process.env.TAP_STREAMS.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
679
+ const include = options?.include && options.include.length > 0 ? options.include : envInclude;
680
+ const exclude = new Set((options?.exclude || []).map((s) => s.trim()));
681
+ let streamsToRun = this.streams.filter((s) => !s.isSilent);
682
+ if (include && include.length > 0) {
683
+ const includeSet = new Set(include);
684
+ streamsToRun = streamsToRun.filter((s) => includeSet.has(s.streamId));
685
+ }
686
+ if (exclude.size > 0) {
687
+ streamsToRun = streamsToRun.filter((s) => !exclude.has(s.streamId));
688
+ }
689
+ const selectedStreamIds = streamsToRun.map((s) => s.streamId);
690
+ logger.info(`\u{1F973} Syncing streams: %s`, selectedStreamIds.join(","));
691
+ await Bluebird.map(streamsToRun, (stream) => {
692
+ return stream.sync();
693
+ }, { concurrency: this.concurrency });
694
+ logger.debug(`[TAP] All streams have finished syncing. We send the [complete] signal to the target.`);
695
+ await this.target.complete();
696
+ return Promise.resolve();
697
+ };
1247
698
  // Used to clean up any ressource before exit
1248
699
  end() {
1249
700
  return Promise.resolve();
@@ -1251,19 +702,13 @@ var Tap = class {
1251
702
  };
1252
703
 
1253
704
  // src/sdk/models/tap/stream.ts
1254
- import { format as format2 } from "util";
1255
- var StreamV2 = class {
705
+ import { format as format3 } from "util";
706
+ var Stream = class {
1256
707
  // Runtime values, can't be overriden
1257
- _config;
1258
- _tapName = "default";
1259
- _tapState;
1260
- _target;
1261
- // Contains the root metadata entry of the catalog stream configuration
1262
- // contains some end user schema configuration
1263
- rootMetadataEntry;
1264
- // Compile time values, can be overriden
1265
- // Replication method that can be forced in the stream implemenation. Will prevail over any user/catalog choice
1266
- forcedReplicationMethod = void 0;
708
+ config;
709
+ tapState;
710
+ target;
711
+ replicationMethod = void 0;
1267
712
  selectedByDefault = true;
1268
713
  STATE_MSG_FREQUENCY = 10;
1269
714
  streamId = "default";
@@ -1271,10 +716,9 @@ var StreamV2 = class {
1271
716
  displayLabel = void 0;
1272
717
  description = void 0;
1273
718
  primaryKey = [];
1274
- forcedReplicationKey = void 0;
719
+ replicationKey = void 0;
1275
720
  // When set, use the state from another stream for this stream
1276
721
  useStateFromStreamId = void 0;
1277
- relationships = [];
1278
722
  children = [];
1279
723
  // If true, it means that the records in this stream are sorted by replicationKey.
1280
724
  // If false, then the records are coming in an unsorted manner.
@@ -1286,11 +730,10 @@ var StreamV2 = class {
1286
730
  // Metrics configuration
1287
731
  rowsSyncedMetricsConf;
1288
732
  executionTimeMetricsConf;
1289
- constructor(tapName, config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1290
- this._tapName = tapName;
1291
- this._config = config;
1292
- this._tapState = _2.cloneDeep(state);
1293
- this._target = target;
733
+ constructor(config, tapState, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
734
+ this.config = config;
735
+ this.tapState = cloneDeep2(tapState);
736
+ this.target = target;
1294
737
  this.childConcurrency = childConcurrency;
1295
738
  this.rowsSyncedMetricsConf = {
1296
739
  isEnabled: () => !this.isSilent,
@@ -1301,9 +744,7 @@ var StreamV2 = class {
1301
744
  getStreamIds: () => [this.streamId]
1302
745
  };
1303
746
  }
1304
- setRootMetadataEntry = (rootMetadataEntry) => {
1305
- this.rootMetadataEntry = rootMetadataEntry;
1306
- };
747
+ // No root metadata: replication config is derived from stream defaults only
1307
748
  /**
1308
749
  * Sync this stream
1309
750
  * Called only for streams with no parents, aka. "root tap streams"
@@ -1323,7 +764,7 @@ var StreamV2 = class {
1323
764
  return this.flush();
1324
765
  } catch (err) {
1325
766
  if (err instanceof Error) {
1326
- throw new Error(format2(`got an error while syncing streamId=%s, err: %s`, this.streamId, err.message));
767
+ throw new Error(format3(`got an error while syncing streamId=%s, err: %s`, this.streamId, err.message));
1327
768
  }
1328
769
  throw err;
1329
770
  }
@@ -1334,7 +775,7 @@ var StreamV2 = class {
1334
775
  `\u{1F6BD} Flushing ${replicationMethod} sync of stream '${this.streamId}' + all of its children...`
1335
776
  );
1336
777
  await this._flush();
1337
- await BPromise2.map(this.children, (child) => {
778
+ await Bluebird2.map(this.children, (child) => {
1338
779
  return child.flush();
1339
780
  }, { concurrency: 3 });
1340
781
  };
@@ -1344,7 +785,7 @@ var StreamV2 = class {
1344
785
  */
1345
786
  _writeStateMessage = () => {
1346
787
  const message = new StateMessage(StateService.getInstance().get());
1347
- return this._target.state(message);
788
+ return this.target.state(message);
1348
789
  };
1349
790
  /**
1350
791
  * Write out a ACTIVATE_VERSION message.
@@ -1352,10 +793,10 @@ var StreamV2 = class {
1352
793
  _writeReplicationMethodMessage = async () => {
1353
794
  const method = this.configuredReplicationConfig().replicationMethod;
1354
795
  const message = new ReplicationMethodMessage(this.streamId, method);
1355
- await BPromise2.map(this.children, async (c) => {
796
+ await Bluebird2.map(this.children, async (c) => {
1356
797
  return c._writeReplicationMethodMessage();
1357
798
  }, { concurrency: 3 });
1358
- await this._target.replicationMethod(message);
799
+ await this.target.replicationMethod(message);
1359
800
  };
1360
801
  /**
1361
802
  * Write out a SCHEMA message with the stream schema.
@@ -1364,23 +805,21 @@ var StreamV2 = class {
1364
805
  if (!this.isSilent) {
1365
806
  const schema = await this.getSchema();
1366
807
  if (schema !== void 0) {
1367
- const relatedRels = findRelatedRelationships(this.streamId, this.relationships);
1368
808
  const replicationConfig = this.configuredReplicationConfig();
1369
809
  const message = new SchemaMessage({
1370
810
  keyProperties: this.primaryKey,
1371
811
  stream: this.streamId,
1372
812
  schema: schema.jsonSchema,
1373
- relationships: relatedRels,
1374
813
  ...replicationConfig.replicationKey ? { bookmarkProperties: [replicationConfig.replicationKey] } : {},
1375
814
  ...this.displayLabel ? { displayLabel: this.displayLabel } : {},
1376
815
  ...this.description ? { description: this.description } : {},
1377
816
  ...schema.propertiesMetadata ? { propertiesMetadata: schema.propertiesMetadata } : {}
1378
817
  });
1379
- await this._target.schema(message);
818
+ await this.target.schema(message);
1380
819
  }
1381
820
  }
1382
821
  if (skipChildrenSchema !== true) {
1383
- await BPromise2.map(this.children, async (child) => {
822
+ await Bluebird2.map(this.children, async (child) => {
1384
823
  await child._writeSchemaMessage();
1385
824
  }, { concurrency: 3 });
1386
825
  }
@@ -1390,7 +829,7 @@ var StreamV2 = class {
1390
829
  const signpostMoment = await this.getReplicationKeySignpost();
1391
830
  const signpostValue = signpostMoment?.format(defaultDateTimeFormat);
1392
831
  if (signpostValue) {
1393
- StateService.getInstance().setBookmarkSignpostV2(this.streamId, signpostValue);
832
+ StateService.getInstance().setBookmarkSignpost(this.streamId, signpostValue);
1394
833
  }
1395
834
  };
1396
835
  /**
@@ -1406,7 +845,7 @@ var StreamV2 = class {
1406
845
  if (this.useStateFromStreamId) {
1407
846
  return;
1408
847
  }
1409
- if (this.forcedReplicationMethod && !this.forcedReplicationKey) {
848
+ if (this.replicationMethod && !this.replicationKey) {
1410
849
  return;
1411
850
  }
1412
851
  if (!replicationConfig.replicationKey) {
@@ -1416,14 +855,14 @@ var StreamV2 = class {
1416
855
  );
1417
856
  }
1418
857
  const stateServiceInst = StateService.getInstance();
1419
- const state = stateServiceInst.getBookmarkV2(this.streamId);
858
+ const state = stateServiceInst.getBookmark(this.streamId);
1420
859
  const newState = incrementStreamState(
1421
860
  state,
1422
861
  replicationConfig.replicationKey,
1423
862
  latestRecord,
1424
863
  this.isSorted
1425
864
  );
1426
- StateService.getInstance().setBookmarkV2(this.streamId, newState);
865
+ StateService.getInstance().setBookmark(this.streamId, newState);
1427
866
  }
1428
867
  }
1429
868
  };
@@ -1455,27 +894,27 @@ var StreamV2 = class {
1455
894
  **/
1456
895
  getStartingTimestamp() {
1457
896
  const streamIdToReadFrom = this.useStateFromStreamId ? this.useStateFromStreamId : this.streamId;
1458
- const state = extractStateForStream(this._tapState, streamIdToReadFrom);
1459
- const startDate = this._config.start_date;
897
+ const state = extractStateForStream(this.tapState, streamIdToReadFrom);
898
+ const startDate = this.config.start_date;
1460
899
  const replicationConfig = this.configuredReplicationConfig();
1461
900
  logger.debug(`${this.streamId}: state.replicationKeyValue: ${state.replicationKeyValue},
1462
901
  state.replicationKey: ${state.replicationKey},
1463
902
  this.replicationKey: ${replicationConfig.replicationKey}`);
1464
903
  if (state.replicationKeyValue) {
1465
904
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning replication key value: ${state.replicationKeyValue}`);
1466
- return moment2(state.replicationKeyValue);
905
+ return dayjs2(state.replicationKeyValue);
1467
906
  } else if (startDate) {
1468
907
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning config start date: ${startDate}`);
1469
- return moment2(startDate);
908
+ return dayjs2(startDate);
1470
909
  } else {
1471
910
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning EPOCH (1970)`);
1472
- return moment2(0);
911
+ return dayjs2(0);
1473
912
  }
1474
913
  }
1475
914
  /**
1476
915
  * Return the max allowable bookmark value for this stream's replication key.
1477
916
  *
1478
- * For timestamp-based replication keys, this defaults to `moment()`. For
917
+ * For timestamp-based replication keys, this defaults to `dayjs()`. For
1479
918
  * non-timestamp replication keys, default to `undefined`.
1480
919
  *
1481
920
  * Override this value to prevent bookmarks from being advanced in cases where we
@@ -1483,7 +922,7 @@ var StreamV2 = class {
1483
922
  */
1484
923
  getReplicationKeySignpost = async () => {
1485
924
  if (await this.isTimestampReplicationKey()) {
1486
- return moment2();
925
+ return dayjs2();
1487
926
  }
1488
927
  return void 0;
1489
928
  };
@@ -1491,22 +930,22 @@ var StreamV2 = class {
1491
930
  * Return the default replication method for the stream that will be used to write the catalog.
1492
931
  */
1493
932
  defaultReplicationConfig = () => {
1494
- if (this.forcedReplicationMethod) {
933
+ if (this.replicationMethod) {
1495
934
  return {
1496
- replicationMethod: this.forcedReplicationMethod,
1497
- replicationKey: this.forcedReplicationKey,
935
+ replicationMethod: this.replicationMethod,
936
+ replicationKey: this.replicationKey,
1498
937
  isForced: true
1499
938
  };
1500
939
  }
1501
- if (this.forcedReplicationKey || this.useStateFromStreamId) {
940
+ if (this.replicationKey || this.useStateFromStreamId) {
1502
941
  return {
1503
- replicationMethod: "INCREMENTAL",
1504
- replicationKey: this.forcedReplicationKey,
942
+ replicationMethod: "INCREMENTAL" /* INCREMENTAL */,
943
+ replicationKey: this.replicationKey,
1505
944
  isForced: true
1506
945
  };
1507
946
  }
1508
947
  return {
1509
- replicationMethod: "FULL_TABLE",
948
+ replicationMethod: "FULL_TABLE" /* FULL_TABLE */,
1510
949
  replicationKey: void 0,
1511
950
  isForced: false
1512
951
  };
@@ -1515,16 +954,10 @@ var StreamV2 = class {
1515
954
  * Return the configured replication method that will be used for the sync.
1516
955
  */
1517
956
  configuredReplicationConfig = () => {
1518
- const defaultReplicationMethod = this.defaultReplicationConfig();
1519
- if (defaultReplicationMethod.isForced) {
1520
- return {
1521
- replicationMethod: defaultReplicationMethod.replicationMethod,
1522
- replicationKey: defaultReplicationMethod.replicationKey
1523
- };
1524
- }
957
+ const defaults = this.defaultReplicationConfig();
1525
958
  return {
1526
- replicationMethod: this.rootMetadataEntry?.["replication-method"] || defaultReplicationMethod.replicationMethod,
1527
- replicationKey: this.rootMetadataEntry?.["replication-key"] || defaultReplicationMethod.replicationKey
959
+ replicationMethod: defaults.replicationMethod,
960
+ replicationKey: defaults.replicationKey
1528
961
  };
1529
962
  };
1530
963
  // Private sync methods:
@@ -1554,9 +987,9 @@ var StreamV2 = class {
1554
987
  this.streamId
1555
988
  );
1556
989
  if (!this.isSilent) {
1557
- await this._target.record(recordMessage);
990
+ await this.target.record(recordMessage);
1558
991
  }
1559
- await BPromise2.map(
992
+ await Bluebird2.map(
1560
993
  this.children,
1561
994
  async (child) => {
1562
995
  await child.asyncInit(row);
@@ -1573,9 +1006,9 @@ var StreamV2 = class {
1573
1006
  rowsSent += 1;
1574
1007
  }
1575
1008
  const stateServiceInst = StateService.getInstance();
1576
- const state = stateServiceInst.getBookmarkV2(this.streamId);
1009
+ const state = stateServiceInst.getBookmark(this.streamId);
1577
1010
  const newState = finalizeStateProgressMarkers(state);
1578
- stateServiceInst.setBookmarkV2(this.streamId, newState);
1011
+ stateServiceInst.setBookmark(this.streamId, newState);
1579
1012
  if (!parent) {
1580
1013
  logger.info(`\u2705 Completed sync for stream: ${this.streamId} (${rowsSent} records)`);
1581
1014
  } else {
@@ -1600,7 +1033,7 @@ var StreamV2 = class {
1600
1033
  getSchema() {
1601
1034
  logger.debug(`stream: ${this.streamId} - getSchema() is called`);
1602
1035
  if (this.schemaPath) {
1603
- const schema = loadJson(`schemas/${this._tapName}/${this.schemaPath}`);
1036
+ const schema = loadJson(`schemas/${this.schemaPath}`);
1604
1037
  return Promise.resolve({ jsonSchema: schema });
1605
1038
  } else {
1606
1039
  if (this.isSilent) {
@@ -1637,7 +1070,7 @@ var StreamV2 = class {
1637
1070
  };
1638
1071
 
1639
1072
  // src/sdk/helpers/qs-logger.ts
1640
- import * as _3 from "lodash";
1073
+ import isPlainObject from "lodash/isPlainObject.js";
1641
1074
  var MAX_QS_KEYS = 10;
1642
1075
  var MAX_QS_STRING_LENGTH = 200;
1643
1076
  var MAX_QS_ARRAY_ITEMS = 5;
@@ -1658,7 +1091,7 @@ function truncateQueryParams(value, depth = 0) {
1658
1091
  }
1659
1092
  return items;
1660
1093
  }
1661
- if (_3.isPlainObject(value)) {
1094
+ if (isPlainObject(value)) {
1662
1095
  const keys = Object.keys(value);
1663
1096
  const result = {};
1664
1097
  for (const key of keys.slice(0, MAX_QS_KEYS)) {
@@ -1681,11 +1114,12 @@ function getTruncatedParamsForLog(params) {
1681
1114
  }
1682
1115
 
1683
1116
  // src/sdk/models/tap/restStream.ts
1684
- import * as _4 from "lodash";
1685
- var RESTStreamV2 = class extends StreamV2 {
1117
+ import cloneDeep3 from "lodash/cloneDeep.js";
1118
+ import isEqual from "lodash/isEqual.js";
1119
+ var RESTStream = class extends Stream {
1686
1120
  axiosInstance;
1687
- constructor(tapName, config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1688
- super(tapName, config, state, target, childConcurrency);
1121
+ constructor(config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
1122
+ super(config, state, target, childConcurrency);
1689
1123
  this.apiCallsMetricsConf = {
1690
1124
  isEnabled: () => !this.isSilent,
1691
1125
  getStreamIds: () => [this.streamId]
@@ -1701,14 +1135,25 @@ var RESTStreamV2 = class extends StreamV2 {
1701
1135
  */
1702
1136
  _prepareRequest = async (nextPageToken, parent) => {
1703
1137
  const method = this.httpMethod;
1704
- const url = this.getUrl(parent);
1138
+ const url = this.getNextUrl(nextPageToken, parent);
1705
1139
  if (!url) {
1706
1140
  throw new Error(`StreamId: ${this.streamId} - URL is undefined. Did your properly define the URL for this stream?`);
1707
1141
  }
1708
1142
  const authenticator = this.authenticator;
1143
+ let finalUrl = url;
1144
+ let urlQueryParams = {};
1145
+ const queryIndex = url.indexOf("?");
1146
+ if (queryIndex !== -1) {
1147
+ finalUrl = url.substring(0, queryIndex);
1148
+ const rawQueryString = url.substring(queryIndex + 1);
1149
+ urlQueryParams = qs2.parse(rawQueryString);
1150
+ }
1709
1151
  const params = {
1710
1152
  ...this.getNextUrlParams(nextPageToken, parent),
1711
- ...await authenticator?.getAuthQS(this._config)
1153
+ // Query params embedded in URL should override computed pagination params
1154
+ // but should not override auth params which are appended last
1155
+ ...urlQueryParams,
1156
+ ...await authenticator?.getAuthQS(this.config)
1712
1157
  };
1713
1158
  const requestBody = this.getRequestBodyForNextCall(nextPageToken, parent);
1714
1159
  let headers = this.getCustomHTTPHeaders(parent);
@@ -1716,7 +1161,7 @@ var RESTStreamV2 = class extends StreamV2 {
1716
1161
  headers = {};
1717
1162
  }
1718
1163
  if (authenticator) {
1719
- const authHeaders = await authenticator.getAuthHeaders(this._config);
1164
+ const authHeaders = await authenticator.getAuthHeaders(this.config);
1720
1165
  headers = {
1721
1166
  ...headers,
1722
1167
  ...authHeaders
@@ -1726,7 +1171,7 @@ var RESTStreamV2 = class extends StreamV2 {
1726
1171
  return qs2.stringify(params2, { arrayFormat: "repeat" });
1727
1172
  };
1728
1173
  const request = {
1729
- url,
1174
+ url: finalUrl,
1730
1175
  method,
1731
1176
  data: requestBody,
1732
1177
  params,
@@ -1799,9 +1244,9 @@ var RESTStreamV2 = class extends StreamV2 {
1799
1244
  for await (const row of rows) {
1800
1245
  yield row;
1801
1246
  }
1802
- const previousToken = _4.cloneDeep(nextPageToken);
1247
+ const previousToken = cloneDeep3(nextPageToken);
1803
1248
  nextPageToken = this.getNextPageToken(resp, previousToken);
1804
- if (nextPageToken && _4.isEqual(nextPageToken, previousToken)) {
1249
+ if (nextPageToken && isEqual(nextPageToken, previousToken)) {
1805
1250
  throw new Error(
1806
1251
  `${this.streamId} - Loop detected in pagination. "
1807
1252
  Pagination token: \`${JSON.stringify(nextPageToken)}\` is identical to prior token \`${JSON.stringify(previousToken)}\`.`
@@ -1878,7 +1323,7 @@ var RESTStreamV2 = class extends StreamV2 {
1878
1323
  * TODO: Make URL + Path templatisable and replace those with Extractor (or Tap) config
1879
1324
  * @returns
1880
1325
  */
1881
- getUrl(parent) {
1326
+ getNextUrl(previousToken, parent) {
1882
1327
  const urlPattern = [this.baseUrl, this.path].join("");
1883
1328
  return urlPattern;
1884
1329
  }
@@ -2002,20 +1447,20 @@ var StreamWarehouseSyncService = class {
2002
1447
  const columnNamesFromWarehouse = columnsFromWarehouse.map((col) => col.name);
2003
1448
  const columnsMappingStore = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2004
1449
  const columnsToAdd = Object.keys(columnsMappingStore).filter((unsafeColumnKey) => {
2005
- const safeColumnName = columnsMappingStore[unsafeColumnKey];
2006
- if (!safeColumnName) {
1450
+ const safeColumnName2 = columnsMappingStore[unsafeColumnKey];
1451
+ if (!safeColumnName2) {
2007
1452
  return false;
2008
1453
  }
2009
- return !columnNamesFromWarehouse.includes(safeColumnName);
1454
+ return !columnNamesFromWarehouse.includes(safeColumnName2);
2010
1455
  }).map((unsafeColumnKey) => {
2011
- const safeColumnName = columnsMappingStore[unsafeColumnKey];
1456
+ const safeColumnName2 = columnsMappingStore[unsafeColumnKey];
2012
1457
  const definition = this.flattenedSchema[unsafeColumnKey];
2013
- if (!safeColumnName || !definition) {
1458
+ if (!safeColumnName2 || !definition) {
2014
1459
  return void 0;
2015
1460
  }
2016
1461
  return {
2017
1462
  unsafeName: unsafeColumnKey,
2018
- name: safeColumnName,
1463
+ name: safeColumnName2,
2019
1464
  definition
2020
1465
  };
2021
1466
  }).filter((item) => !!item);
@@ -2230,18 +1675,18 @@ var flattenSchema = (streamId, schema) => {
2230
1675
 
2231
1676
  // src/sdk/models/target/target.ts
2232
1677
  import { Validator } from "jsonschema";
2233
- import moment4 from "moment";
1678
+ import dayjs4 from "dayjs";
2234
1679
 
2235
1680
  // src/sdk/models/target/streamDbState.ts
2236
- import moment3 from "moment";
1681
+ import dayjs3 from "dayjs";
2237
1682
 
2238
1683
  // src/sdk/models/target/temporaryFile.ts
2239
- import fs3 from "fs-extra";
2240
- import { uniqueId } from "lodash";
1684
+ import fs2 from "fs-extra";
1685
+ import uniqueId from "lodash/uniqueId.js";
2241
1686
  var createTemporaryFileStream = (streamId) => {
2242
- fs3.ensureDirSync("./tmp");
1687
+ fs2.ensureDirSync("./tmp");
2243
1688
  const path = `./tmp/${safePath(streamId)}-${uniqueId()}.tmp`;
2244
- const writeStream = fs3.createWriteStream(path, { flags: "w" });
1689
+ const writeStream = fs2.createWriteStream(path, { flags: "w" });
2245
1690
  return {
2246
1691
  stream: writeStream,
2247
1692
  path
@@ -2289,7 +1734,7 @@ function replaceSymbols(str) {
2289
1734
  }
2290
1735
 
2291
1736
  // src/sdk/models/target/streamDbState.ts
2292
- import * as fs4 from "fs";
1737
+ import * as fs3 from "fs";
2293
1738
  var StreamState = class {
2294
1739
  streamId;
2295
1740
  schema;
@@ -2314,7 +1759,7 @@ var StreamState = class {
2314
1759
  this.fileToLoad = createTemporaryFileStream(streamId);
2315
1760
  this.syncedRowCount = 0;
2316
1761
  this.keyProperties = [];
2317
- this.batchDate = moment3();
1762
+ this.batchDate = dayjs3();
2318
1763
  this.replicationMethod = replicationMethod;
2319
1764
  this._hasBeenLoadedYetDuringThisSync = false;
2320
1765
  }
@@ -2339,7 +1784,7 @@ var StreamState = class {
2339
1784
  resetFileToLoad() {
2340
1785
  this.fileToLoad.stream.close();
2341
1786
  const oldPath = this.fileToLoad.path;
2342
- fs4.unlinkSync(oldPath);
1787
+ fs3.unlinkSync(oldPath);
2343
1788
  this.fileToLoad = createTemporaryFileStream(this.streamId);
2344
1789
  this.batchedRowCount = 0;
2345
1790
  }
@@ -2365,13 +1810,13 @@ var StreamState = class {
2365
1810
 
2366
1811
  // src/sdk/models/target/target.ts
2367
1812
  import { Semaphore } from "async-mutex";
2368
- import { Promise as BPromise3 } from "bluebird";
1813
+ import Bluebird3 from "bluebird";
2369
1814
  var semaphore = new Semaphore(1);
2370
1815
  var ITarget = class _ITarget {
2371
1816
  config;
2372
1817
  schemaHooks;
2373
- resolver;
2374
1818
  syncTime;
1819
+ stateProvider;
2375
1820
  // Latest state message received from the Tap
2376
1821
  // Not yet flushed as we didn't upload the stream data since receiving it
2377
1822
  batchedState;
@@ -2382,15 +1827,15 @@ var ITarget = class _ITarget {
2382
1827
  streams;
2383
1828
  // JSON Schema validator
2384
1829
  validator;
2385
- constructor(config, resolver) {
1830
+ constructor(config, stateProvider) {
2386
1831
  this.config = config;
2387
- this.resolver = resolver;
2388
- this.schemaHooks = resolver.getSchemaHooks();
1832
+ this.stateProvider = stateProvider;
1833
+ this.schemaHooks = [];
2389
1834
  this.streams = {};
2390
1835
  this.batchedState = {};
2391
1836
  this.flushedState = {};
2392
1837
  this.validator = new Validator();
2393
- this.syncTime = moment4();
1838
+ this.syncTime = dayjs4();
2394
1839
  this.renameColumnStore = new RenameColumnStore();
2395
1840
  }
2396
1841
  ///////////////////////////////////////////
@@ -2499,7 +1944,7 @@ var ITarget = class _ITarget {
2499
1944
  this.streams[streamId] = new StreamState(
2500
1945
  streamId,
2501
1946
  dbSyncInstance,
2502
- replicationMethod || "FULL_TABLE"
1947
+ replicationMethod || "FULL_TABLE" /* FULL_TABLE */
2503
1948
  );
2504
1949
  }
2505
1950
  replicationMethod = (message) => {
@@ -2576,41 +2021,9 @@ var ITarget = class _ITarget {
2576
2021
  return translation;
2577
2022
  }
2578
2023
  return k;
2579
- }),
2580
- relationships: {
2581
- left: message.relationships.left.flatMap((l) => {
2582
- if (this.renameColumnStore.isReady(l.streamId) && this.renameColumnStore.isReady(message.stream)) {
2583
- const from = this.renameColumnStore.getColumnTranslation(l.streamId, l.from);
2584
- const to = this.renameColumnStore.getColumnTranslation(message.stream, l.to);
2585
- if (from && to) {
2586
- return [{
2587
- type: l.type,
2588
- streamId: l.streamId,
2589
- from,
2590
- to
2591
- }];
2592
- }
2593
- }
2594
- return [];
2595
- }),
2596
- right: message.relationships.right.flatMap((r) => {
2597
- if (this.renameColumnStore.isReady(message.stream) && this.renameColumnStore.isReady(r.streamId)) {
2598
- const from = this.renameColumnStore.getColumnTranslation(message.stream, r.from);
2599
- const to = this.renameColumnStore.getColumnTranslation(r.streamId, r.to);
2600
- if (from && to) {
2601
- return [{
2602
- type: r.type,
2603
- streamId: r.streamId,
2604
- from,
2605
- to
2606
- }];
2607
- }
2608
- }
2609
- return [];
2610
- })
2611
- }
2024
+ })
2612
2025
  };
2613
- await BPromise3.map(this.schemaHooks, async (hook) => {
2026
+ await Bluebird3.map(this.schemaHooks, async (hook) => {
2614
2027
  const input = {
2615
2028
  databaseName,
2616
2029
  schemaName,
@@ -2641,9 +2054,6 @@ var ITarget = class _ITarget {
2641
2054
  try {
2642
2055
  logger.info(`\u{1F44D} Data Extraction is complete. Will load remaining batched stream records.`);
2643
2056
  await this.loadAllStreamsInWarehouse({ isFinalLoad: true });
2644
- const configResolver = loadResolver();
2645
- await configResolver.markSyncComplete();
2646
- await configResolver.flushMetrics();
2647
2057
  return Promise.resolve();
2648
2058
  } catch (err) {
2649
2059
  logger.error(`Error while handling complete event.`);
@@ -2686,12 +2096,11 @@ var ITarget = class _ITarget {
2686
2096
  }
2687
2097
  };
2688
2098
  emitState = (state) => {
2689
- const configResolver = loadResolver();
2690
- return writeStateFile(configResolver, JSON.stringify(state));
2099
+ return this.stateProvider.writeState(JSON.stringify(state));
2691
2100
  };
2692
2101
  uploadStreamsToWarehouse = async (streamsToUpload) => {
2693
2102
  try {
2694
- await BPromise3.map(streamsToUpload, async (streamId) => {
2103
+ await Bluebird3.map(streamsToUpload, async (streamId) => {
2695
2104
  logger.info(`\u{1F4E4} Upload stream: ${streamId} to Warehouse`);
2696
2105
  const streamState = this.streams[streamId];
2697
2106
  if (streamState) {
@@ -2725,39 +2134,731 @@ var ITarget = class _ITarget {
2725
2134
  }
2726
2135
  };
2727
2136
  };
2137
+
2138
+ // src/targets/bigquery/helpers.ts
2139
+ var safeColumnName = (key) => {
2140
+ let returnString = key;
2141
+ const shouldNotStartWith = ["_TABLE_", "_FILE_", "_PARTITION"];
2142
+ shouldNotStartWith.forEach((snm) => {
2143
+ if (returnString.startsWith(snm)) {
2144
+ returnString = returnString.replace(snm, "");
2145
+ }
2146
+ });
2147
+ returnString = returnString.replace(/^[-\d\s]*/g, "");
2148
+ returnString = returnString.toLowerCase();
2149
+ returnString = returnString.replace("`", "");
2150
+ returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2151
+ if (returnString.length > 300) {
2152
+ returnString = returnString.substr(0, 300);
2153
+ }
2154
+ const pattern = /[^a-zA-Z0-9_]/gm;
2155
+ return returnString.replace(pattern, "_");
2156
+ };
2157
+ var safeTableName = (key) => {
2158
+ let returnString = key;
2159
+ returnString = returnString.toLowerCase();
2160
+ returnString = returnString.replace("`", "");
2161
+ returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2162
+ if (returnString.length > 1024) {
2163
+ returnString = returnString.substr(0, 1024);
2164
+ }
2165
+ const pattern = /[^a-zA-Z0-9_]/gm;
2166
+ return returnString.replace(pattern, "_");
2167
+ };
2168
+
2169
+ // src/targets/bigquery/service/record.ts
2170
+ import Decimal from "decimal.js";
2171
+ import dayjs5 from "dayjs";
2172
+ var validateDateRange = (record, schema) => {
2173
+ Object.keys(schema).forEach((key) => {
2174
+ const { format: format6 } = schema[key];
2175
+ if (format6 === "date-time") {
2176
+ if (record[key]) {
2177
+ const formattedDate = dayjs5(record[key]).format(defaultDateTimeFormat);
2178
+ const isNotTooMuchInTheFuture = dayjs5(record[key]).isBefore(dayjs5("9999-12-31 23:59:59.999999"));
2179
+ const isNotTooMuchInThePast = dayjs5(record[key]).isAfter(dayjs5("0001-01-01 00:00:00"));
2180
+ if (formattedDate !== "Invalid date" && isNotTooMuchInTheFuture && isNotTooMuchInThePast) {
2181
+ record[key] = formattedDate;
2182
+ } else {
2183
+ record[key] = void 0;
2184
+ logger.info("Skipping date for key %s for being invalid", key);
2185
+ }
2186
+ }
2187
+ }
2188
+ });
2189
+ return record;
2190
+ };
2191
+ var maxBQNumericValue = new Decimal("9.9999999999999999999999999999999999999E+28");
2192
+ var convertNumberIntoDecimal = (record, schema) => {
2193
+ Object.keys(record).forEach((key) => {
2194
+ if (schema[key]) {
2195
+ const { type, items } = schema[key];
2196
+ const types = type instanceof Array ? type : [type];
2197
+ if (types.includes("number")) {
2198
+ if (typeof record[key] === "number" || record[key] instanceof Decimal) {
2199
+ const decimal = new Decimal(record[key]).toDecimalPlaces(9);
2200
+ if (decimal.greaterThanOrEqualTo(maxBQNumericValue)) {
2201
+ record[key] = null;
2202
+ } else {
2203
+ record[key] = decimal;
2204
+ }
2205
+ } else {
2206
+ record[key] = null;
2207
+ }
2208
+ } else if (types.includes("array") && items?.type.includes("number")) {
2209
+ if (Array.isArray(record[key])) {
2210
+ const rawArr = record[key];
2211
+ const newArr = rawArr.map((item) => {
2212
+ if (typeof item === "number" || item instanceof Decimal) {
2213
+ const parsed = new Decimal(item).toDecimalPlaces(9);
2214
+ if (parsed.greaterThanOrEqualTo(maxBQNumericValue)) {
2215
+ return void 0;
2216
+ } else {
2217
+ return parsed;
2218
+ }
2219
+ } else {
2220
+ return void 0;
2221
+ }
2222
+ }).filter((item) => !!item);
2223
+ record[key] = newArr;
2224
+ } else {
2225
+ record[key] = null;
2226
+ }
2227
+ }
2228
+ }
2229
+ });
2230
+ return record;
2231
+ };
2232
+
2233
+ // src/targets/bigquery/service/dbSync.ts
2234
+ import dayjs6 from "dayjs";
2235
+
2236
+ // src/targets/bigquery/service/bigquery.ts
2237
+ import { BigQuery } from "@google-cloud/bigquery";
2238
+ var BqClientHolder = class {
2239
+ static instance;
2240
+ client;
2241
+ constructor(config) {
2242
+ const retryOptions = {
2243
+ autoRetry: true,
2244
+ maxRetries: 3
2245
+ };
2246
+ this.client = new BigQuery({
2247
+ projectId: config.database.trim(),
2248
+ ...retryOptions
2249
+ });
2250
+ }
2251
+ static client(config) {
2252
+ if (!this.instance) {
2253
+ this.instance = new this(config);
2254
+ }
2255
+ return this.instance.client;
2256
+ }
2257
+ };
2258
+
2259
+ // src/targets/bigquery/service/cloudStorage.ts
2260
+ import { Storage } from "@google-cloud/storage";
2261
+
2262
+ // node_modules/uuid/dist/esm-node/rng.js
2263
+ import crypto from "crypto";
2264
+ var rnds8Pool = new Uint8Array(256);
2265
+ var poolPtr = rnds8Pool.length;
2266
+ function rng() {
2267
+ if (poolPtr > rnds8Pool.length - 16) {
2268
+ crypto.randomFillSync(rnds8Pool);
2269
+ poolPtr = 0;
2270
+ }
2271
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
2272
+ }
2273
+
2274
+ // node_modules/uuid/dist/esm-node/regex.js
2275
+ 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;
2276
+
2277
+ // node_modules/uuid/dist/esm-node/validate.js
2278
+ function validate(uuid) {
2279
+ return typeof uuid === "string" && regex_default.test(uuid);
2280
+ }
2281
+ var validate_default = validate;
2282
+
2283
+ // node_modules/uuid/dist/esm-node/stringify.js
2284
+ var byteToHex = [];
2285
+ for (let i = 0; i < 256; ++i) {
2286
+ byteToHex.push((i + 256).toString(16).substr(1));
2287
+ }
2288
+ function stringify3(arr, offset = 0) {
2289
+ 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();
2290
+ if (!validate_default(uuid)) {
2291
+ throw TypeError("Stringified UUID is invalid");
2292
+ }
2293
+ return uuid;
2294
+ }
2295
+ var stringify_default = stringify3;
2296
+
2297
+ // node_modules/uuid/dist/esm-node/v4.js
2298
+ function v4(options, buf, offset) {
2299
+ options = options || {};
2300
+ const rnds = options.random || (options.rng || rng)();
2301
+ rnds[6] = rnds[6] & 15 | 64;
2302
+ rnds[8] = rnds[8] & 63 | 128;
2303
+ if (buf) {
2304
+ offset = offset || 0;
2305
+ for (let i = 0; i < 16; ++i) {
2306
+ buf[offset + i] = rnds[i];
2307
+ }
2308
+ return buf;
2309
+ }
2310
+ return stringify_default(rnds);
2311
+ }
2312
+ var v4_default = v4;
2313
+
2314
+ // src/targets/bigquery/service/cloudStorage.ts
2315
+ var uploadFileInBucket = async (streamId, gcsBuckerName, connectorId, filePath) => {
2316
+ try {
2317
+ const retryOptions = { autoRetry: true, maxRetries: 20 };
2318
+ let storage = new Storage({ retryOptions });
2319
+ const bucketRef = storage.bucket(gcsBuckerName);
2320
+ await bucketRef.get({ autoCreate: false });
2321
+ const destinationFileName = `${connectorId}/${streamId}-${v4_default()}.jsonnl`;
2322
+ await bucketRef.upload(filePath, {
2323
+ destination: destinationFileName
2324
+ });
2325
+ logger.info(`\u{1F5F3} Uploaded ${filePath} into ${gcsBuckerName}/${destinationFileName} GCS File`);
2326
+ return bucketRef.file(destinationFileName);
2327
+ } catch (err) {
2328
+ logger.error(`Issue when uploading file into GCS bucket for stream: ${streamId}
2329
+
2330
+ Error: ${err.message}
2331
+ Stack: ${err.stack}
2332
+ Code: ${err.code}
2333
+ `);
2334
+ if (err.code < 500) {
2335
+ await haltAndCatchFire(
2336
+ `unauthorized`,
2337
+ `We couldn't connect to your Cloud Storage bucket \u{1F614}
2338
+
2339
+ The error from Google is: '${err.message}' \u{1F440}
2340
+
2341
+ Could you troubleshoot your Cloud Storage configuration in Google Cloud and sync again the source? \u{1F64F}`,
2342
+ `Got error from cloud storage lib`
2343
+ );
2344
+ }
2345
+ throw err;
2346
+ }
2347
+ };
2348
+
2349
+ // src/targets/bigquery/service/dbSync.ts
2350
+ import chunk from "lodash/chunk.js";
2351
+ import { Semaphore as Semaphore2 } from "async-mutex";
2352
+ var semaphore2 = new Semaphore2(1);
2353
+ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2354
+ config;
2355
+ bqClient;
2356
+ bqDataset;
2357
+ bqTable;
2358
+ bqStagingTable;
2359
+ tableName;
2360
+ stagingTableName;
2361
+ sqlTableId;
2362
+ sqlStagingTableId;
2363
+ constructor(config, streamId, database, schema, table, renameColumnStore) {
2364
+ super(
2365
+ streamId,
2366
+ database,
2367
+ schema,
2368
+ table,
2369
+ renameColumnStore
2370
+ );
2371
+ this.config = config;
2372
+ this.bqClient = BqClientHolder.client(config);
2373
+ this.bqDataset = this.bqClient.dataset(schema, { location: config.location || "EU" });
2374
+ this.bqTable = this.bqDataset.table(table, { location: config.location || "EU" });
2375
+ this.tableName = table;
2376
+ this.stagingTableName = `${table}_temp`;
2377
+ this.bqStagingTable = this.bqDataset.table(this.stagingTableName, { location: config.location || "EU" });
2378
+ this.sqlTableId = `${schema}.${table}`;
2379
+ this.sqlStagingTableId = `${this.sqlTableId}_temp`;
2380
+ }
2381
+ // Implementation of abstract classes
2382
+ getSerializedRecord = (record) => {
2383
+ return JSON.stringify(record) + `
2384
+ `;
2385
+ };
2386
+ safeColumnName = safeColumnName;
2387
+ getWarehouseTypeFromJSONSchema = (definition) => {
2388
+ const type = definition.type;
2389
+ const format6 = definition.format;
2390
+ let typeArr;
2391
+ if (type instanceof Array) {
2392
+ typeArr = type;
2393
+ } else {
2394
+ typeArr = [type];
2395
+ }
2396
+ if (format6) {
2397
+ switch (format6) {
2398
+ case "date-time":
2399
+ return "TIMESTAMP";
2400
+ case "json":
2401
+ return "STRING";
2402
+ default:
2403
+ throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${format6}`);
2404
+ }
2405
+ } else if (typeArr.includes("number")) {
2406
+ return "NUMERIC";
2407
+ } else if (typeArr.includes("integer") && type.includes("string")) {
2408
+ return "NUMERIC";
2409
+ } else if (typeArr.includes("integer")) {
2410
+ return "INTEGER";
2411
+ } else if (typeArr.includes("boolean")) {
2412
+ return "BOOLEAN";
2413
+ } else if (typeArr.includes("string")) {
2414
+ return "STRING";
2415
+ } else if (typeArr.includes("array")) {
2416
+ const items = definition.items;
2417
+ if (items) {
2418
+ return this.getWarehouseTypeFromJSONSchema(items);
2419
+ }
2420
+ throw new Error(`StreamId: ${this.streamId} - Unsupported array schema field definition: ${JSON.stringify(definition)}`);
2421
+ } else {
2422
+ throw new Error(`StreamId: ${this.streamId} - Unsupported schema field definition: ${JSON.stringify(definition)}`);
2423
+ }
2424
+ };
2425
+ getMergeQueries = () => {
2426
+ const primaryKeyCondition = this.primaryKeys.map((pk) => {
2427
+ return `\`sourced\`.\`${pk}\` = \`destination\`.\`${pk}\``;
2428
+ }).join(` AND `);
2429
+ const primaryKeys = this.primaryKeys.map((pk) => `\`${pk}\``).join(` ,`);
2430
+ const columnUnsafeToSafeMapping = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2431
+ const columns = Object.keys(columnUnsafeToSafeMapping).map((column) => {
2432
+ return {
2433
+ isUnsafe: true,
2434
+ colName: column
2435
+ };
2436
+ });
2437
+ const columnsChunks = chunk(columns, 300).map((chunk2) => {
2438
+ this.primaryKeys.forEach((pk) => {
2439
+ if (!chunk2.map((c) => columnUnsafeToSafeMapping[c.colName]).includes(pk)) {
2440
+ chunk2.push({
2441
+ isUnsafe: false,
2442
+ colName: pk
2443
+ });
2444
+ }
2445
+ });
2446
+ return chunk2;
2447
+ });
2448
+ return columnsChunks.map((chunk2) => {
2449
+ const setValues = chunk2.map((column) => {
2450
+ if (column.isUnsafe) {
2451
+ return `\`destination\`.\`${columnUnsafeToSafeMapping[column.colName]}\` = \`sourced\`.\`${columnUnsafeToSafeMapping[column.colName]}\``;
2452
+ } else {
2453
+ return `\`destination\`.\`${column.colName}\` = \`sourced\`.\`${column.colName}\``;
2454
+ }
2455
+ }).join(`, `);
2456
+ const renamedCols = chunk2.map((column) => {
2457
+ if (column.isUnsafe) {
2458
+ return `\`${columnUnsafeToSafeMapping[column.colName]}\``;
2459
+ } else {
2460
+ return `\`${column.colName}\``;
2461
+ }
2462
+ }).join(`, `);
2463
+ const query = `
2464
+ MERGE ${this.sqlTableId} destination
2465
+ USING (
2466
+ WITH all_staging_rows AS (
2467
+ SELECT *
2468
+ FROM ${this.sqlStagingTableId}
2469
+ ),
2470
+ numbered_rows AS (
2471
+ SELECT *,
2472
+ ROW_NUMBER() OVER (PARTITION BY ${primaryKeys}) as _wly_row_nb,
2473
+ COUNT(1) OVER (PARTITION BY ${primaryKeys}) AS _wly_partition_size,
2474
+ FROM all_staging_rows
2475
+ ),
2476
+ staging_deduped AS (
2477
+ SELECT * EXCEPT(_wly_partition_size, _wly_row_nb)
2478
+ FROM numbered_rows
2479
+ WHERE _wly_partition_size = _wly_row_nb
2480
+ )
2481
+ SELECT *
2482
+ FROM staging_deduped
2483
+ ) AS sourced
2484
+ ON ${primaryKeyCondition}
2485
+ WHEN MATCHED THEN
2486
+ UPDATE SET ${setValues}
2487
+ WHEN NOT MATCHED THEN
2488
+ INSERT (${renamedCols}) VALUES (${renamedCols});
2489
+ `;
2490
+ return query;
2491
+ });
2492
+ };
2493
+ getReplaceQueries = () => {
2494
+ return [
2495
+ `TRUNCATE TABLE ${this.sqlTableId};`,
2496
+ ...this.getMergeQueries()
2497
+ ];
2498
+ };
2499
+ createDatabaseAndSchemaIfNotExists = async (retryCount) => {
2500
+ try {
2501
+ await semaphore2.runExclusive(async () => {
2502
+ const [exists] = await this.bqDataset.exists();
2503
+ if (!exists) {
2504
+ logger.info(`\u{1F423} Creating dataset: ${this.bqDataset.id} on BigQuery`);
2505
+ await this.bqDataset.create();
2506
+ } else {
2507
+ const [metadata] = await this.bqDataset.getMetadata();
2508
+ const alreadyExistinglocation = metadata.location;
2509
+ if (this.config.location && this.config.location !== alreadyExistinglocation) {
2510
+ await haltAndCatchFire(
2511
+ `unknown`,
2512
+ `A BigQuery dataset called '${this.bqDataset.id}' already exists in location: '${alreadyExistinglocation}' \u{1F605}
2513
+
2514
+ 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}
2515
+
2516
+ 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}
2517
+
2518
+ Please relaunch the connector once it's done to fix the issue \u{1F917}
2519
+ `,
2520
+ `
2521
+ We have a conflict between an already existing dataset in location ${alreadyExistinglocation} and the configured location: ${this.config.location}
2522
+ `
2523
+ );
2524
+ throw Error();
2525
+ }
2526
+ logger.info(`\u{1F44B} Dataset: ${this.bqDataset.id} already exists on BigQuery, reusing it.`);
2527
+ }
2528
+ });
2529
+ } catch (err) {
2530
+ if (err.code === 400) {
2531
+ await haltAndCatchFire(
2532
+ `unauthorized`,
2533
+ `We can't connect to your BigQuery account \u{1F614}
2534
+
2535
+ Here is the message from BigQuery: ${err.message}
2536
+
2537
+ Can you check that your Warehouse credentials and Source target schema are properly configured and try to sync again the source? \u{1F64F}
2538
+ `,
2539
+ `Got error when trying to get Dataset from BigQuery: ${err.message} - ${err.code}`
2540
+ );
2541
+ return;
2542
+ }
2543
+ if (retryCount && retryCount > this.maxRetryCount) {
2544
+ logger.error(`StreamId: ${this.streamId} - Error when checking if dataset already exists. ${err.message} - ${err.code}`);
2545
+ gracefulExit(1);
2546
+ } else {
2547
+ logger.error(`StreamId: ${this.streamId} - Error when checking if dataset already exists. Retrying... ${err.message} - ${err.code}`);
2548
+ await new Promise((resolve, reject) => setTimeout(resolve, 1e3 * retryCount));
2549
+ await this.createDatabaseAndSchemaIfNotExists(retryCount + 1);
2550
+ }
2551
+ }
2552
+ return Promise.resolve();
2553
+ };
2554
+ createTable = async () => {
2555
+ try {
2556
+ const tableName = this.tableName;
2557
+ const schema = this.generateBigquerySchema();
2558
+ logger.info(`\u{1F423} Creating table: ${tableName} on BigQuery`);
2559
+ const createTableOptions = {
2560
+ schema
2561
+ };
2562
+ await this.bqDataset.createTable(
2563
+ tableName,
2564
+ createTableOptions
2565
+ );
2566
+ return;
2567
+ } catch (err) {
2568
+ throw new Error(`StreamId: ${this.streamId} - Issue when creating table in BigQuery
2569
+
2570
+ Error: ${err.message}
2571
+ Stack: ${err.stack}`);
2572
+ }
2573
+ };
2574
+ addColumns = async (tableFields) => {
2575
+ if (tableFields.length > 0) {
2576
+ logger.info(`\u{1F195} Stream: ${this.streamId} - Adding following columns in BigQuery schema: ${JSON.stringify(tableFields)}`);
2577
+ const [metadata] = await this.bqTable.getMetadata();
2578
+ const bgTableFields = tableFields.map((tblField) => {
2579
+ return this.getBigqueryTableFieldFromInternalSchemaFieldDefinition(tblField.name, tblField.definition);
2580
+ }).filter((fld) => {
2581
+ return !!fld;
2582
+ });
2583
+ const schema = metadata.schema;
2584
+ const new_schema = {
2585
+ ...schema,
2586
+ fields: schema.fields.concat(bgTableFields)
2587
+ };
2588
+ metadata.schema = new_schema;
2589
+ await this.bqTable.setMetadata(metadata);
2590
+ return;
2591
+ }
2592
+ };
2593
+ createStagingArea = async () => {
2594
+ try {
2595
+ const tableName = this.stagingTableName;
2596
+ const [exists] = await this.bqDataset.table(tableName).exists();
2597
+ if (exists) {
2598
+ logger.info(`\u{1F474} Temporary table ${tableName} already exists, it should be a relic form the past.
2599
+ Dropping it.`);
2600
+ await this.deleteStagingArea();
2601
+ }
2602
+ const schema = this.generateBigquerySchema();
2603
+ const expirationTime = dayjs6().add(1, "day").valueOf().toString();
2604
+ const createTableOptions = {
2605
+ schema,
2606
+ expirationTime
2607
+ };
2608
+ logger.info(`\u{1F423} Creating staging table: ${tableName} on BigQuery`);
2609
+ await this.bqDataset.createTable(
2610
+ tableName,
2611
+ createTableOptions
2612
+ );
2613
+ return;
2614
+ } catch (err) {
2615
+ throw new Error(`StreamId: ${this.streamId} - Issue when creating table in BigQuery
2616
+
2617
+ Error: ${err.message}
2618
+ Stack: ${err.stack}`);
2619
+ }
2620
+ };
2621
+ loadStreamInStagingArea = async (localFilePath) => {
2622
+ try {
2623
+ const file = await uploadFileInBucket(
2624
+ this.streamId,
2625
+ this.config.loading_deck_gcs_bucket_name,
2626
+ this.config.connector_id,
2627
+ localFilePath
2628
+ );
2629
+ const metadata = {
2630
+ sourceFormat: "NEWLINE_DELIMITED_JSON",
2631
+ writeDisposition: "WRITE_TRUNCATE"
2632
+ };
2633
+ await this.loadGCSFileInTable(this.sqlStagingTableId, file, metadata);
2634
+ } catch (err) {
2635
+ logger.error(`StreamId: ${this.streamId} - Error while uploading stream.`);
2636
+ throw err;
2637
+ }
2638
+ };
2639
+ deleteStagingArea = () => {
2640
+ const query = `DROP TABLE IF EXISTS ${this.sqlStagingTableId};`;
2641
+ return this.runQueriesWithRetry([query]);
2642
+ };
2643
+ getTablesInSchema = async () => {
2644
+ const [bqTables] = await this.bqDataset.getTables();
2645
+ const foundTables = bqTables.filter((tbl) => {
2646
+ return !!tbl.id;
2647
+ }).map((bqTable) => {
2648
+ return {
2649
+ tableName: bqTable.id
2650
+ };
2651
+ });
2652
+ return foundTables;
2653
+ };
2654
+ getTableColumnsFromWarehouse = async () => {
2655
+ const [table] = await this.bqTable.getMetadata();
2656
+ return table.schema.fields;
2657
+ };
2658
+ runQueries = async (queries) => {
2659
+ const sqlQuery = `
2660
+ BEGIN
2661
+ ${queries.join(`
2662
+ `)}
2663
+ END;
2664
+ `;
2665
+ await this.bqClient.query(sqlQuery);
2666
+ };
2667
+ // Specific methods
2668
+ /**
2669
+ *
2670
+ * Generate a TableField definition object that can be passed to BigQuery APIs to define a column on a table
2671
+ *
2672
+ * @param colName
2673
+ * @param schemaFieldDefinition
2674
+ * @returns
2675
+ */
2676
+ getBigqueryTableFieldFromInternalSchemaFieldDefinition = (colName, schemaFieldDefinition) => {
2677
+ const name = colName;
2678
+ if (!schemaFieldDefinition) {
2679
+ throw new Error(`StreamId: ${this.streamId} - Schema is unknown for colName=${colName}, so we can't infer the proper BigQuery definition for this column.`);
2680
+ }
2681
+ const type = schemaFieldDefinition.type;
2682
+ let typeArr;
2683
+ if (type instanceof Array) {
2684
+ typeArr = type;
2685
+ } else {
2686
+ typeArr = [type];
2687
+ }
2688
+ if (typeArr.includes("array")) {
2689
+ return {
2690
+ name,
2691
+ mode: "REPEATED",
2692
+ type: this.getWarehouseTypeFromJSONSchema(schemaFieldDefinition)
2693
+ };
2694
+ } else {
2695
+ let nullableMode = "NULLABLE";
2696
+ return {
2697
+ name,
2698
+ mode: nullableMode,
2699
+ type: this.getWarehouseTypeFromJSONSchema(schemaFieldDefinition)
2700
+ };
2701
+ }
2702
+ };
2703
+ generateBigquerySchema = () => {
2704
+ const unsafeToSafeMappingColumnsFromColumnStore = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
2705
+ if (!unsafeToSafeMappingColumnsFromColumnStore) {
2706
+ throw new Error(`StreamId: ${this.streamId} - There is no unsafe->safe column mapping`);
2707
+ }
2708
+ return Object.keys(unsafeToSafeMappingColumnsFromColumnStore).reduce((acc, unsafeKey) => {
2709
+ const renamedColName = this.renamedColumnStore.getColumnTranslation(this.streamId, unsafeKey);
2710
+ const bigqueryTableField = this.getBigqueryTableFieldFromInternalSchemaFieldDefinition(renamedColName, this.flattenedSchema[unsafeKey]);
2711
+ if (bigqueryTableField) {
2712
+ acc.push(bigqueryTableField);
2713
+ }
2714
+ return acc;
2715
+ }, []);
2716
+ };
2717
+ loadGCSFileInTable = async (tableName, file, metadata) => {
2718
+ try {
2719
+ const [job] = await this.bqStagingTable.load(file, metadata);
2720
+ logger.info(`\u{1F69A} Loaded GCS file \`${file.id}\` in BigQuery table: ${tableName} with Job: \`${job.id}\``);
2721
+ logger.debug(`Upload Stats: ${JSON.stringify(job.statistics)}`);
2722
+ const errors2 = job.status?.errors;
2723
+ if (errors2 && errors2.length > 0) {
2724
+ throw errors2;
2725
+ }
2726
+ } catch (err) {
2727
+ logger.error(err);
2728
+ throw new Error(`StreamId: ${this.streamId} - Issue when loading file in BigQuery table | fileName: ${file.name}, tableName: ${tableName}
2729
+
2730
+ Error: ${err.message}
2731
+ Stack: ${err.stack}`);
2732
+ }
2733
+ };
2734
+ };
2735
+
2736
+ // src/targets/bigquery/main.ts
2737
+ var BigQueryTarget = class extends ITarget {
2738
+ constructor(config, stateProvider) {
2739
+ super(config, stateProvider);
2740
+ this.renameColumnStore.setSafeColumnNameConverter(this.safeColumnNameConverter);
2741
+ }
2742
+ static requiredConfigKeys = [
2743
+ "schema",
2744
+ "project_id",
2745
+ "loading_deck_gcs_bucket_name"
2746
+ ];
2747
+ newDBSyncInstance = (streamId, database, schema, table) => {
2748
+ return new BigQueryDBSync(
2749
+ this.config,
2750
+ streamId,
2751
+ database,
2752
+ schema,
2753
+ table,
2754
+ this.renameColumnStore
2755
+ );
2756
+ };
2757
+ genTableName = safeTableName;
2758
+ safeColumnNameConverter = safeColumnName;
2759
+ validateDateRange = validateDateRange;
2760
+ convertNumberIntoDecimal = convertNumberIntoDecimal;
2761
+ };
2762
+
2763
+ // src/sdk/service/gcs.ts
2764
+ import { Storage as Storage2 } from "@google-cloud/storage";
2765
+ import { format as format4 } from "util";
2766
+ var getStorage = () => new Storage2({ retryOptions: { autoRetry: true, maxRetries: 20 } });
2767
+ var readObjectAsString = async (bucketName, objectPath) => {
2768
+ try {
2769
+ const storage = getStorage();
2770
+ const bucketRef = storage.bucket(bucketName);
2771
+ await bucketRef.get({ autoCreate: false });
2772
+ const fileRef = bucketRef.file(objectPath);
2773
+ const [exists] = await fileRef.exists();
2774
+ if (!exists) {
2775
+ throw new Error(`GCS object not found: gs://${bucketName}/${objectPath}`);
2776
+ }
2777
+ const [contents] = await fileRef.download();
2778
+ return contents.toString("utf8");
2779
+ } catch (err) {
2780
+ throw new Error(format4(`error reading GCS object gs://${bucketName}/${objectPath}, err: %s`, err?.message));
2781
+ }
2782
+ };
2783
+ var writeStringObject = async (bucketName, objectPath, contents) => {
2784
+ try {
2785
+ const storage = getStorage();
2786
+ const bucketRef = storage.bucket(bucketName);
2787
+ await bucketRef.get({ autoCreate: false });
2788
+ const fileRef = bucketRef.file(objectPath);
2789
+ await fileRef.save(contents, { contentType: "application/json" });
2790
+ logger.info(`\u{1F5F3} Uploaded object to gs://${bucketName}/${objectPath}`);
2791
+ } catch (err) {
2792
+ throw new Error(format4(`error writing GCS object gs://${bucketName}/${objectPath}, err: %s`, err?.message));
2793
+ }
2794
+ };
2795
+
2796
+ // src/state-providers/gcs/main.ts
2797
+ import { format as format5 } from "util";
2798
+ var GCSStateProvider = class {
2799
+ bucketName;
2800
+ connectorId;
2801
+ constructor(connectorId, bucketName) {
2802
+ this.connectorId = connectorId;
2803
+ this.bucketName = bucketName;
2804
+ }
2805
+ getObjectPath() {
2806
+ return `${this.connectorId}/state.json`;
2807
+ }
2808
+ async getState() {
2809
+ const objectPath = this.getObjectPath();
2810
+ try {
2811
+ logger.info(`\u{1F4C1} Loading state from gs://${this.bucketName}/${objectPath}`);
2812
+ const raw = await readObjectAsString(this.bucketName, objectPath);
2813
+ const parsed = JSON.parse(raw);
2814
+ return { state: parsed };
2815
+ } catch (err) {
2816
+ logger.warn(format5(`error loading state from gs://${this.bucketName}/${objectPath}, will start with empty state, err: %s`, err?.message));
2817
+ return { state: void 0 };
2818
+ }
2819
+ }
2820
+ async writeState(state) {
2821
+ const objectPath = this.getObjectPath();
2822
+ try {
2823
+ logger.info(`\u{1F4DD} Writing state to gs://${this.bucketName}/${objectPath}`);
2824
+ await writeStringObject(this.bucketName, objectPath, state);
2825
+ } catch (err) {
2826
+ logger.error(format5(`\u{1F4A5} Error while writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
2827
+ throw new Error(format5(`error writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
2828
+ }
2829
+ }
2830
+ };
2728
2831
  export {
2729
2832
  ALL_STREAM_ID_KEYWORD,
2730
2833
  API_CALLS_METRIC_NAME,
2731
2834
  Authenticator,
2732
2835
  BATCH_INTERVAL_MS,
2733
- Catalog,
2836
+ BigQueryTarget,
2734
2837
  CounterMetric,
2735
2838
  DEFAULT_MAX_CONCURRENT_STREAMS,
2736
2839
  EXECUTION_TIME_METRIC_NAME,
2840
+ GCSStateProvider,
2737
2841
  ITarget,
2738
2842
  MissingFieldInSchemaError,
2739
2843
  MissingSchemaError,
2740
- RESTStreamV2,
2844
+ RESTStream,
2741
2845
  ROWS_SYNCED_METRIC_NAME,
2742
2846
  RecordMessage,
2743
2847
  RenameColumnStore,
2848
+ ReplicationMethod,
2744
2849
  ReplicationMethodMessage,
2745
- Resolver,
2746
2850
  SchemaMessage,
2747
2851
  SchemaValidationError,
2748
2852
  StateMessage,
2749
2853
  StateService,
2750
- StreamMetadata,
2751
- StreamV2,
2854
+ Stream,
2752
2855
  StreamWarehouseSyncService,
2753
2856
  Tap,
2754
2857
  _greaterThan,
2755
2858
  addWhalyFields,
2756
- checkRequiredConfigKeys,
2757
2859
  createTemporaryFileStream,
2758
2860
  extractStateForStream,
2759
2861
  finalizeStateProgressMarkers,
2760
- findRelatedRelationships,
2761
2862
  flattenSchema,
2762
2863
  getAllMetrics,
2763
2864
  getAxiosInstance,
@@ -2772,15 +2873,12 @@ export {
2772
2873
  incrementStreamState,
2773
2874
  loadJson,
2774
2875
  loadSchema,
2775
- loadShoreConfig,
2776
2876
  logger,
2777
2877
  postFormDataApiCall,
2778
2878
  postJSONApiCall,
2779
2879
  postUrlEncodedApiCall,
2780
2880
  printMemoryFootprint,
2781
2881
  removeParasiteProperties,
2782
- safePath,
2783
- writeCatalogFile,
2784
- writeStateFile
2882
+ safePath
2785
2883
  };
2786
2884
  //# sourceMappingURL=index.mjs.map