@whaly/connector-sdk 0.1.1 → 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.d.mts +112 -200
- package/dist/index.d.ts +112 -200
- package/dist/index.js +242 -470
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +241 -466
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -2,180 +2,10 @@
|
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
3
|
|
|
4
4
|
// src/sdk/service/logger.ts
|
|
5
|
-
import * as
|
|
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 fsExtra from "fs-extra";
|
|
29
|
-
var LocalSchemaHook = class {
|
|
30
|
-
async writeSchema(input) {
|
|
31
|
-
await fsExtra.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 statePath = "./state.json";
|
|
93
|
-
var metricsPath = "./metrics.json";
|
|
94
|
-
var LocalFilesResolver = class extends Resolver {
|
|
95
|
-
checkIfCanSync() {
|
|
96
|
-
return Promise.resolve(true);
|
|
97
|
-
}
|
|
98
|
-
markSyncStarted() {
|
|
99
|
-
return Promise.resolve();
|
|
100
|
-
}
|
|
101
|
-
onError(errorType, errorText, errorDebugText) {
|
|
102
|
-
const path = `./error.json`;
|
|
103
|
-
return writeFile(path, JSON.stringify({
|
|
104
|
-
errorType,
|
|
105
|
-
errorText,
|
|
106
|
-
errorDebugText
|
|
107
|
-
}));
|
|
108
|
-
}
|
|
109
|
-
getSchemaHooks() {
|
|
110
|
-
const wlyHook = new LocalSchemaHook();
|
|
111
|
-
return [wlyHook];
|
|
112
|
-
}
|
|
113
|
-
writeSchema() {
|
|
114
|
-
return Promise.resolve();
|
|
115
|
-
}
|
|
116
|
-
constructor() {
|
|
117
|
-
super();
|
|
118
|
-
}
|
|
119
|
-
getState() {
|
|
120
|
-
logger.info(`\u{1F4C1} Using the LOCAL resolver`);
|
|
121
|
-
const state = readAndParseJSONFile(statePath);
|
|
122
|
-
return Promise.resolve({
|
|
123
|
-
state
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
writeCatalog(catalog) {
|
|
127
|
-
return Promise.resolve();
|
|
128
|
-
}
|
|
129
|
-
writeState(state) {
|
|
130
|
-
return writeFile(statePath, state);
|
|
131
|
-
}
|
|
132
|
-
markSyncFailed() {
|
|
133
|
-
logger.info(`\u{1F622} Sync has failed.`);
|
|
134
|
-
return Promise.resolve();
|
|
135
|
-
}
|
|
136
|
-
markSyncComplete() {
|
|
137
|
-
logger.info(`\u{1F389} Sync is complete.`);
|
|
138
|
-
return Promise.resolve();
|
|
139
|
-
}
|
|
140
|
-
markDiscoveryComplete() {
|
|
141
|
-
logger.info(`\u{1F389} Discovery is complete.`);
|
|
142
|
-
return Promise.resolve();
|
|
143
|
-
}
|
|
144
|
-
async flushMetrics() {
|
|
145
|
-
const allMetrics = getAllMetrics();
|
|
146
|
-
const metricsOuput = allMetrics.map((metric) => {
|
|
147
|
-
return {
|
|
148
|
-
streamId: metric.getStreamId(),
|
|
149
|
-
name: metric.getName(),
|
|
150
|
-
value: metric.getValue()
|
|
151
|
-
};
|
|
152
|
-
}).map((obj) => JSON.stringify(obj)).join(`
|
|
153
|
-
`);
|
|
154
|
-
return writeFile(metricsPath, metricsOuput);
|
|
155
|
-
}
|
|
156
|
-
updateSourceValue(optionKey, optionValue) {
|
|
157
|
-
logger.info(`Updating source value ${optionKey} with value ${optionValue}`, { private: true });
|
|
158
|
-
return Promise.resolve();
|
|
159
|
-
}
|
|
160
|
-
getLogFormat() {
|
|
161
|
-
return combine(
|
|
162
|
-
timestamp(),
|
|
163
|
-
errors({ stack: true }),
|
|
164
|
-
splat(),
|
|
165
|
-
prettyPrint()
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
// src/sdk/service/resolver.ts
|
|
171
|
-
var loadResolver = () => {
|
|
172
|
-
return new LocalFilesResolver();
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/sdk/service/logger.ts
|
|
5
|
+
import * as winston from "winston";
|
|
176
6
|
var BATCH_INTERVAL_MS = 100;
|
|
177
7
|
var transports2 = {
|
|
178
|
-
console: new
|
|
8
|
+
console: new winston.transports.Console({
|
|
179
9
|
stderrLevels: [],
|
|
180
10
|
level: "info"
|
|
181
11
|
})
|
|
@@ -185,8 +15,14 @@ var getTransports = () => {
|
|
|
185
15
|
transports2.console
|
|
186
16
|
];
|
|
187
17
|
};
|
|
188
|
-
var
|
|
189
|
-
|
|
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
|
+
),
|
|
190
26
|
transports: getTransports()
|
|
191
27
|
});
|
|
192
28
|
var isWinstonOpen = true;
|
|
@@ -215,10 +51,6 @@ var logger = {
|
|
|
215
51
|
};
|
|
216
52
|
|
|
217
53
|
// src/sdk/utils.ts
|
|
218
|
-
var writeStateFile = (resolver, state) => {
|
|
219
|
-
logger.info(`\u{1F4DD} Writing the state file.`);
|
|
220
|
-
return resolver.writeState(state);
|
|
221
|
-
};
|
|
222
54
|
function readFile(fileName, filePath) {
|
|
223
55
|
try {
|
|
224
56
|
return readFileSync(filePath);
|
|
@@ -248,7 +80,7 @@ var loadSchema = (streamId) => {
|
|
|
248
80
|
// src/sdk/service/network.ts
|
|
249
81
|
import axios from "axios";
|
|
250
82
|
import { URLSearchParams } from "url";
|
|
251
|
-
import * as
|
|
83
|
+
import * as fs from "fs";
|
|
252
84
|
import * as qs from "qs";
|
|
253
85
|
import axiosRetry, { isNetworkOrIdempotentRequestError } from "axios-retry";
|
|
254
86
|
var shouldRetry = (error) => {
|
|
@@ -361,7 +193,7 @@ var getJSONApiCall = async (endpoint, config, privateLogging) => {
|
|
|
361
193
|
};
|
|
362
194
|
var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) => {
|
|
363
195
|
const instance = getAxiosInstance(config.retryCount);
|
|
364
|
-
const writer =
|
|
196
|
+
const writer = fs.createWriteStream(output);
|
|
365
197
|
const paramsSerializer = (params) => {
|
|
366
198
|
return qs.stringify(params, { arrayFormat: "repeat" });
|
|
367
199
|
};
|
|
@@ -455,6 +287,58 @@ var postUrlEncodedApiCall = async (endpoint, config, payload) => {
|
|
|
455
287
|
}
|
|
456
288
|
};
|
|
457
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
|
+
|
|
458
342
|
// src/sdk/service/memory.ts
|
|
459
343
|
var formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
|
|
460
344
|
var printMemoryFootprint = (prefix) => {
|
|
@@ -470,9 +354,6 @@ var printMemoryFootprint = (prefix) => {
|
|
|
470
354
|
|
|
471
355
|
// src/sdk/service/exit.ts
|
|
472
356
|
async function gracefulExit(exitCode) {
|
|
473
|
-
if (exitCode !== 0) {
|
|
474
|
-
await loadResolver().markSyncFailed();
|
|
475
|
-
}
|
|
476
357
|
const loggerFinish = new Promise((resolve, reject) => {
|
|
477
358
|
logger.closeWinston(resolve);
|
|
478
359
|
});
|
|
@@ -492,34 +373,15 @@ var haltAndCatchFire = async (errorType, errorText, errorDebugText) => {
|
|
|
492
373
|
errorText,
|
|
493
374
|
errorDebugText
|
|
494
375
|
);
|
|
495
|
-
const resolver = loadResolver();
|
|
496
|
-
await resolver.onError(errorType, errorText, errorDebugText);
|
|
497
376
|
await gracefulExit(0);
|
|
498
377
|
};
|
|
499
378
|
|
|
500
|
-
// src/sdk/
|
|
501
|
-
var
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
to: rel.to
|
|
507
|
-
};
|
|
508
|
-
if (rel.left === streamId) {
|
|
509
|
-
acc.right.push({
|
|
510
|
-
streamId: rel.right,
|
|
511
|
-
...base
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
if (rel.right === streamId) {
|
|
515
|
-
acc.left.push({
|
|
516
|
-
streamId: rel.left,
|
|
517
|
-
...base
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
return acc;
|
|
521
|
-
}, { left: [], right: [] });
|
|
522
|
-
};
|
|
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 || {});
|
|
523
385
|
|
|
524
386
|
// src/sdk/models/messages.ts
|
|
525
387
|
var RecordMessage = class {
|
|
@@ -579,7 +441,6 @@ var SchemaMessage = class {
|
|
|
579
441
|
displayLabel;
|
|
580
442
|
description;
|
|
581
443
|
propertiesMetadata;
|
|
582
|
-
relationships;
|
|
583
444
|
constructor(opts) {
|
|
584
445
|
const {
|
|
585
446
|
stream,
|
|
@@ -588,7 +449,6 @@ var SchemaMessage = class {
|
|
|
588
449
|
bookmarkProperties,
|
|
589
450
|
displayLabel,
|
|
590
451
|
description,
|
|
591
|
-
relationships,
|
|
592
452
|
propertiesMetadata
|
|
593
453
|
} = opts;
|
|
594
454
|
this.stream = stream;
|
|
@@ -597,7 +457,6 @@ var SchemaMessage = class {
|
|
|
597
457
|
this.bookmarkProperties = bookmarkProperties;
|
|
598
458
|
this.displayLabel = displayLabel;
|
|
599
459
|
this.description = description;
|
|
600
|
-
this.relationships = relationships;
|
|
601
460
|
this.propertiesMetadata = propertiesMetadata;
|
|
602
461
|
}
|
|
603
462
|
toString() {
|
|
@@ -607,8 +466,7 @@ var SchemaMessage = class {
|
|
|
607
466
|
schema: this.schema,
|
|
608
467
|
key_properties: this.keyProperties,
|
|
609
468
|
label: this.displayLabel,
|
|
610
|
-
description: this.description
|
|
611
|
-
relationships: this.relationships
|
|
469
|
+
description: this.description
|
|
612
470
|
};
|
|
613
471
|
if (this.bookmarkProperties) {
|
|
614
472
|
result["bookmark_properties"] = this.bookmarkProperties;
|
|
@@ -617,148 +475,28 @@ var SchemaMessage = class {
|
|
|
617
475
|
}
|
|
618
476
|
};
|
|
619
477
|
|
|
620
|
-
// src/sdk/models/metadata.ts
|
|
621
|
-
var MetadataMap = class extends Map {
|
|
622
|
-
areEquals(array1, array2) {
|
|
623
|
-
return array1.length === array2.length && array1.every(function(value, index) {
|
|
624
|
-
return value === array2[index];
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
findExistingEntry(key) {
|
|
628
|
-
return Array.from(super.entries()).find((entry) => {
|
|
629
|
-
if (this.areEquals(entry[0], key)) {
|
|
630
|
-
return true;
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
get(key) {
|
|
635
|
-
const existingEntry = this.findExistingEntry(key);
|
|
636
|
-
return existingEntry?.[1];
|
|
637
|
-
}
|
|
638
|
-
set(key, value) {
|
|
639
|
-
const existingKey = this.findExistingEntry(key);
|
|
640
|
-
if (existingKey) {
|
|
641
|
-
return super.set(existingKey?.[0], value);
|
|
642
|
-
} else {
|
|
643
|
-
return super.set(key, value);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
};
|
|
647
|
-
var StreamMetadata = class {
|
|
648
|
-
metadataByBreadcrumb;
|
|
649
|
-
constructor() {
|
|
650
|
-
this.metadataByBreadcrumb = new MetadataMap();
|
|
651
|
-
}
|
|
652
|
-
fromCatalogStreamMetadata(metadataArr) {
|
|
653
|
-
metadataArr.forEach((metadata) => {
|
|
654
|
-
this.metadataByBreadcrumb.set(metadata.breadcrumb, metadata.metadata);
|
|
655
|
-
});
|
|
656
|
-
return this;
|
|
657
|
-
}
|
|
658
|
-
toString() {
|
|
659
|
-
return this.metadataByBreadcrumb;
|
|
660
|
-
}
|
|
661
|
-
get(breadcrumb) {
|
|
662
|
-
return this.metadataByBreadcrumb.get(breadcrumb);
|
|
663
|
-
}
|
|
664
|
-
write(breadcrumb, key, value) {
|
|
665
|
-
if (value === void 0) {
|
|
666
|
-
throw new Error(`The value you're trying to write on the metadata at breadcrumb: \`${breadcrumb}\` and key: \`${key}\` is undefined.
|
|
667
|
-
|
|
668
|
-
Writing an undefined value is not a valid operation. Is there something wrong with your value?`);
|
|
669
|
-
}
|
|
670
|
-
if (!this.metadataByBreadcrumb.get(breadcrumb)) {
|
|
671
|
-
this.metadataByBreadcrumb.set(breadcrumb, {});
|
|
672
|
-
}
|
|
673
|
-
const previousValue = this.metadataByBreadcrumb.get(breadcrumb);
|
|
674
|
-
const newValue = {};
|
|
675
|
-
newValue[key] = value;
|
|
676
|
-
this.metadataByBreadcrumb.set(breadcrumb, { ...previousValue, ...newValue });
|
|
677
|
-
return this;
|
|
678
|
-
}
|
|
679
|
-
getRootBreadcrumbMetadata() {
|
|
680
|
-
const rootBreadcrumbMetadata = this.metadataByBreadcrumb.get([]);
|
|
681
|
-
if (!rootBreadcrumbMetadata) {
|
|
682
|
-
throw new Error(`No metadata was attached to the root breadcrumb \`[\`] while we need one to store metadata at the table level.
|
|
683
|
-
|
|
684
|
-
Is your catalog properly generated?`);
|
|
685
|
-
}
|
|
686
|
-
return rootBreadcrumbMetadata;
|
|
687
|
-
}
|
|
688
|
-
isStreamSelected() {
|
|
689
|
-
const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
|
|
690
|
-
if (rootBreadcrumbMetadata.selected === false) {
|
|
691
|
-
return false;
|
|
692
|
-
}
|
|
693
|
-
if (rootBreadcrumbMetadata.selected === true || rootBreadcrumbMetadata["selected-by-default"] === true) {
|
|
694
|
-
return true;
|
|
695
|
-
}
|
|
696
|
-
return false;
|
|
697
|
-
}
|
|
698
|
-
getKeyProperties() {
|
|
699
|
-
const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
|
|
700
|
-
if (!rootBreadcrumbMetadata["table-key-properties"]) {
|
|
701
|
-
return [];
|
|
702
|
-
}
|
|
703
|
-
return rootBreadcrumbMetadata["table-key-properties"];
|
|
704
|
-
}
|
|
705
|
-
getReplicationKey() {
|
|
706
|
-
const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
|
|
707
|
-
if (!rootBreadcrumbMetadata["replication-key"]) {
|
|
708
|
-
return void 0;
|
|
709
|
-
}
|
|
710
|
-
return rootBreadcrumbMetadata["replication-key"];
|
|
711
|
-
}
|
|
712
|
-
getReplicationMethod() {
|
|
713
|
-
const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
|
|
714
|
-
if (!rootBreadcrumbMetadata["replication-method"]) {
|
|
715
|
-
return void 0;
|
|
716
|
-
}
|
|
717
|
-
return rootBreadcrumbMetadata["replication-method"];
|
|
718
|
-
}
|
|
719
|
-
getForcedReplicationMethod() {
|
|
720
|
-
const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
|
|
721
|
-
if (!rootBreadcrumbMetadata["forced-replication-method"]) {
|
|
722
|
-
return void 0;
|
|
723
|
-
}
|
|
724
|
-
return rootBreadcrumbMetadata["forced-replication-method"];
|
|
725
|
-
}
|
|
726
|
-
toList() {
|
|
727
|
-
return Array.from(this.metadataByBreadcrumb.keys()).map((breadcrumb) => {
|
|
728
|
-
const metadata = this.metadataByBreadcrumb.get(breadcrumb);
|
|
729
|
-
if (!metadata) {
|
|
730
|
-
throw new Error(`There was no metadata entry for breadcrumb: \`${breadcrumb}\` in metadata map: ${this.metadataByBreadcrumb.toString()}`);
|
|
731
|
-
}
|
|
732
|
-
return {
|
|
733
|
-
breadcrumb,
|
|
734
|
-
metadata
|
|
735
|
-
};
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
|
|
740
478
|
// src/sdk/models/state.ts
|
|
741
|
-
import
|
|
479
|
+
import dayjs from "dayjs";
|
|
742
480
|
|
|
743
481
|
// src/sdk/constants/date.ts
|
|
744
482
|
var defaultDateTimeFormat = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ";
|
|
745
483
|
|
|
746
484
|
// src/sdk/models/state.ts
|
|
747
|
-
import { format } from "util";
|
|
485
|
+
import { format as format2 } from "util";
|
|
748
486
|
var PROGRESS_MARKERS = "progressMarkers";
|
|
749
487
|
var PROGRESS_MARKER_NOTE = "Note";
|
|
750
488
|
var SIGNPOST_MARKER = "replicationKeySignpost";
|
|
751
489
|
var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
|
|
752
490
|
let newRkValue = latestRecord[replicationKey];
|
|
753
491
|
if (!newRkValue) {
|
|
754
|
-
throw new Error(
|
|
492
|
+
throw new Error(format2(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
|
|
755
493
|
}
|
|
756
|
-
let newRkValueMoment =
|
|
494
|
+
let newRkValueMoment = dayjs(newRkValue);
|
|
757
495
|
let prevRkValue;
|
|
758
496
|
let prevRkValueMoment;
|
|
759
497
|
if (isSorted) {
|
|
760
498
|
prevRkValue = state["replicationKeyValue"];
|
|
761
|
-
prevRkValueMoment =
|
|
499
|
+
prevRkValueMoment = dayjs(prevRkValue);
|
|
762
500
|
if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
|
|
763
501
|
throw new Error(
|
|
764
502
|
`Unsorted data detected in stream. Latest value '${newRkValue}' is
|
|
@@ -773,24 +511,24 @@ var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
|
|
|
773
511
|
state[PROGRESS_MARKERS] = marker;
|
|
774
512
|
}
|
|
775
513
|
prevRkValue = state[PROGRESS_MARKERS]["replicationKeyValue"];
|
|
776
|
-
prevRkValueMoment =
|
|
514
|
+
prevRkValueMoment = dayjs(prevRkValue || state["replicationKeyValue"]);
|
|
777
515
|
if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
|
|
778
516
|
newRkValueMoment = prevRkValueMoment;
|
|
779
517
|
newRkValue = prevRkValue;
|
|
780
518
|
}
|
|
781
519
|
}
|
|
782
520
|
const signpostMarker = state[SIGNPOST_MARKER];
|
|
783
|
-
const replicationKeySignpostMoment =
|
|
521
|
+
const replicationKeySignpostMoment = dayjs(signpostMarker);
|
|
784
522
|
if (replicationKeySignpostMoment && replicationKeySignpostMoment.isBefore(newRkValueMoment)) {
|
|
785
523
|
newRkValueMoment = replicationKeySignpostMoment;
|
|
786
524
|
newRkValue = replicationKeySignpostMoment.format(defaultDateTimeFormat);
|
|
787
525
|
}
|
|
788
526
|
if (isSorted === false) {
|
|
789
527
|
state[PROGRESS_MARKERS]["replicationKey"] = replicationKey;
|
|
790
|
-
state[PROGRESS_MARKERS]["replicationKeyValue"] =
|
|
528
|
+
state[PROGRESS_MARKERS]["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
|
|
791
529
|
} else {
|
|
792
530
|
state["replicationKey"] = replicationKey;
|
|
793
|
-
state["replicationKeyValue"] =
|
|
531
|
+
state["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
|
|
794
532
|
}
|
|
795
533
|
return state;
|
|
796
534
|
};
|
|
@@ -812,7 +550,7 @@ var finalizeStateProgressMarkers = (state) => {
|
|
|
812
550
|
return state;
|
|
813
551
|
};
|
|
814
552
|
var _greaterThan = (aValue, bValue) => {
|
|
815
|
-
return
|
|
553
|
+
return dayjs(aValue).isAfter(dayjs(bValue));
|
|
816
554
|
};
|
|
817
555
|
var extractStateForStream = (state, streamId) => {
|
|
818
556
|
if (!state.bookmarks) {
|
|
@@ -835,26 +573,16 @@ var StateService = class {
|
|
|
835
573
|
}
|
|
836
574
|
return this.instance;
|
|
837
575
|
}
|
|
838
|
-
|
|
839
|
-
setBookmark(streamId, ts) {
|
|
840
|
-
this.bookmarks[streamId] = ts.format(defaultDateTimeFormat);
|
|
841
|
-
}
|
|
842
|
-
setBookmarkV2(streamId, streamState) {
|
|
576
|
+
setBookmark(streamId, streamState) {
|
|
843
577
|
this.bookmarks[streamId] = streamState;
|
|
844
578
|
}
|
|
845
|
-
|
|
579
|
+
setBookmarkSignpost(streamId, signpostValue) {
|
|
846
580
|
if (!this.bookmarks[streamId]) {
|
|
847
581
|
this.bookmarks[streamId] = {};
|
|
848
582
|
}
|
|
849
583
|
this.bookmarks[streamId][SIGNPOST_MARKER] = signpostValue;
|
|
850
584
|
}
|
|
851
585
|
getBookmark(streamId) {
|
|
852
|
-
if (!this.bookmarks[streamId]) {
|
|
853
|
-
return moment(0);
|
|
854
|
-
}
|
|
855
|
-
return moment(this.bookmarks[streamId]);
|
|
856
|
-
}
|
|
857
|
-
getBookmarkV2(streamId) {
|
|
858
586
|
if (this.bookmarks[streamId]) {
|
|
859
587
|
return this.bookmarks[streamId];
|
|
860
588
|
} else {
|
|
@@ -894,7 +622,7 @@ import axios2 from "axios";
|
|
|
894
622
|
import * as qs2 from "qs";
|
|
895
623
|
|
|
896
624
|
// src/sdk/models/tap/stream.ts
|
|
897
|
-
import
|
|
625
|
+
import dayjs2 from "dayjs";
|
|
898
626
|
|
|
899
627
|
// src/sdk/helpers/typing.ts
|
|
900
628
|
var isDatetimeType = (typeDef) => {
|
|
@@ -917,10 +645,11 @@ var isDatetimeType = (typeDef) => {
|
|
|
917
645
|
};
|
|
918
646
|
|
|
919
647
|
// src/sdk/models/tap/stream.ts
|
|
920
|
-
import
|
|
648
|
+
import cloneDeep2 from "lodash/cloneDeep.js";
|
|
921
649
|
import Bluebird2 from "bluebird";
|
|
922
650
|
|
|
923
651
|
// src/sdk/models/tap/tap.ts
|
|
652
|
+
import cloneDeep from "lodash/cloneDeep.js";
|
|
924
653
|
import Bluebird from "bluebird";
|
|
925
654
|
var DEFAULT_MAX_CONCURRENT_STREAMS = 5;
|
|
926
655
|
var Tap = class {
|
|
@@ -928,12 +657,21 @@ var Tap = class {
|
|
|
928
657
|
config;
|
|
929
658
|
streams;
|
|
930
659
|
concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
|
|
931
|
-
|
|
660
|
+
stateProvider;
|
|
661
|
+
tapState;
|
|
662
|
+
constructor(target, config, stateProvider) {
|
|
932
663
|
this.streams = [];
|
|
933
664
|
this.target = target;
|
|
934
665
|
this.config = config;
|
|
666
|
+
this.stateProvider = stateProvider;
|
|
667
|
+
this.tapState = { bookmarks: {} };
|
|
935
668
|
}
|
|
936
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();
|
|
937
675
|
logger.info(`\u{1F680} Start syncing`);
|
|
938
676
|
const state = StateService.getInstance().get();
|
|
939
677
|
logger.info(`\u{1F4CD} Received state: %j`, state);
|
|
@@ -964,15 +702,13 @@ var Tap = class {
|
|
|
964
702
|
};
|
|
965
703
|
|
|
966
704
|
// src/sdk/models/tap/stream.ts
|
|
967
|
-
import { format as
|
|
705
|
+
import { format as format3 } from "util";
|
|
968
706
|
var Stream = class {
|
|
969
707
|
// Runtime values, can't be overriden
|
|
970
708
|
config;
|
|
971
709
|
tapState;
|
|
972
710
|
target;
|
|
973
|
-
|
|
974
|
-
// Replication method that can be forced in the stream implemenation. Will prevail over any user/catalog choice
|
|
975
|
-
forcedReplicationMethod = void 0;
|
|
711
|
+
replicationMethod = void 0;
|
|
976
712
|
selectedByDefault = true;
|
|
977
713
|
STATE_MSG_FREQUENCY = 10;
|
|
978
714
|
streamId = "default";
|
|
@@ -980,10 +716,9 @@ var Stream = class {
|
|
|
980
716
|
displayLabel = void 0;
|
|
981
717
|
description = void 0;
|
|
982
718
|
primaryKey = [];
|
|
983
|
-
|
|
719
|
+
replicationKey = void 0;
|
|
984
720
|
// When set, use the state from another stream for this stream
|
|
985
721
|
useStateFromStreamId = void 0;
|
|
986
|
-
relationships = [];
|
|
987
722
|
children = [];
|
|
988
723
|
// If true, it means that the records in this stream are sorted by replicationKey.
|
|
989
724
|
// If false, then the records are coming in an unsorted manner.
|
|
@@ -995,9 +730,9 @@ var Stream = class {
|
|
|
995
730
|
// Metrics configuration
|
|
996
731
|
rowsSyncedMetricsConf;
|
|
997
732
|
executionTimeMetricsConf;
|
|
998
|
-
constructor(config,
|
|
733
|
+
constructor(config, tapState, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
|
|
999
734
|
this.config = config;
|
|
1000
|
-
this.tapState =
|
|
735
|
+
this.tapState = cloneDeep2(tapState);
|
|
1001
736
|
this.target = target;
|
|
1002
737
|
this.childConcurrency = childConcurrency;
|
|
1003
738
|
this.rowsSyncedMetricsConf = {
|
|
@@ -1029,7 +764,7 @@ var Stream = class {
|
|
|
1029
764
|
return this.flush();
|
|
1030
765
|
} catch (err) {
|
|
1031
766
|
if (err instanceof Error) {
|
|
1032
|
-
throw new Error(
|
|
767
|
+
throw new Error(format3(`got an error while syncing streamId=%s, err: %s`, this.streamId, err.message));
|
|
1033
768
|
}
|
|
1034
769
|
throw err;
|
|
1035
770
|
}
|
|
@@ -1070,13 +805,11 @@ var Stream = class {
|
|
|
1070
805
|
if (!this.isSilent) {
|
|
1071
806
|
const schema = await this.getSchema();
|
|
1072
807
|
if (schema !== void 0) {
|
|
1073
|
-
const relatedRels = findRelatedRelationships(this.streamId, this.relationships);
|
|
1074
808
|
const replicationConfig = this.configuredReplicationConfig();
|
|
1075
809
|
const message = new SchemaMessage({
|
|
1076
810
|
keyProperties: this.primaryKey,
|
|
1077
811
|
stream: this.streamId,
|
|
1078
812
|
schema: schema.jsonSchema,
|
|
1079
|
-
relationships: relatedRels,
|
|
1080
813
|
...replicationConfig.replicationKey ? { bookmarkProperties: [replicationConfig.replicationKey] } : {},
|
|
1081
814
|
...this.displayLabel ? { displayLabel: this.displayLabel } : {},
|
|
1082
815
|
...this.description ? { description: this.description } : {},
|
|
@@ -1096,7 +829,7 @@ var Stream = class {
|
|
|
1096
829
|
const signpostMoment = await this.getReplicationKeySignpost();
|
|
1097
830
|
const signpostValue = signpostMoment?.format(defaultDateTimeFormat);
|
|
1098
831
|
if (signpostValue) {
|
|
1099
|
-
StateService.getInstance().
|
|
832
|
+
StateService.getInstance().setBookmarkSignpost(this.streamId, signpostValue);
|
|
1100
833
|
}
|
|
1101
834
|
};
|
|
1102
835
|
/**
|
|
@@ -1112,7 +845,7 @@ var Stream = class {
|
|
|
1112
845
|
if (this.useStateFromStreamId) {
|
|
1113
846
|
return;
|
|
1114
847
|
}
|
|
1115
|
-
if (this.
|
|
848
|
+
if (this.replicationMethod && !this.replicationKey) {
|
|
1116
849
|
return;
|
|
1117
850
|
}
|
|
1118
851
|
if (!replicationConfig.replicationKey) {
|
|
@@ -1122,14 +855,14 @@ var Stream = class {
|
|
|
1122
855
|
);
|
|
1123
856
|
}
|
|
1124
857
|
const stateServiceInst = StateService.getInstance();
|
|
1125
|
-
const state = stateServiceInst.
|
|
858
|
+
const state = stateServiceInst.getBookmark(this.streamId);
|
|
1126
859
|
const newState = incrementStreamState(
|
|
1127
860
|
state,
|
|
1128
861
|
replicationConfig.replicationKey,
|
|
1129
862
|
latestRecord,
|
|
1130
863
|
this.isSorted
|
|
1131
864
|
);
|
|
1132
|
-
StateService.getInstance().
|
|
865
|
+
StateService.getInstance().setBookmark(this.streamId, newState);
|
|
1133
866
|
}
|
|
1134
867
|
}
|
|
1135
868
|
};
|
|
@@ -1169,19 +902,19 @@ var Stream = class {
|
|
|
1169
902
|
this.replicationKey: ${replicationConfig.replicationKey}`);
|
|
1170
903
|
if (state.replicationKeyValue) {
|
|
1171
904
|
logger.debug(`${this.streamId} - getStartingTimestamp -> Returning replication key value: ${state.replicationKeyValue}`);
|
|
1172
|
-
return
|
|
905
|
+
return dayjs2(state.replicationKeyValue);
|
|
1173
906
|
} else if (startDate) {
|
|
1174
907
|
logger.debug(`${this.streamId} - getStartingTimestamp -> Returning config start date: ${startDate}`);
|
|
1175
|
-
return
|
|
908
|
+
return dayjs2(startDate);
|
|
1176
909
|
} else {
|
|
1177
910
|
logger.debug(`${this.streamId} - getStartingTimestamp -> Returning EPOCH (1970)`);
|
|
1178
|
-
return
|
|
911
|
+
return dayjs2(0);
|
|
1179
912
|
}
|
|
1180
913
|
}
|
|
1181
914
|
/**
|
|
1182
915
|
* Return the max allowable bookmark value for this stream's replication key.
|
|
1183
916
|
*
|
|
1184
|
-
* For timestamp-based replication keys, this defaults to `
|
|
917
|
+
* For timestamp-based replication keys, this defaults to `dayjs()`. For
|
|
1185
918
|
* non-timestamp replication keys, default to `undefined`.
|
|
1186
919
|
*
|
|
1187
920
|
* Override this value to prevent bookmarks from being advanced in cases where we
|
|
@@ -1189,7 +922,7 @@ var Stream = class {
|
|
|
1189
922
|
*/
|
|
1190
923
|
getReplicationKeySignpost = async () => {
|
|
1191
924
|
if (await this.isTimestampReplicationKey()) {
|
|
1192
|
-
return
|
|
925
|
+
return dayjs2();
|
|
1193
926
|
}
|
|
1194
927
|
return void 0;
|
|
1195
928
|
};
|
|
@@ -1197,22 +930,22 @@ var Stream = class {
|
|
|
1197
930
|
* Return the default replication method for the stream that will be used to write the catalog.
|
|
1198
931
|
*/
|
|
1199
932
|
defaultReplicationConfig = () => {
|
|
1200
|
-
if (this.
|
|
933
|
+
if (this.replicationMethod) {
|
|
1201
934
|
return {
|
|
1202
|
-
replicationMethod: this.
|
|
1203
|
-
replicationKey: this.
|
|
935
|
+
replicationMethod: this.replicationMethod,
|
|
936
|
+
replicationKey: this.replicationKey,
|
|
1204
937
|
isForced: true
|
|
1205
938
|
};
|
|
1206
939
|
}
|
|
1207
|
-
if (this.
|
|
940
|
+
if (this.replicationKey || this.useStateFromStreamId) {
|
|
1208
941
|
return {
|
|
1209
|
-
replicationMethod: "INCREMENTAL"
|
|
1210
|
-
replicationKey: this.
|
|
942
|
+
replicationMethod: "INCREMENTAL" /* INCREMENTAL */,
|
|
943
|
+
replicationKey: this.replicationKey,
|
|
1211
944
|
isForced: true
|
|
1212
945
|
};
|
|
1213
946
|
}
|
|
1214
947
|
return {
|
|
1215
|
-
replicationMethod: "FULL_TABLE"
|
|
948
|
+
replicationMethod: "FULL_TABLE" /* FULL_TABLE */,
|
|
1216
949
|
replicationKey: void 0,
|
|
1217
950
|
isForced: false
|
|
1218
951
|
};
|
|
@@ -1273,9 +1006,9 @@ var Stream = class {
|
|
|
1273
1006
|
rowsSent += 1;
|
|
1274
1007
|
}
|
|
1275
1008
|
const stateServiceInst = StateService.getInstance();
|
|
1276
|
-
const state = stateServiceInst.
|
|
1009
|
+
const state = stateServiceInst.getBookmark(this.streamId);
|
|
1277
1010
|
const newState = finalizeStateProgressMarkers(state);
|
|
1278
|
-
stateServiceInst.
|
|
1011
|
+
stateServiceInst.setBookmark(this.streamId, newState);
|
|
1279
1012
|
if (!parent) {
|
|
1280
1013
|
logger.info(`\u2705 Completed sync for stream: ${this.streamId} (${rowsSent} records)`);
|
|
1281
1014
|
} else {
|
|
@@ -1381,7 +1114,7 @@ function getTruncatedParamsForLog(params) {
|
|
|
1381
1114
|
}
|
|
1382
1115
|
|
|
1383
1116
|
// src/sdk/models/tap/restStream.ts
|
|
1384
|
-
import
|
|
1117
|
+
import cloneDeep3 from "lodash/cloneDeep.js";
|
|
1385
1118
|
import isEqual from "lodash/isEqual.js";
|
|
1386
1119
|
var RESTStream = class extends Stream {
|
|
1387
1120
|
axiosInstance;
|
|
@@ -1402,13 +1135,24 @@ var RESTStream = class extends Stream {
|
|
|
1402
1135
|
*/
|
|
1403
1136
|
_prepareRequest = async (nextPageToken, parent) => {
|
|
1404
1137
|
const method = this.httpMethod;
|
|
1405
|
-
const url = this.
|
|
1138
|
+
const url = this.getNextUrl(nextPageToken, parent);
|
|
1406
1139
|
if (!url) {
|
|
1407
1140
|
throw new Error(`StreamId: ${this.streamId} - URL is undefined. Did your properly define the URL for this stream?`);
|
|
1408
1141
|
}
|
|
1409
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
|
+
}
|
|
1410
1151
|
const params = {
|
|
1411
1152
|
...this.getNextUrlParams(nextPageToken, parent),
|
|
1153
|
+
// Query params embedded in URL should override computed pagination params
|
|
1154
|
+
// but should not override auth params which are appended last
|
|
1155
|
+
...urlQueryParams,
|
|
1412
1156
|
...await authenticator?.getAuthQS(this.config)
|
|
1413
1157
|
};
|
|
1414
1158
|
const requestBody = this.getRequestBodyForNextCall(nextPageToken, parent);
|
|
@@ -1427,7 +1171,7 @@ var RESTStream = class extends Stream {
|
|
|
1427
1171
|
return qs2.stringify(params2, { arrayFormat: "repeat" });
|
|
1428
1172
|
};
|
|
1429
1173
|
const request = {
|
|
1430
|
-
url,
|
|
1174
|
+
url: finalUrl,
|
|
1431
1175
|
method,
|
|
1432
1176
|
data: requestBody,
|
|
1433
1177
|
params,
|
|
@@ -1500,7 +1244,7 @@ var RESTStream = class extends Stream {
|
|
|
1500
1244
|
for await (const row of rows) {
|
|
1501
1245
|
yield row;
|
|
1502
1246
|
}
|
|
1503
|
-
const previousToken =
|
|
1247
|
+
const previousToken = cloneDeep3(nextPageToken);
|
|
1504
1248
|
nextPageToken = this.getNextPageToken(resp, previousToken);
|
|
1505
1249
|
if (nextPageToken && isEqual(nextPageToken, previousToken)) {
|
|
1506
1250
|
throw new Error(
|
|
@@ -1579,7 +1323,7 @@ var RESTStream = class extends Stream {
|
|
|
1579
1323
|
* TODO: Make URL + Path templatisable and replace those with Extractor (or Tap) config
|
|
1580
1324
|
* @returns
|
|
1581
1325
|
*/
|
|
1582
|
-
|
|
1326
|
+
getNextUrl(previousToken, parent) {
|
|
1583
1327
|
const urlPattern = [this.baseUrl, this.path].join("");
|
|
1584
1328
|
return urlPattern;
|
|
1585
1329
|
}
|
|
@@ -1931,18 +1675,18 @@ var flattenSchema = (streamId, schema) => {
|
|
|
1931
1675
|
|
|
1932
1676
|
// src/sdk/models/target/target.ts
|
|
1933
1677
|
import { Validator } from "jsonschema";
|
|
1934
|
-
import
|
|
1678
|
+
import dayjs4 from "dayjs";
|
|
1935
1679
|
|
|
1936
1680
|
// src/sdk/models/target/streamDbState.ts
|
|
1937
|
-
import
|
|
1681
|
+
import dayjs3 from "dayjs";
|
|
1938
1682
|
|
|
1939
1683
|
// src/sdk/models/target/temporaryFile.ts
|
|
1940
|
-
import
|
|
1684
|
+
import fs2 from "fs-extra";
|
|
1941
1685
|
import uniqueId from "lodash/uniqueId.js";
|
|
1942
1686
|
var createTemporaryFileStream = (streamId) => {
|
|
1943
|
-
|
|
1687
|
+
fs2.ensureDirSync("./tmp");
|
|
1944
1688
|
const path = `./tmp/${safePath(streamId)}-${uniqueId()}.tmp`;
|
|
1945
|
-
const writeStream =
|
|
1689
|
+
const writeStream = fs2.createWriteStream(path, { flags: "w" });
|
|
1946
1690
|
return {
|
|
1947
1691
|
stream: writeStream,
|
|
1948
1692
|
path
|
|
@@ -1990,7 +1734,7 @@ function replaceSymbols(str) {
|
|
|
1990
1734
|
}
|
|
1991
1735
|
|
|
1992
1736
|
// src/sdk/models/target/streamDbState.ts
|
|
1993
|
-
import * as
|
|
1737
|
+
import * as fs3 from "fs";
|
|
1994
1738
|
var StreamState = class {
|
|
1995
1739
|
streamId;
|
|
1996
1740
|
schema;
|
|
@@ -2015,7 +1759,7 @@ var StreamState = class {
|
|
|
2015
1759
|
this.fileToLoad = createTemporaryFileStream(streamId);
|
|
2016
1760
|
this.syncedRowCount = 0;
|
|
2017
1761
|
this.keyProperties = [];
|
|
2018
|
-
this.batchDate =
|
|
1762
|
+
this.batchDate = dayjs3();
|
|
2019
1763
|
this.replicationMethod = replicationMethod;
|
|
2020
1764
|
this._hasBeenLoadedYetDuringThisSync = false;
|
|
2021
1765
|
}
|
|
@@ -2040,7 +1784,7 @@ var StreamState = class {
|
|
|
2040
1784
|
resetFileToLoad() {
|
|
2041
1785
|
this.fileToLoad.stream.close();
|
|
2042
1786
|
const oldPath = this.fileToLoad.path;
|
|
2043
|
-
|
|
1787
|
+
fs3.unlinkSync(oldPath);
|
|
2044
1788
|
this.fileToLoad = createTemporaryFileStream(this.streamId);
|
|
2045
1789
|
this.batchedRowCount = 0;
|
|
2046
1790
|
}
|
|
@@ -2071,8 +1815,8 @@ var semaphore = new Semaphore(1);
|
|
|
2071
1815
|
var ITarget = class _ITarget {
|
|
2072
1816
|
config;
|
|
2073
1817
|
schemaHooks;
|
|
2074
|
-
resolver;
|
|
2075
1818
|
syncTime;
|
|
1819
|
+
stateProvider;
|
|
2076
1820
|
// Latest state message received from the Tap
|
|
2077
1821
|
// Not yet flushed as we didn't upload the stream data since receiving it
|
|
2078
1822
|
batchedState;
|
|
@@ -2083,15 +1827,15 @@ var ITarget = class _ITarget {
|
|
|
2083
1827
|
streams;
|
|
2084
1828
|
// JSON Schema validator
|
|
2085
1829
|
validator;
|
|
2086
|
-
constructor(config,
|
|
1830
|
+
constructor(config, stateProvider) {
|
|
2087
1831
|
this.config = config;
|
|
2088
|
-
this.
|
|
2089
|
-
this.schemaHooks =
|
|
1832
|
+
this.stateProvider = stateProvider;
|
|
1833
|
+
this.schemaHooks = [];
|
|
2090
1834
|
this.streams = {};
|
|
2091
1835
|
this.batchedState = {};
|
|
2092
1836
|
this.flushedState = {};
|
|
2093
1837
|
this.validator = new Validator();
|
|
2094
|
-
this.syncTime =
|
|
1838
|
+
this.syncTime = dayjs4();
|
|
2095
1839
|
this.renameColumnStore = new RenameColumnStore();
|
|
2096
1840
|
}
|
|
2097
1841
|
///////////////////////////////////////////
|
|
@@ -2200,7 +1944,7 @@ var ITarget = class _ITarget {
|
|
|
2200
1944
|
this.streams[streamId] = new StreamState(
|
|
2201
1945
|
streamId,
|
|
2202
1946
|
dbSyncInstance,
|
|
2203
|
-
replicationMethod || "FULL_TABLE"
|
|
1947
|
+
replicationMethod || "FULL_TABLE" /* FULL_TABLE */
|
|
2204
1948
|
);
|
|
2205
1949
|
}
|
|
2206
1950
|
replicationMethod = (message) => {
|
|
@@ -2277,39 +2021,7 @@ var ITarget = class _ITarget {
|
|
|
2277
2021
|
return translation;
|
|
2278
2022
|
}
|
|
2279
2023
|
return k;
|
|
2280
|
-
})
|
|
2281
|
-
relationships: {
|
|
2282
|
-
left: message.relationships.left.flatMap((l) => {
|
|
2283
|
-
if (this.renameColumnStore.isReady(l.streamId) && this.renameColumnStore.isReady(message.stream)) {
|
|
2284
|
-
const from = this.renameColumnStore.getColumnTranslation(l.streamId, l.from);
|
|
2285
|
-
const to = this.renameColumnStore.getColumnTranslation(message.stream, l.to);
|
|
2286
|
-
if (from && to) {
|
|
2287
|
-
return [{
|
|
2288
|
-
type: l.type,
|
|
2289
|
-
streamId: l.streamId,
|
|
2290
|
-
from,
|
|
2291
|
-
to
|
|
2292
|
-
}];
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
return [];
|
|
2296
|
-
}),
|
|
2297
|
-
right: message.relationships.right.flatMap((r) => {
|
|
2298
|
-
if (this.renameColumnStore.isReady(message.stream) && this.renameColumnStore.isReady(r.streamId)) {
|
|
2299
|
-
const from = this.renameColumnStore.getColumnTranslation(message.stream, r.from);
|
|
2300
|
-
const to = this.renameColumnStore.getColumnTranslation(r.streamId, r.to);
|
|
2301
|
-
if (from && to) {
|
|
2302
|
-
return [{
|
|
2303
|
-
type: r.type,
|
|
2304
|
-
streamId: r.streamId,
|
|
2305
|
-
from,
|
|
2306
|
-
to
|
|
2307
|
-
}];
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
return [];
|
|
2311
|
-
})
|
|
2312
|
-
}
|
|
2024
|
+
})
|
|
2313
2025
|
};
|
|
2314
2026
|
await Bluebird3.map(this.schemaHooks, async (hook) => {
|
|
2315
2027
|
const input = {
|
|
@@ -2342,9 +2054,6 @@ var ITarget = class _ITarget {
|
|
|
2342
2054
|
try {
|
|
2343
2055
|
logger.info(`\u{1F44D} Data Extraction is complete. Will load remaining batched stream records.`);
|
|
2344
2056
|
await this.loadAllStreamsInWarehouse({ isFinalLoad: true });
|
|
2345
|
-
const configResolver = loadResolver();
|
|
2346
|
-
await configResolver.markSyncComplete();
|
|
2347
|
-
await configResolver.flushMetrics();
|
|
2348
2057
|
return Promise.resolve();
|
|
2349
2058
|
} catch (err) {
|
|
2350
2059
|
logger.error(`Error while handling complete event.`);
|
|
@@ -2387,8 +2096,7 @@ var ITarget = class _ITarget {
|
|
|
2387
2096
|
}
|
|
2388
2097
|
};
|
|
2389
2098
|
emitState = (state) => {
|
|
2390
|
-
|
|
2391
|
-
return writeStateFile(configResolver, JSON.stringify(state));
|
|
2099
|
+
return this.stateProvider.writeState(JSON.stringify(state));
|
|
2392
2100
|
};
|
|
2393
2101
|
uploadStreamsToWarehouse = async (streamsToUpload) => {
|
|
2394
2102
|
try {
|
|
@@ -2460,15 +2168,15 @@ var safeTableName = (key) => {
|
|
|
2460
2168
|
|
|
2461
2169
|
// src/targets/bigquery/service/record.ts
|
|
2462
2170
|
import Decimal from "decimal.js";
|
|
2463
|
-
import
|
|
2171
|
+
import dayjs5 from "dayjs";
|
|
2464
2172
|
var validateDateRange = (record, schema) => {
|
|
2465
2173
|
Object.keys(schema).forEach((key) => {
|
|
2466
|
-
const { format:
|
|
2467
|
-
if (
|
|
2174
|
+
const { format: format6 } = schema[key];
|
|
2175
|
+
if (format6 === "date-time") {
|
|
2468
2176
|
if (record[key]) {
|
|
2469
|
-
const formattedDate =
|
|
2470
|
-
const isNotTooMuchInTheFuture =
|
|
2471
|
-
const isNotTooMuchInThePast =
|
|
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"));
|
|
2472
2180
|
if (formattedDate !== "Invalid date" && isNotTooMuchInTheFuture && isNotTooMuchInThePast) {
|
|
2473
2181
|
record[key] = formattedDate;
|
|
2474
2182
|
} else {
|
|
@@ -2523,7 +2231,7 @@ var convertNumberIntoDecimal = (record, schema) => {
|
|
|
2523
2231
|
};
|
|
2524
2232
|
|
|
2525
2233
|
// src/targets/bigquery/service/dbSync.ts
|
|
2526
|
-
import
|
|
2234
|
+
import dayjs6 from "dayjs";
|
|
2527
2235
|
|
|
2528
2236
|
// src/targets/bigquery/service/bigquery.ts
|
|
2529
2237
|
import { BigQuery } from "@google-cloud/bigquery";
|
|
@@ -2604,13 +2312,13 @@ function v4(options, buf, offset) {
|
|
|
2604
2312
|
var v4_default = v4;
|
|
2605
2313
|
|
|
2606
2314
|
// src/targets/bigquery/service/cloudStorage.ts
|
|
2607
|
-
var uploadFileInBucket = async (streamId, gcsBuckerName, filePath) => {
|
|
2315
|
+
var uploadFileInBucket = async (streamId, gcsBuckerName, connectorId, filePath) => {
|
|
2608
2316
|
try {
|
|
2609
2317
|
const retryOptions = { autoRetry: true, maxRetries: 20 };
|
|
2610
2318
|
let storage = new Storage({ retryOptions });
|
|
2611
2319
|
const bucketRef = storage.bucket(gcsBuckerName);
|
|
2612
2320
|
await bucketRef.get({ autoCreate: false });
|
|
2613
|
-
const destinationFileName = `${streamId}-${v4_default()}.jsonnl`;
|
|
2321
|
+
const destinationFileName = `${connectorId}/${streamId}-${v4_default()}.jsonnl`;
|
|
2614
2322
|
await bucketRef.upload(filePath, {
|
|
2615
2323
|
destination: destinationFileName
|
|
2616
2324
|
});
|
|
@@ -2678,21 +2386,21 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
|
|
|
2678
2386
|
safeColumnName = safeColumnName;
|
|
2679
2387
|
getWarehouseTypeFromJSONSchema = (definition) => {
|
|
2680
2388
|
const type = definition.type;
|
|
2681
|
-
const
|
|
2389
|
+
const format6 = definition.format;
|
|
2682
2390
|
let typeArr;
|
|
2683
2391
|
if (type instanceof Array) {
|
|
2684
2392
|
typeArr = type;
|
|
2685
2393
|
} else {
|
|
2686
2394
|
typeArr = [type];
|
|
2687
2395
|
}
|
|
2688
|
-
if (
|
|
2689
|
-
switch (
|
|
2396
|
+
if (format6) {
|
|
2397
|
+
switch (format6) {
|
|
2690
2398
|
case "date-time":
|
|
2691
2399
|
return "TIMESTAMP";
|
|
2692
2400
|
case "json":
|
|
2693
2401
|
return "STRING";
|
|
2694
2402
|
default:
|
|
2695
|
-
throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${
|
|
2403
|
+
throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${format6}`);
|
|
2696
2404
|
}
|
|
2697
2405
|
} else if (typeArr.includes("number")) {
|
|
2698
2406
|
return "NUMERIC";
|
|
@@ -2892,7 +2600,7 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
|
|
|
2892
2600
|
await this.deleteStagingArea();
|
|
2893
2601
|
}
|
|
2894
2602
|
const schema = this.generateBigquerySchema();
|
|
2895
|
-
const expirationTime =
|
|
2603
|
+
const expirationTime = dayjs6().add(1, "day").valueOf().toString();
|
|
2896
2604
|
const createTableOptions = {
|
|
2897
2605
|
schema,
|
|
2898
2606
|
expirationTime
|
|
@@ -2915,6 +2623,7 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
|
|
|
2915
2623
|
const file = await uploadFileInBucket(
|
|
2916
2624
|
this.streamId,
|
|
2917
2625
|
this.config.loading_deck_gcs_bucket_name,
|
|
2626
|
+
this.config.connector_id,
|
|
2918
2627
|
localFilePath
|
|
2919
2628
|
);
|
|
2920
2629
|
const metadata = {
|
|
@@ -3026,8 +2735,8 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
|
|
|
3026
2735
|
|
|
3027
2736
|
// src/targets/bigquery/main.ts
|
|
3028
2737
|
var BigQueryTarget = class extends ITarget {
|
|
3029
|
-
constructor(config,
|
|
3030
|
-
super(config,
|
|
2738
|
+
constructor(config, stateProvider) {
|
|
2739
|
+
super(config, stateProvider);
|
|
3031
2740
|
this.renameColumnStore.setSafeColumnNameConverter(this.safeColumnNameConverter);
|
|
3032
2741
|
}
|
|
3033
2742
|
static requiredConfigKeys = [
|
|
@@ -3050,6 +2759,75 @@ var BigQueryTarget = class extends ITarget {
|
|
|
3050
2759
|
validateDateRange = validateDateRange;
|
|
3051
2760
|
convertNumberIntoDecimal = convertNumberIntoDecimal;
|
|
3052
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
|
+
};
|
|
3053
2831
|
export {
|
|
3054
2832
|
ALL_STREAM_ID_KEYWORD,
|
|
3055
2833
|
API_CALLS_METRIC_NAME,
|
|
@@ -3059,6 +2837,7 @@ export {
|
|
|
3059
2837
|
CounterMetric,
|
|
3060
2838
|
DEFAULT_MAX_CONCURRENT_STREAMS,
|
|
3061
2839
|
EXECUTION_TIME_METRIC_NAME,
|
|
2840
|
+
GCSStateProvider,
|
|
3062
2841
|
ITarget,
|
|
3063
2842
|
MissingFieldInSchemaError,
|
|
3064
2843
|
MissingSchemaError,
|
|
@@ -3066,14 +2845,13 @@ export {
|
|
|
3066
2845
|
ROWS_SYNCED_METRIC_NAME,
|
|
3067
2846
|
RecordMessage,
|
|
3068
2847
|
RenameColumnStore,
|
|
2848
|
+
ReplicationMethod,
|
|
3069
2849
|
ReplicationMethodMessage,
|
|
3070
|
-
Resolver,
|
|
3071
2850
|
SchemaMessage,
|
|
3072
2851
|
SchemaValidationError,
|
|
3073
2852
|
StateMessage,
|
|
3074
2853
|
StateService,
|
|
3075
2854
|
Stream,
|
|
3076
|
-
StreamMetadata,
|
|
3077
2855
|
StreamWarehouseSyncService,
|
|
3078
2856
|
Tap,
|
|
3079
2857
|
_greaterThan,
|
|
@@ -3081,7 +2859,6 @@ export {
|
|
|
3081
2859
|
createTemporaryFileStream,
|
|
3082
2860
|
extractStateForStream,
|
|
3083
2861
|
finalizeStateProgressMarkers,
|
|
3084
|
-
findRelatedRelationships,
|
|
3085
2862
|
flattenSchema,
|
|
3086
2863
|
getAllMetrics,
|
|
3087
2864
|
getAxiosInstance,
|
|
@@ -3095,7 +2872,6 @@ export {
|
|
|
3095
2872
|
haltAndCatchFire,
|
|
3096
2873
|
incrementStreamState,
|
|
3097
2874
|
loadJson,
|
|
3098
|
-
loadResolver,
|
|
3099
2875
|
loadSchema,
|
|
3100
2876
|
logger,
|
|
3101
2877
|
postFormDataApiCall,
|
|
@@ -3103,7 +2879,6 @@ export {
|
|
|
3103
2879
|
postUrlEncodedApiCall,
|
|
3104
2880
|
printMemoryFootprint,
|
|
3105
2881
|
removeParasiteProperties,
|
|
3106
|
-
safePath
|
|
3107
|
-
writeStateFile
|
|
2882
|
+
safePath
|
|
3108
2883
|
};
|
|
3109
2884
|
//# sourceMappingURL=index.mjs.map
|