@useairfoil/connector-kit 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/METRICS.md +16 -17
- package/README.md +127 -442
- package/dist/annotations-BE74YmAO.d.mts +39 -0
- package/dist/auth/index.d.mts +2 -0
- package/dist/{auth-DvU_CZ7C.js → auth/index.mjs} +6 -8
- package/dist/auth/index.mjs.map +1 -0
- package/dist/connector-app/index.d.mts +2 -0
- package/dist/connector-app/index.mjs +2 -0
- package/dist/connector-app-C-L4QqG6.mjs +123 -0
- package/dist/connector-app-C-L4QqG6.mjs.map +1 -0
- package/dist/{constants-D113Ut1x.js → constants-D-GQzpHZ.mjs} +4 -5
- package/dist/constants-D-GQzpHZ.mjs.map +1 -0
- package/dist/demo/index.d.mts +2 -0
- package/dist/demo/index.mjs +2 -0
- package/dist/demo-B30Vlkk2.mjs +191 -0
- package/dist/demo-B30Vlkk2.mjs.map +1 -0
- package/dist/errors/index.d.mts +2 -0
- package/dist/errors/index.mjs +2 -0
- package/dist/{errors-DHWylqRZ.js → errors-pT0YVJU5.mjs} +2 -3
- package/dist/errors-pT0YVJU5.mjs.map +1 -0
- package/dist/iceberg/index.d.mts +3 -0
- package/dist/iceberg/index.mjs +3 -0
- package/dist/iceberg-D6L8I6vA.mjs +39 -0
- package/dist/iceberg-D6L8I6vA.mjs.map +1 -0
- package/dist/{index-VpHaZXZw.d.ts → index-BUHSGm9Q.d.mts} +11 -13
- package/dist/{index-DXa26Ry5.d.ts → index-BX8G7d41.d.mts} +1 -2
- package/dist/{index-bfjH7Yb0.d.ts → index-BfXpgxk9.d.mts} +9 -10
- package/dist/{index-BKw7jA2a.d.ts → index-CRVAwJ5-.d.mts} +10 -8
- package/dist/index-CUPP2A_m.d.mts +27 -0
- package/dist/{index-DYpEMJf-.d.ts → index-CgvFomO6.d.mts} +6 -7
- package/dist/index-DJhDp4Xc.d.mts +58 -0
- package/dist/index-Dk7urvpF.d.mts +76 -0
- package/dist/{index-DKoRwyFn.d.ts → index-DkVNO6ks.d.mts} +2 -5
- package/dist/{index-C5Ma7V7t.d.ts → index-NnCutIqM.d.mts} +62 -37
- package/dist/{index-CR9HJDdr.d.ts → index-eFuoMZZ3.d.mts} +18 -20
- package/dist/{index-ZSuiZX5H.d.ts → index-ygpml2kC.d.mts} +19 -31
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +45 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ingestion/index.d.mts +2 -0
- package/dist/ingestion/index.mjs +2 -0
- package/dist/ingestion-q0WwqUSt.mjs +275 -0
- package/dist/ingestion-q0WwqUSt.mjs.map +1 -0
- package/dist/ingestor/index.d.mts +3 -0
- package/dist/ingestor/index.mjs +4 -0
- package/dist/ingestor-D4L-HPkg.mjs +26 -0
- package/dist/ingestor-D4L-HPkg.mjs.map +1 -0
- package/dist/instrumented-Bhp7fyvh.mjs +29 -0
- package/dist/instrumented-Bhp7fyvh.mjs.map +1 -0
- package/dist/manifest/index.d.mts +2 -0
- package/dist/manifest/{index.js → index.mjs} +2 -4
- package/dist/{manifest-P7kBnch9.js → manifest-BGKxmKLy.mjs} +14 -19
- package/dist/manifest-BGKxmKLy.mjs.map +1 -0
- package/dist/{telemetry-j_nxwYfh.js → metrics-BaEX__MN.mjs} +170 -19
- package/dist/metrics-BaEX__MN.mjs.map +1 -0
- package/dist/{metrics-C4V1pm6v.d.ts → metrics-CVXFL7YT.d.mts} +18 -13
- package/dist/metrics.d.mts +2 -0
- package/dist/metrics.mjs +2 -0
- package/dist/{chunk-Bp6m_JJh.js → rolldown-runtime-D7D4PA-g.mjs} +4 -4
- package/dist/runtime-config/index.d.mts +2 -0
- package/dist/runtime-config/index.mjs +3 -0
- package/dist/runtime-config-Oa55gAew.mjs +97 -0
- package/dist/runtime-config-Oa55gAew.mjs.map +1 -0
- package/dist/server-CMnxz3h0.mjs +144 -0
- package/dist/server-CMnxz3h0.mjs.map +1 -0
- package/dist/service-DLkhuxYl.d.mts +27 -0
- package/dist/service-jWxxfEht.mjs +7 -0
- package/dist/service-jWxxfEht.mjs.map +1 -0
- package/dist/state-store/index.d.mts +2 -0
- package/dist/state-store/index.mjs +2 -0
- package/dist/{state-store-BdYulQOY.js → state-store-uFvHY7Ax.mjs} +15 -22
- package/dist/state-store-uFvHY7Ax.mjs.map +1 -0
- package/dist/status/{index.d.ts → index.d.mts} +1 -4
- package/dist/status/{index.js → index.mjs} +2 -6
- package/dist/{status-BTTd7AUR.js → status-CKb9IhQ9.mjs} +14 -16
- package/dist/status-CKb9IhQ9.mjs.map +1 -0
- package/dist/table-V0tgUC5i.mjs +370 -0
- package/dist/table-V0tgUC5i.mjs.map +1 -0
- package/dist/types-xZtSmrGA.d.mts +220 -0
- package/dist/webhook/index.d.mts +3 -0
- package/dist/webhook/index.mjs +12 -0
- package/dist/webhook/index.mjs.map +1 -0
- package/dist/wings-I56hGtNv.mjs +325 -0
- package/dist/wings-I56hGtNv.mjs.map +1 -0
- package/package.json +64 -51
- package/src/catalog/table.ts +201 -0
- package/src/connector-app/create-table.ts +47 -0
- package/src/connector-app/demo.ts +42 -0
- package/src/connector-app/index.ts +2 -0
- package/src/connector-app/start.ts +1 -1
- package/src/core/builder.ts +15 -23
- package/src/core/index.ts +11 -1
- package/src/core/types.ts +114 -39
- package/src/demo/definition.ts +68 -0
- package/src/demo/generate.ts +167 -0
- package/src/demo/index.ts +3 -0
- package/src/demo/run.ts +142 -0
- package/src/iceberg/annotations.ts +62 -0
- package/src/iceberg/error.ts +9 -0
- package/src/iceberg/index.ts +3 -0
- package/src/iceberg/table.ts +511 -0
- package/src/index.ts +3 -1
- package/src/ingestion/engine.ts +240 -60
- package/src/ingestor/arrow.ts +248 -0
- package/src/ingestor/console.ts +13 -0
- package/src/ingestor/index.ts +9 -0
- package/src/ingestor/instrumented.ts +61 -0
- package/src/ingestor/service.ts +30 -0
- package/src/ingestor/wings.ts +143 -0
- package/src/manifest/config.ts +3 -3
- package/src/metrics.ts +66 -20
- package/src/runtime-config/config.ts +6 -8
- package/src/runtime-config/constants.ts +2 -2
- package/src/runtime-config/index.ts +2 -7
- package/src/runtime-config/layer.ts +1 -1
- package/src/runtime-config/table-bindings.ts +48 -136
- package/src/state-store/layer-key-value-store.ts +8 -3
- package/src/state-store/layer-memory.ts +1 -1
- package/src/state-store/layer-sql.ts +4 -4
- package/src/state-store/schema.ts +2 -2
- package/src/state-store/state.ts +14 -2
- package/src/status/route.ts +2 -2
- package/src/telemetry.ts +10 -10
- package/src/webhook/server.ts +39 -40
- package/dist/auth/index.d.ts +0 -2
- package/dist/auth/index.js +0 -3
- package/dist/auth-DvU_CZ7C.js.map +0 -1
- package/dist/connector-app/index.d.ts +0 -14
- package/dist/connector-app/index.js +0 -12
- package/dist/connector-app-BljHd0Pe.js +0 -64
- package/dist/connector-app-BljHd0Pe.js.map +0 -1
- package/dist/constants-D113Ut1x.js.map +0 -1
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -3
- package/dist/errors-DHWylqRZ.js.map +0 -1
- package/dist/index-BA8ASHKr.d.ts +0 -31
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -60
- package/dist/index.js.map +0 -1
- package/dist/ingestion/index.d.ts +0 -6
- package/dist/ingestion/index.js +0 -11
- package/dist/ingestion-QBGKrFr9.js +0 -200
- package/dist/ingestion-QBGKrFr9.js.map +0 -1
- package/dist/manifest/index.d.ts +0 -2
- package/dist/manifest-P7kBnch9.js.map +0 -1
- package/dist/metrics-inkCAxKq.js +0 -136
- package/dist/metrics-inkCAxKq.js.map +0 -1
- package/dist/metrics.d.ts +0 -4
- package/dist/metrics.js +0 -5
- package/dist/publisher/index.d.ts +0 -6
- package/dist/publisher/index.js +0 -8
- package/dist/publisher-BdWJOfer.js +0 -138
- package/dist/publisher-BdWJOfer.js.map +0 -1
- package/dist/runtime-config/index.d.ts +0 -4
- package/dist/runtime-config/index.js +0 -5
- package/dist/runtime-config-IbyCVpei.js +0 -60
- package/dist/runtime-config-IbyCVpei.js.map +0 -1
- package/dist/server-D-eLX3yN.js +0 -183
- package/dist/server-D-eLX3yN.js.map +0 -1
- package/dist/service-CUjGhe-7.d.ts +0 -32
- package/dist/service-DiP-XETo.js +0 -8
- package/dist/service-DiP-XETo.js.map +0 -1
- package/dist/state-store/index.d.ts +0 -3
- package/dist/state-store/index.js +0 -5
- package/dist/state-store-BdYulQOY.js.map +0 -1
- package/dist/status-BTTd7AUR.js.map +0 -1
- package/dist/table-bindings-Dp1DhTeS.js +0 -106
- package/dist/table-bindings-Dp1DhTeS.js.map +0 -1
- package/dist/telemetry-j_nxwYfh.js.map +0 -1
- package/dist/types-jVD5Ys43.d.ts +0 -174
- package/dist/webhook/index.d.ts +0 -7
- package/dist/webhook/index.js +0 -9
- package/dist/webhook-MhFcLW-o.js +0 -13
- package/dist/webhook-MhFcLW-o.js.map +0 -1
- package/src/publisher/console.ts +0 -18
- package/src/publisher/index.ts +0 -7
- package/src/publisher/instrumented.ts +0 -75
- package/src/publisher/service.ts +0 -37
- package/src/publisher/wings.ts +0 -204
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { Effect, Metric } from "effect";
|
|
2
|
-
|
|
3
|
-
import type { ResourceBatch } from "../core/types";
|
|
4
|
-
|
|
5
|
-
import { ConnectorError } from "../errors";
|
|
6
|
-
import * as Metrics from "../metrics";
|
|
7
|
-
import { Attr, EventAttr, EventName, addCurrentSpanEvent } from "../telemetry";
|
|
8
|
-
import { Publisher, type PublishSource } from "./service";
|
|
9
|
-
|
|
10
|
-
export const publishBatch = Effect.fnUntraced(function* (options: {
|
|
11
|
-
readonly connector?: string;
|
|
12
|
-
readonly resource: string;
|
|
13
|
-
readonly source: PublishSource;
|
|
14
|
-
readonly batch: ResourceBatch;
|
|
15
|
-
}) {
|
|
16
|
-
const metric = {
|
|
17
|
-
connector: options.connector ?? "unknown",
|
|
18
|
-
resource: options.resource,
|
|
19
|
-
source: options.source,
|
|
20
|
-
};
|
|
21
|
-
const metricAttributes = Metrics.withResourceAttributes(metric);
|
|
22
|
-
const upserts = options.batch.mutations.filter((mutation) => mutation.op === "upsert").length;
|
|
23
|
-
const deletes = options.batch.mutations.length - upserts;
|
|
24
|
-
|
|
25
|
-
const publisher = yield* Publisher;
|
|
26
|
-
const ack = yield* publisher
|
|
27
|
-
.publish({
|
|
28
|
-
resource: options.resource,
|
|
29
|
-
source: options.source,
|
|
30
|
-
batch: options.batch,
|
|
31
|
-
})
|
|
32
|
-
.pipe(
|
|
33
|
-
Effect.trackDuration(Metric.withAttributes(Metrics.publishDuration, metricAttributes)),
|
|
34
|
-
Effect.catchCause((cause) =>
|
|
35
|
-
Metric.update(
|
|
36
|
-
Metric.withAttributes(Metrics.batches, {
|
|
37
|
-
...metricAttributes,
|
|
38
|
-
[Attr.batchOutcome]: "error",
|
|
39
|
-
}),
|
|
40
|
-
1,
|
|
41
|
-
).pipe(Effect.andThen(Effect.failCause(cause))),
|
|
42
|
-
),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
yield* Metric.update(
|
|
46
|
-
Metric.withAttributes(Metrics.batches, {
|
|
47
|
-
...metricAttributes,
|
|
48
|
-
[Attr.batchOutcome]: ack.status,
|
|
49
|
-
}),
|
|
50
|
-
1,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
yield* Effect.annotateCurrentSpan({ [Attr.publisherSuccess]: ack.status === "accepted" });
|
|
54
|
-
if (ack.status === "rejected") {
|
|
55
|
-
yield* Effect.annotateCurrentSpan({ [Attr.errorPhase]: "publish" });
|
|
56
|
-
return yield* Effect.fail(
|
|
57
|
-
new ConnectorError({
|
|
58
|
-
message: `Publisher rejected batch for ${options.resource}: ${ack.reason}`,
|
|
59
|
-
}),
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
yield* Metric.update(Metric.withAttributes(Metrics.entitiesUpserted, metricAttributes), upserts);
|
|
64
|
-
yield* Metric.update(Metric.withAttributes(Metrics.entitiesDeleted, metricAttributes), deletes);
|
|
65
|
-
yield* Metric.update(
|
|
66
|
-
Metric.withAttributes(Metrics.batchSize, metricAttributes),
|
|
67
|
-
options.batch.mutations.length,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (options.batch.cursor !== undefined) {
|
|
71
|
-
yield* addCurrentSpanEvent(EventName.batchCheckpoint, {
|
|
72
|
-
[EventAttr.batchCursor]: options.batch.cursor,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
});
|
package/src/publisher/service.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type * as Wings from "@useairfoil/wings";
|
|
2
|
-
import type * as Effect from "effect/Effect";
|
|
3
|
-
|
|
4
|
-
import { Context } from "effect";
|
|
5
|
-
|
|
6
|
-
import type { ResourceBatch } from "../core/types";
|
|
7
|
-
import type { ConnectorError } from "../errors";
|
|
8
|
-
|
|
9
|
-
export type PublishSource = "backfill" | "changes" | "webhook";
|
|
10
|
-
|
|
11
|
-
export type PublishAck =
|
|
12
|
-
| {
|
|
13
|
-
readonly status: "accepted";
|
|
14
|
-
readonly resource: string;
|
|
15
|
-
readonly partition?: Wings.PartitionValue.PartitionValue;
|
|
16
|
-
}
|
|
17
|
-
| {
|
|
18
|
-
readonly status: "rejected";
|
|
19
|
-
readonly resource: string;
|
|
20
|
-
readonly reason: string;
|
|
21
|
-
readonly rejectedRows?: number;
|
|
22
|
-
readonly partition?: Wings.PartitionValue.PartitionValue;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type PublishOptions = {
|
|
26
|
-
readonly resource: string;
|
|
27
|
-
readonly source: PublishSource;
|
|
28
|
-
readonly batch: ResourceBatch;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export interface PublisherService {
|
|
32
|
-
readonly publish: (options: PublishOptions) => Effect.Effect<PublishAck, ConnectorError>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class Publisher extends Context.Service<Publisher, PublisherService>()(
|
|
36
|
-
"@useairfoil/connector-kit/Publisher",
|
|
37
|
-
) {}
|
package/src/publisher/wings.ts
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import * as Wings from "@useairfoil/wings";
|
|
2
|
-
import { Effect, Layer } from "effect";
|
|
3
|
-
|
|
4
|
-
import type { ConnectorDefinition, ResourceDefinition } from "../core/types";
|
|
5
|
-
|
|
6
|
-
import { ConnectorError } from "../errors";
|
|
7
|
-
import { loadTableBindings } from "../runtime-config/table-bindings";
|
|
8
|
-
import { SpanName } from "../telemetry";
|
|
9
|
-
import { Publisher, type PublishAck, type PublisherService } from "./service";
|
|
10
|
-
|
|
11
|
-
type Rows = Record<string, unknown>;
|
|
12
|
-
|
|
13
|
-
/** Programmatic mapping between a connector resource and a Wings table. */
|
|
14
|
-
export type WingsTableMapping = {
|
|
15
|
-
readonly name: string;
|
|
16
|
-
readonly partitionValue?: Wings.PartitionValue.PartitionValue;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/** Direct Wings publisher configuration, mainly for tests and local runtimes. */
|
|
20
|
-
export type WingsPublisherConfig = {
|
|
21
|
-
readonly connector: ConnectorDefinition;
|
|
22
|
-
readonly tables: Record<string, string | WingsTableMapping>;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type PublisherEntry = {
|
|
26
|
-
readonly resource: ResourceDefinition;
|
|
27
|
-
readonly table: Wings.Cluster.Table.Table;
|
|
28
|
-
readonly publisher: Wings.WingsClient.Publisher;
|
|
29
|
-
readonly partitionValue?: Wings.PartitionValue.PartitionValue;
|
|
30
|
-
readonly keyField: Wings.Cluster.ArrowType.Field;
|
|
31
|
-
readonly versionField: Wings.Cluster.ArrowType.Field;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const normalizeMapping = (mapping: string | WingsTableMapping): WingsTableMapping =>
|
|
35
|
-
typeof mapping === "string" ? { name: mapping } : mapping;
|
|
36
|
-
|
|
37
|
-
const buildRecordBatch = (rows: ReadonlyArray<Rows>) => {
|
|
38
|
-
const table = Wings.Arrow.tableFromJSON(Array.from(rows));
|
|
39
|
-
const [batch] = table.batches;
|
|
40
|
-
return batch
|
|
41
|
-
? Effect.succeed(batch)
|
|
42
|
-
: Effect.fail(new ConnectorError({ message: "No rows to publish" }));
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const validateTableMapping = (options: {
|
|
46
|
-
readonly resource: ResourceDefinition;
|
|
47
|
-
readonly table: Wings.Cluster.Table.Table;
|
|
48
|
-
readonly partitionValue?: Wings.PartitionValue.PartitionValue;
|
|
49
|
-
}) =>
|
|
50
|
-
Effect.try({
|
|
51
|
-
try: () => {
|
|
52
|
-
const keyField = Wings.TableUtils.getKeyField(options.table);
|
|
53
|
-
const versionField = Wings.TableUtils.getVersionField(options.table);
|
|
54
|
-
|
|
55
|
-
if (keyField.name !== options.resource.key) {
|
|
56
|
-
throw new Error(
|
|
57
|
-
`Resource ${options.resource.name} key '${options.resource.key}' does not match Wings table key '${keyField.name}'`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
if (versionField.name !== options.resource.version) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
`Resource ${options.resource.name} version '${options.resource.version}' does not match Wings table version '${versionField.name}'`,
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (options.resource.partition?.required === true && !options.partitionValue) {
|
|
66
|
-
throw new Error(`Resource ${options.resource.name} requires a partition value`);
|
|
67
|
-
}
|
|
68
|
-
Wings.TableUtils.validatePartitionValueUnsafe(options.table, options.partitionValue);
|
|
69
|
-
|
|
70
|
-
return { keyField, versionField };
|
|
71
|
-
},
|
|
72
|
-
catch: (cause) =>
|
|
73
|
-
new ConnectorError({
|
|
74
|
-
message: `Invalid Wings table mapping for ${options.resource.name}`,
|
|
75
|
-
cause,
|
|
76
|
-
}),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const accepted = (entry: PublisherEntry): PublishAck => ({
|
|
80
|
-
status: "accepted",
|
|
81
|
-
resource: entry.resource.name,
|
|
82
|
-
partition: entry.partitionValue,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const rejected = (entry: PublisherEntry, reason: string, rejectedRows: number): PublishAck => ({
|
|
86
|
-
status: "rejected",
|
|
87
|
-
resource: entry.resource.name,
|
|
88
|
-
reason,
|
|
89
|
-
rejectedRows,
|
|
90
|
-
partition: entry.partitionValue,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
/** Builds a Wings publisher from explicit table mappings. */
|
|
94
|
-
export const layerWings = (
|
|
95
|
-
config: WingsPublisherConfig,
|
|
96
|
-
): Layer.Layer<Publisher, ConnectorError, Wings.WingsClient.WingsClient> =>
|
|
97
|
-
Layer.effect(Publisher)(
|
|
98
|
-
Effect.gen(function* () {
|
|
99
|
-
const entries = new Map<string, PublisherEntry>();
|
|
100
|
-
const clusterClient = yield* Wings.WingsClient.clusterClient;
|
|
101
|
-
|
|
102
|
-
for (const resource of config.connector.resources) {
|
|
103
|
-
const mappingInput = config.tables[resource.name];
|
|
104
|
-
if (!mappingInput) {
|
|
105
|
-
return yield* Effect.fail(
|
|
106
|
-
new ConnectorError({ message: `Missing table for ${resource.name}` }),
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const mapping = normalizeMapping(mappingInput);
|
|
111
|
-
const table = yield* clusterClient.getTable({ name: mapping.name }).pipe(
|
|
112
|
-
Effect.mapError(
|
|
113
|
-
(error) =>
|
|
114
|
-
new ConnectorError({
|
|
115
|
-
message: `Failed to resolve Wings table for ${resource.name}`,
|
|
116
|
-
cause: error,
|
|
117
|
-
}),
|
|
118
|
-
),
|
|
119
|
-
);
|
|
120
|
-
const { keyField, versionField } = yield* validateTableMapping({
|
|
121
|
-
resource,
|
|
122
|
-
table,
|
|
123
|
-
partitionValue: mapping.partitionValue,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const publisher = yield* Wings.WingsClient.publisher({
|
|
127
|
-
table,
|
|
128
|
-
partitionValue: mapping.partitionValue,
|
|
129
|
-
}).pipe(
|
|
130
|
-
Effect.mapError((error) => new ConnectorError({ message: error.message, cause: error })),
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
entries.set(resource.name, {
|
|
134
|
-
resource,
|
|
135
|
-
table,
|
|
136
|
-
publisher,
|
|
137
|
-
partitionValue: mapping.partitionValue,
|
|
138
|
-
keyField,
|
|
139
|
-
versionField,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const pushRows = (
|
|
144
|
-
entry: PublisherEntry,
|
|
145
|
-
operation: "upsert" | "delete",
|
|
146
|
-
rows: ReadonlyArray<Rows>,
|
|
147
|
-
) =>
|
|
148
|
-
Effect.gen(function* () {
|
|
149
|
-
if (rows.length === 0) return accepted(entry);
|
|
150
|
-
const batch = yield* buildRecordBatch(rows);
|
|
151
|
-
return yield* entry.publisher
|
|
152
|
-
.push({ operation, batch, partitionValue: entry.partitionValue })
|
|
153
|
-
.pipe(
|
|
154
|
-
Effect.matchEffect({
|
|
155
|
-
onFailure: (error) => Effect.succeed(rejected(entry, error.message, rows.length)),
|
|
156
|
-
onSuccess: () => Effect.succeed(accepted(entry)),
|
|
157
|
-
}),
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const service: PublisherService = {
|
|
162
|
-
publish: Effect.fn(SpanName.publish, { kind: "producer" })(function* ({
|
|
163
|
-
resource,
|
|
164
|
-
source: _source,
|
|
165
|
-
batch,
|
|
166
|
-
}) {
|
|
167
|
-
const entry = entries.get(resource);
|
|
168
|
-
if (!entry) {
|
|
169
|
-
return yield* Effect.fail(
|
|
170
|
-
new ConnectorError({ message: `Unknown resource ${resource}` }),
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (batch.mutations.length === 0) return accepted(entry);
|
|
175
|
-
|
|
176
|
-
const upserts: Rows[] = [];
|
|
177
|
-
const deletes: Rows[] = [];
|
|
178
|
-
for (const mutation of batch.mutations) {
|
|
179
|
-
if (mutation.op === "upsert") {
|
|
180
|
-
upserts.push(mutation.row as Rows);
|
|
181
|
-
} else {
|
|
182
|
-
deletes.push({
|
|
183
|
-
[entry.keyField.name]: mutation.key,
|
|
184
|
-
[entry.versionField.name]: mutation.version,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const upsertAck = yield* pushRows(entry, "upsert", upserts);
|
|
190
|
-
if (upsertAck.status === "rejected") return upsertAck;
|
|
191
|
-
|
|
192
|
-
return yield* pushRows(entry, "delete", deletes);
|
|
193
|
-
}),
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
return Publisher.of(service);
|
|
197
|
-
}),
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
/** Builds a Wings publisher from the complete hosted `AIRFOIL_TABLE_BINDINGS` document. */
|
|
201
|
-
export const layerWingsConfig = (connector: ConnectorDefinition) =>
|
|
202
|
-
Layer.unwrap(
|
|
203
|
-
loadTableBindings(connector).pipe(Effect.map((tables) => layerWings({ connector, tables }))),
|
|
204
|
-
);
|