snowpipe-streaming 1.4.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/README.md +104 -0
- package/package.json +37 -0
- package/src/channel.js +241 -0
- package/src/channel_status.js +179 -0
- package/src/client.js +216 -0
- package/src/errors.js +110 -0
- package/src/index.d.ts +351 -0
- package/src/index.js +51 -0
- package/src/loader.js +167 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Snowflake Streaming Ingest Node.js SDK
|
|
2
|
+
|
|
3
|
+
A high-performance Node.js SDK for streaming data ingestion into Snowflake, built with Rust for optimal performance and reliability.
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Supported Platforms](#supported-platforms)
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Architecture](#architecture)
|
|
11
|
+
- [Dependencies](#dependencies)
|
|
12
|
+
- [Support](#support)
|
|
13
|
+
- [License](#license)
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
The Snowflake Streaming Ingest Node.js SDK provides a Node.js interface for real-time data streaming into Snowflake tables. It leverages a Rust core for high performance while providing a familiar JavaScript API for easy integration.
|
|
18
|
+
|
|
19
|
+
## Supported Platforms
|
|
20
|
+
|
|
21
|
+
| OS | Architecture | C Library | Notes |
|
|
22
|
+
|----|-------------|-----------|-------|
|
|
23
|
+
| Linux | x64 | glibc (>= 2.26) | |
|
|
24
|
+
| Linux | x64 | musl | Alpine Linux, etc. |
|
|
25
|
+
| Linux | arm64 | glibc (>= 2.26) | |
|
|
26
|
+
| Linux | arm64 | musl | Alpine Linux, etc. |
|
|
27
|
+
| macOS | arm64 | — | Apple Silicon, macOS 11.0+ |
|
|
28
|
+
| Windows | x64 | MSVC | Windows 10+ |
|
|
29
|
+
|
|
30
|
+
### Node.js
|
|
31
|
+
- **Node.js 20** or later
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install snowpipe-streaming
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The correct native binary for your platform is installed automatically via `optionalDependencies`.
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const { createClient } = require("snowpipe-streaming");
|
|
45
|
+
|
|
46
|
+
// Create a client
|
|
47
|
+
const client = await createClient({
|
|
48
|
+
clientName: "my-client",
|
|
49
|
+
dbName: "MY_DB",
|
|
50
|
+
schemaName: "MY_SCHEMA",
|
|
51
|
+
pipeName: "MY_PIPE",
|
|
52
|
+
properties: {
|
|
53
|
+
account: "your_account",
|
|
54
|
+
user: "your_user",
|
|
55
|
+
private_key: "-----BEGIN PRIVATE KEY-----\n...",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Open a channel
|
|
60
|
+
const { channel } = await client.openChannel({ name: "my_channel" });
|
|
61
|
+
|
|
62
|
+
// Insert data
|
|
63
|
+
await channel.insertRows([
|
|
64
|
+
{ id: 1, name: "John Doe", timestamp: "2024-01-01T00:00:00Z" },
|
|
65
|
+
{ id: 2, name: "Jane Smith", timestamp: "2024-01-02T00:00:00Z" },
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
// Wait for data to be committed to Snowflake
|
|
69
|
+
await channel.waitForCommit();
|
|
70
|
+
|
|
71
|
+
// Close resources
|
|
72
|
+
await channel.close();
|
|
73
|
+
await client.close();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Features
|
|
77
|
+
|
|
78
|
+
- **High Performance**: Rust-based core for optimal throughput and low latency
|
|
79
|
+
- **Memory Efficient**: Minimal memory footprint with efficient data handling
|
|
80
|
+
- **Automatic Retries**: Built-in retry logic for transient failures
|
|
81
|
+
- **Backpressure Handling**: Intelligent backpressure management
|
|
82
|
+
- **Comprehensive Logging**: Detailed logging for debugging and monitoring
|
|
83
|
+
- **Cross-Platform**: Native support for Linux, macOS, and Windows
|
|
84
|
+
- **Zero Dependencies**: No JavaScript runtime dependencies
|
|
85
|
+
|
|
86
|
+
## Architecture
|
|
87
|
+
|
|
88
|
+
The SDK uses a hybrid Rust-Node.js architecture:
|
|
89
|
+
- **Rust Core**: High-performance data processing, networking, and Snowflake communication
|
|
90
|
+
- **Node.js Bindings**: JavaScript API using napi-rs for seamless integration
|
|
91
|
+
- **Platform Packages**: Architecture-specific native binaries distributed as npm optional dependencies
|
|
92
|
+
|
|
93
|
+
## Dependencies
|
|
94
|
+
|
|
95
|
+
- **None**: The SDK has zero JavaScript runtime dependencies
|
|
96
|
+
- **Native Extensions**: Rust-based binary modules (automatically installed for your platform)
|
|
97
|
+
|
|
98
|
+
## Support
|
|
99
|
+
|
|
100
|
+
- **Documentation**: [Snowpipe Streaming Overview](https://docs.snowflake.com/en/user-guide/snowpipe-streaming/data-load-snowpipe-streaming-overview)
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
This project is licensed under the Apache License 2.0.
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "snowpipe-streaming",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Snowflake Streaming Ingest SDK for Node.js",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src/"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"snowflake",
|
|
12
|
+
"snowpipe",
|
|
13
|
+
"streaming",
|
|
14
|
+
"ingest"
|
|
15
|
+
],
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Snowflake Computing, Inc.",
|
|
18
|
+
"email": "support@snowflake.com",
|
|
19
|
+
"url": "https://www.snowflake.com/"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.6.0",
|
|
27
|
+
"typedoc": "^0.28.19"
|
|
28
|
+
},
|
|
29
|
+
"optionalDependencies": {
|
|
30
|
+
"snowpipe-streaming-linux-x64-gnu": "1.4.0",
|
|
31
|
+
"snowpipe-streaming-linux-x64-musl": "1.4.0",
|
|
32
|
+
"snowpipe-streaming-linux-arm64-gnu": "1.4.0",
|
|
33
|
+
"snowpipe-streaming-linux-arm64-musl": "1.4.0",
|
|
34
|
+
"snowpipe-streaming-darwin-arm64": "1.4.0",
|
|
35
|
+
"snowpipe-streaming-win32-x64-msvc": "1.4.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/channel.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Copyright 2026 Snowflake Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
|
|
16
|
+
"use strict";
|
|
17
|
+
|
|
18
|
+
const { callFFI, StreamingIngestError } = require("./errors.js");
|
|
19
|
+
const { ChannelStatus } = require("./channel_status.js");
|
|
20
|
+
|
|
21
|
+
const WAIT_FOR_COMMIT_CHECK_INTERVAL_MS = 1000;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Streaming ingest channel for ingesting data into a Snowflake table.
|
|
25
|
+
*
|
|
26
|
+
* Channels are created via {@link StreamingIngestClient#openChannel},
|
|
27
|
+
* not directly.
|
|
28
|
+
*/
|
|
29
|
+
class StreamingIngestChannel {
|
|
30
|
+
/** @param {object} nativeChannel - Internal JsChannel from FFI */
|
|
31
|
+
constructor(nativeChannel) {
|
|
32
|
+
if (!(nativeChannel && typeof nativeChannel === "object")) {
|
|
33
|
+
throw new TypeError(
|
|
34
|
+
"StreamingIngestChannel cannot be instantiated directly. " +
|
|
35
|
+
"Use client.openChannel() instead.",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
this._native = nativeChannel;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @returns {string} The channel name */
|
|
42
|
+
get channelName() {
|
|
43
|
+
return this._native.channelName;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @returns {string} The database name */
|
|
47
|
+
get dbName() {
|
|
48
|
+
return this._native.dbName;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @returns {string} The schema name */
|
|
52
|
+
get schemaName() {
|
|
53
|
+
return this._native.schemaName;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @returns {string} The pipe name */
|
|
57
|
+
get pipeName() {
|
|
58
|
+
return this._native.pipeName;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @returns {boolean} True if the channel is closed */
|
|
62
|
+
get isClosed() {
|
|
63
|
+
return this._native.isClosed;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the binary input format for this channel.
|
|
68
|
+
* @returns {string} One of: "BASE64", "HEX", "UTF-8"
|
|
69
|
+
*/
|
|
70
|
+
get binaryInputFormat() {
|
|
71
|
+
return this._native.binaryInputFormat;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Convert Buffer/Uint8Array values in a row according to binaryInputFormat.
|
|
76
|
+
* For HEX, binary values become hex-encoded strings.
|
|
77
|
+
* For UTF-8, binary values are decoded as UTF-8 strings.
|
|
78
|
+
* Only top-level values are converted; nested structures are left as-is.
|
|
79
|
+
* @param {Object} row - Row object
|
|
80
|
+
* @returns {Object} Row with binary values converted
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
_convertBytesInRow(row) {
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const [key, value] of Object.entries(row)) {
|
|
86
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
87
|
+
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
88
|
+
if (this.binaryInputFormat === "HEX") {
|
|
89
|
+
result[key] = buf.toString("hex");
|
|
90
|
+
} else if (this.binaryInputFormat === "UTF-8") {
|
|
91
|
+
result[key] = buf.toString("utf8");
|
|
92
|
+
} else {
|
|
93
|
+
// BASE64: convert to base64 string for JSON serialization
|
|
94
|
+
result[key] = buf.toString("base64");
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
result[key] = value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Append a single row. Convenience wrapper for appendRows.
|
|
105
|
+
* @param {Object} row - Row data as key-value pairs
|
|
106
|
+
* @param {string} [offsetToken] - Offset token for this row
|
|
107
|
+
* @returns {Promise<void>}
|
|
108
|
+
*/
|
|
109
|
+
async appendRow(row, offsetToken) {
|
|
110
|
+
await this.appendRows([row], offsetToken, offsetToken);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Append multiple rows to the channel.
|
|
115
|
+
* Rows are serialized to NDJSON (newline-delimited JSON) on the JS side.
|
|
116
|
+
* @param {Object[]} rows - Array of row objects
|
|
117
|
+
* @param {string} [startOffsetToken] - Start offset token for this batch
|
|
118
|
+
* @param {string} [endOffsetToken] - End offset token for this batch
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
async appendRows(rows, startOffsetToken, endOffsetToken) {
|
|
122
|
+
if (!Array.isArray(rows)) {
|
|
123
|
+
throw new TypeError("rows must be an array");
|
|
124
|
+
}
|
|
125
|
+
// Empty arrays are handled by the Rust core, which returns a proper InvalidRequest error
|
|
126
|
+
if (rows.length === 0) {
|
|
127
|
+
// Pass empty buffer to Rust core to get proper error message
|
|
128
|
+
return callFFI(() =>
|
|
129
|
+
this._native.appendRows(Buffer.alloc(0), 0, startOffsetToken, endOffsetToken),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
// Convert binary values according to binaryInputFormat before JSON serialization
|
|
133
|
+
const ndjson = rows.map((r) => JSON.stringify(this._convertBytesInRow(r))).join("\n");
|
|
134
|
+
const buffer = Buffer.from(ndjson, "utf8");
|
|
135
|
+
return callFFI(() =>
|
|
136
|
+
this._native.appendRows(buffer, rows.length, startOffsetToken, endOffsetToken),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Close the channel. Idempotent — safe to call multiple times.
|
|
142
|
+
* @param {{drop?: boolean, waitForFlush?: boolean, timeoutMs?: number}} [options]
|
|
143
|
+
* @returns {Promise<void>}
|
|
144
|
+
*/
|
|
145
|
+
async close(options) {
|
|
146
|
+
const nativeOpts = options
|
|
147
|
+
? { drop: options.drop, waitForFlush: options.waitForFlush, timeoutMs: options.timeoutMs }
|
|
148
|
+
: undefined;
|
|
149
|
+
return callFFI(() => this._native.close(nativeOpts));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Trigger a flush of this channel (non-blocking).
|
|
154
|
+
* @throws {StreamingIngestError} If the flush cannot be initiated
|
|
155
|
+
*/
|
|
156
|
+
initiateFlush() {
|
|
157
|
+
try {
|
|
158
|
+
this._native.initiateFlush();
|
|
159
|
+
} catch (e) {
|
|
160
|
+
throw StreamingIngestError.from(e);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Wait for this channel to flush.
|
|
166
|
+
* @param {{timeoutMs?: number}} [options]
|
|
167
|
+
* @returns {Promise<void>}
|
|
168
|
+
*/
|
|
169
|
+
async waitForFlush(options) {
|
|
170
|
+
return callFFI(() => this._native.waitForFlush(options?.timeoutMs));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the current status of this channel.
|
|
175
|
+
* @returns {Promise<ChannelStatus>}
|
|
176
|
+
*/
|
|
177
|
+
async getChannelStatus() {
|
|
178
|
+
const raw = await callFFI(() => this._native.getChannelStatus());
|
|
179
|
+
return new ChannelStatus(raw);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get the latest committed offset token.
|
|
184
|
+
* @returns {Promise<string|null>}
|
|
185
|
+
*/
|
|
186
|
+
async getLatestCommittedOffsetToken() {
|
|
187
|
+
const status = await this.getChannelStatus();
|
|
188
|
+
return status.latestCommittedOffsetToken;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Wait for a specific offset token to be committed.
|
|
193
|
+
* Polls getChannelStatus() every 1 second until tokenChecker returns true.
|
|
194
|
+
* @param {function(string|null): boolean} tokenChecker - Returns true when the desired token is committed
|
|
195
|
+
* @param {{timeoutMs?: number}} [options]
|
|
196
|
+
* @returns {Promise<void>}
|
|
197
|
+
*/
|
|
198
|
+
async waitForCommit(tokenChecker, options) {
|
|
199
|
+
if (typeof tokenChecker !== "function") {
|
|
200
|
+
throw new TypeError("tokenChecker must be a function");
|
|
201
|
+
}
|
|
202
|
+
const timeoutMs = options?.timeoutMs;
|
|
203
|
+
if (timeoutMs !== undefined && timeoutMs < 0) {
|
|
204
|
+
throw new RangeError("timeoutMs cannot be negative");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const startTime = Date.now();
|
|
208
|
+
|
|
209
|
+
while (true) {
|
|
210
|
+
const status = await this.getChannelStatus();
|
|
211
|
+
|
|
212
|
+
if (tokenChecker(status.latestCommittedOffsetToken)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (status.statusCode.toUpperCase() !== "SUCCESS") {
|
|
217
|
+
throw new StreamingIngestError(
|
|
218
|
+
"InvalidChannelError",
|
|
219
|
+
`Channel is invalid in the remote with status: ${status.statusCode}`,
|
|
220
|
+
409,
|
|
221
|
+
"Conflict",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (timeoutMs !== undefined && Date.now() - startTime >= timeoutMs) {
|
|
226
|
+
throw new Error("Wait for commit timed out");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await new Promise((resolve) => setTimeout(resolve, WAIT_FOR_COMMIT_CHECK_INTERVAL_MS));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Support `await using channel = ...` (Node 20+) */
|
|
234
|
+
async [Symbol.asyncDispose]() {
|
|
235
|
+
if (!this.isClosed) {
|
|
236
|
+
await this.close();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = { StreamingIngestChannel };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Copyright 2026 Snowflake Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
|
|
16
|
+
"use strict";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Channel status information from Snowflake server.
|
|
20
|
+
*
|
|
21
|
+
* This class wraps the raw status data returned from the Rust FFI layer
|
|
22
|
+
* and provides a clean JavaScript API with proper getters.
|
|
23
|
+
*/
|
|
24
|
+
class ChannelStatus {
|
|
25
|
+
/**
|
|
26
|
+
* Create a ChannelStatus from raw FFI data.
|
|
27
|
+
* @param {Object} data - Raw status data from FFI (NodejsChannelStatus struct)
|
|
28
|
+
*/
|
|
29
|
+
constructor(data) {
|
|
30
|
+
if (!data || typeof data !== "object") {
|
|
31
|
+
throw new TypeError("ChannelStatus requires a data object");
|
|
32
|
+
}
|
|
33
|
+
this._databaseName = data.databaseName;
|
|
34
|
+
this._schemaName = data.schemaName;
|
|
35
|
+
this._pipeName = data.pipeName;
|
|
36
|
+
this._channelName = data.channelName;
|
|
37
|
+
this._statusCode = data.statusCode;
|
|
38
|
+
this._latestCommittedOffsetToken = data.latestCommittedOffsetToken;
|
|
39
|
+
this._createdOnMs = data.createdOnMs;
|
|
40
|
+
this._rowsInserted = data.rowsInserted;
|
|
41
|
+
this._rowsParsed = data.rowsParsed;
|
|
42
|
+
this._rowsErrorCount = data.rowsErrorCount;
|
|
43
|
+
this._lastErrorOffsetUpperBound = data.lastErrorOffsetUpperBound;
|
|
44
|
+
this._lastErrorMessage = data.lastErrorMessage;
|
|
45
|
+
this._lastErrorTimestampMs = data.lastErrorTimestampMs;
|
|
46
|
+
this._snowflakeAvgProcessingLatencyMs = data.snowflakeAvgProcessingLatencyMs;
|
|
47
|
+
this._lastRefreshedOnMs = data.lastRefreshedOnMs;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the database name.
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
get databaseName() {
|
|
55
|
+
return this._databaseName;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the schema name.
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
get schemaName() {
|
|
63
|
+
return this._schemaName;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the pipe name.
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
get pipeName() {
|
|
71
|
+
return this._pipeName;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get the channel name.
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
get channelName() {
|
|
79
|
+
return this._channelName;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the status code from Snowflake server.
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
get statusCode() {
|
|
87
|
+
return this._statusCode;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the latest committed offset token.
|
|
92
|
+
* @returns {string|null}
|
|
93
|
+
*/
|
|
94
|
+
get latestCommittedOffsetToken() {
|
|
95
|
+
return this._latestCommittedOffsetToken;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the created on timestamp as a Date.
|
|
100
|
+
* @returns {Date}
|
|
101
|
+
*/
|
|
102
|
+
get createdOn() {
|
|
103
|
+
return this._createdOnMs != null ? new Date(this._createdOnMs) : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the number of rows inserted.
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
get rowsInsertedCount() {
|
|
111
|
+
return this._rowsInserted;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the number of rows parsed.
|
|
116
|
+
* @returns {number}
|
|
117
|
+
*/
|
|
118
|
+
get rowsParsedCount() {
|
|
119
|
+
return this._rowsParsed;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the number of rows with errors.
|
|
124
|
+
* @returns {number}
|
|
125
|
+
*/
|
|
126
|
+
get rowsErrorCount() {
|
|
127
|
+
return this._rowsErrorCount;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the last error offset token upper bound.
|
|
132
|
+
* @returns {string|null}
|
|
133
|
+
*/
|
|
134
|
+
get lastErrorOffsetTokenUpperBound() {
|
|
135
|
+
return this._lastErrorOffsetUpperBound;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get the last error message.
|
|
140
|
+
* @returns {string|null}
|
|
141
|
+
*/
|
|
142
|
+
get lastErrorMessage() {
|
|
143
|
+
return this._lastErrorMessage;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the last error timestamp as a Date.
|
|
148
|
+
* @returns {Date|null}
|
|
149
|
+
*/
|
|
150
|
+
get lastErrorTimestamp() {
|
|
151
|
+
return this._lastErrorTimestampMs != null ? new Date(this._lastErrorTimestampMs) : null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get the Snowflake average processing latency in milliseconds.
|
|
156
|
+
* @returns {number|null}
|
|
157
|
+
*/
|
|
158
|
+
get serverAvgProcessingLatencyMs() {
|
|
159
|
+
return this._snowflakeAvgProcessingLatencyMs;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get the last refreshed on timestamp as a Date.
|
|
164
|
+
* @returns {Date}
|
|
165
|
+
*/
|
|
166
|
+
get lastRefreshedOn() {
|
|
167
|
+
return this._lastRefreshedOnMs != null ? new Date(this._lastRefreshedOnMs) : null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns a string representation of the channel status.
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
toString() {
|
|
175
|
+
return `Channel '${this._channelName}': ${this._statusCode}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { ChannelStatus };
|