@stilyng94/athena-query-client 1.0.2 → 1.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/demo.js +31 -14
- package/dist/demo.js.map +1 -1
- package/dist/demo.processor.d.ts +88 -0
- package/dist/demo.processor.js +192 -0
- package/dist/demo.processor.js.map +1 -0
- package/dist/query-results-processor.js +54 -18
- package/dist/query-results-processor.js.map +1 -1
- package/dist/schema.d.ts +72 -0
- package/dist/schema.js +62 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +13 -1
- package/dist/utils.d.ts +21 -0
- package/dist/utils.js +98 -0
- package/dist/utils.js.map +1 -0
- package/package.json +2 -1
package/dist/demo.js
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { DemoS3QueryResultProcessor } from "./demo.processor.js";
|
|
2
|
+
import { JsonFileAppender } from "./json-file-appender.js";
|
|
3
|
+
import { processToJson } from "./utils.js";
|
|
4
|
+
const execute = async () => {
|
|
5
|
+
const fileNamesSet = new Set();
|
|
6
|
+
const s3QueryResultProcessor = new DemoS3QueryResultProcessor({
|
|
7
|
+
s3OutputLocation: "s3://results/queries",
|
|
8
|
+
s3Region: "us-west-2",
|
|
9
|
+
batchSize: 999,
|
|
10
|
+
onData: async (rows) => {
|
|
11
|
+
const fileNames = await processToJson(rows, "conversions");
|
|
12
|
+
// biome-ignore lint/complexity/noForEach: <explanation>
|
|
13
|
+
fileNames.forEach((fileName) => {
|
|
14
|
+
fileNamesSet.add(fileName);
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
onComplete: async () => {
|
|
18
|
+
await Promise.all(Array.from(fileNamesSet).map((fileName) => {
|
|
19
|
+
const jsonFileAppender = new JsonFileAppender({
|
|
20
|
+
fileName,
|
|
21
|
+
directory: "conversions",
|
|
22
|
+
});
|
|
23
|
+
return jsonFileAppender.closeFileWithBracket();
|
|
24
|
+
}));
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
s3QueryResultProcessor
|
|
28
|
+
.processResults("094cedb4-8739-4957-81c1-ecf3e4d8c783")
|
|
29
|
+
.catch((error) => console.error(error));
|
|
30
|
+
};
|
|
31
|
+
execute().catch(console.error);
|
|
15
32
|
//# sourceMappingURL=demo.js.map
|
package/dist/demo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;IACzB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,MAAM,sBAAsB,GAAG,IAAI,0BAA0B,CAAC;QAC5D,gBAAgB,EAAE,sBAAsB;QACxC,QAAQ,EAAE,WAAW;QACrB,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACrB,MAAM,SAAS,GAAG,MAAM,aAAa,CACnC,IASE,EACF,aAAa,CACd,CAAC;YACF,wDAAwD;YACxD,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;gBAC7B,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;QACL,CAAC;QACD,UAAU,EAAE,KAAK,IAAI,EAAE;YACrB,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACxC,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,CAAC;oBAC5C,QAAQ;oBACR,SAAS,EAAE,aAAa;iBACzB,CAAC,CAAC;gBACH,OAAO,gBAAgB,CAAC,oBAAoB,EAAE,CAAC;YACjD,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IACH,sBAAsB;SACnB,cAAc,CAAC,sCAAsC,CAAC;SACtD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Readable } from "node:stream";
|
|
2
|
+
import { type ResultSet, type Row } from "@aws-sdk/client-athena";
|
|
3
|
+
import { type MappedQueryResultProcessorParams, type QueryResultProcessor, type S3QueryResultProcessorParams } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Processes Athena query results stored in S3
|
|
6
|
+
*/
|
|
7
|
+
export declare class DemoS3QueryResultProcessor implements QueryResultProcessor {
|
|
8
|
+
private readonly config;
|
|
9
|
+
readonly batchSize: number;
|
|
10
|
+
readonly s3OutputLocation: string;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new S3QueryResultProcessor
|
|
13
|
+
* @param {S3QueryResultProcessorParams} config - Configuration parameters
|
|
14
|
+
*/
|
|
15
|
+
constructor(config: S3QueryResultProcessorParams);
|
|
16
|
+
/**
|
|
17
|
+
* Processes query results from S3
|
|
18
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
19
|
+
* @returns {Promise<void>} Promise that resolves when processing is complete
|
|
20
|
+
*/
|
|
21
|
+
processResults(queryExecutionId: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Processes streaming results from S3
|
|
24
|
+
* @param {Readable} stream - The readable stream of results
|
|
25
|
+
* @returns {Promise<void>} Promise that resolves when processing is complete
|
|
26
|
+
*/
|
|
27
|
+
processStreamingResults(stream: Readable): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Processes a batch of records
|
|
30
|
+
* @param {unknown[]} batch - Array of records to process
|
|
31
|
+
* @returns {Promise<void>} Promise that resolves when batch is processed
|
|
32
|
+
*/
|
|
33
|
+
processBatch(batch: unknown[]): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Validates the batch size configuration
|
|
36
|
+
* @param {number} [batchSize] - The batch size to validate
|
|
37
|
+
* @returns {number} The validated batch size
|
|
38
|
+
* @throws {Error} If batch size exceeds maximum allowed
|
|
39
|
+
*/
|
|
40
|
+
validateBatchSize(batchSize?: number): number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Processes Athena query results by mapping them to objects
|
|
44
|
+
*/
|
|
45
|
+
export declare class MappedQueryResultProcessor implements QueryResultProcessor {
|
|
46
|
+
private readonly config;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new MappedQueryResultProcessor
|
|
49
|
+
* @param {MappedQueryResultProcessorParams} config - Configuration parameters
|
|
50
|
+
*/
|
|
51
|
+
constructor(config: MappedQueryResultProcessorParams);
|
|
52
|
+
/**
|
|
53
|
+
* Processes query results by mapping them to objects
|
|
54
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
55
|
+
* @returns {Promise<Record<string, string>[]>} Promise resolving to array of mapped objects
|
|
56
|
+
*/
|
|
57
|
+
processResults(queryExecutionId: string): Promise<Record<string, string>[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Fetches query results from Athena
|
|
60
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
61
|
+
* @returns {Promise<ResultSet>} Promise resolving to Athena ResultSet
|
|
62
|
+
* @throws {Error} If results are empty or undefined
|
|
63
|
+
*/
|
|
64
|
+
fetchQueryResults(queryExecutionId: string): Promise<ResultSet>;
|
|
65
|
+
/**
|
|
66
|
+
* Extracts column headers from result rows
|
|
67
|
+
* @param {Row[]} rows - Array of result rows
|
|
68
|
+
* @returns {string[]} Array of header names
|
|
69
|
+
* @throws {Error} If no headers are found
|
|
70
|
+
*/
|
|
71
|
+
extractHeaders(rows: Row[]): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Maps a row to an object using column headers as keys
|
|
74
|
+
* @param {Row} row - The row to map
|
|
75
|
+
* @param {string[]} headers - Array of column headers
|
|
76
|
+
* @returns {Record<string, string>} Object with header-value pairs
|
|
77
|
+
*/
|
|
78
|
+
mapRowToObject(row: Row, headers: string[]): Record<string, string>;
|
|
79
|
+
/**
|
|
80
|
+
* Extracts Athena ResultSet data into an array of key-value objects
|
|
81
|
+
* Extracts Athena ResultSet data into an array of key-value objects
|
|
82
|
+
* @private
|
|
83
|
+
* @param {ResultSet} resultSet - The ResultSet object returned by Athena
|
|
84
|
+
* @returns {Record<string, string>[]} Array of objects representing query rows
|
|
85
|
+
* @throws {Error} If no headers are found
|
|
86
|
+
*/
|
|
87
|
+
extractRows(resultSet: ResultSet): Record<string, string>[];
|
|
88
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { cwd } from "node:process";
|
|
4
|
+
import { finished } from "node:stream/promises";
|
|
5
|
+
import { GetQueryResultsCommand, } from "@aws-sdk/client-athena";
|
|
6
|
+
import { parse } from "csv-parse";
|
|
7
|
+
import { MAX_BATCH_SIZE, } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Processes Athena query results stored in S3
|
|
10
|
+
*/
|
|
11
|
+
export class DemoS3QueryResultProcessor {
|
|
12
|
+
config;
|
|
13
|
+
batchSize = MAX_BATCH_SIZE;
|
|
14
|
+
s3OutputLocation;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new S3QueryResultProcessor
|
|
17
|
+
* @param {S3QueryResultProcessorParams} config - Configuration parameters
|
|
18
|
+
*/
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.batchSize = this.validateBatchSize(config.batchSize);
|
|
22
|
+
this.s3OutputLocation = config.s3OutputLocation;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Processes query results from S3
|
|
26
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
27
|
+
* @returns {Promise<void>} Promise that resolves when processing is complete
|
|
28
|
+
*/
|
|
29
|
+
async processResults(queryExecutionId) {
|
|
30
|
+
console.log("Fetching query results from S3:", queryExecutionId);
|
|
31
|
+
const responseStream = createReadStream(path.join(cwd(), "athena.csv"));
|
|
32
|
+
return this.processStreamingResults(responseStream);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Processes streaming results from S3
|
|
36
|
+
* @param {Readable} stream - The readable stream of results
|
|
37
|
+
* @returns {Promise<void>} Promise that resolves when processing is complete
|
|
38
|
+
*/
|
|
39
|
+
async processStreamingResults(stream) {
|
|
40
|
+
const batch = [];
|
|
41
|
+
let totalProcessed = 0;
|
|
42
|
+
const parser = stream.pipe(parse({ ...(this.config.csvParseOptions ?? { columns: true }) }));
|
|
43
|
+
try {
|
|
44
|
+
parser.on("readable", async () => {
|
|
45
|
+
let record = parser.read();
|
|
46
|
+
while (record !== null) {
|
|
47
|
+
// Work with each record
|
|
48
|
+
batch.push(record);
|
|
49
|
+
if (batch.length >= this.batchSize) {
|
|
50
|
+
console.log(`Processing batch of ${batch.length} records...`);
|
|
51
|
+
await this.processBatch(batch);
|
|
52
|
+
totalProcessed += batch.length;
|
|
53
|
+
batch.length = 0; // Clear array efficiently
|
|
54
|
+
console.log(`Total records processed: ${totalProcessed}`);
|
|
55
|
+
}
|
|
56
|
+
record = parser.read();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Wait for parsing to complete
|
|
60
|
+
await finished(parser);
|
|
61
|
+
// Process remaining records
|
|
62
|
+
if (batch.length > 0) {
|
|
63
|
+
console.log(`Processing final batch of ${batch.length} records...`);
|
|
64
|
+
await this.processBatch(batch);
|
|
65
|
+
totalProcessed += batch.length;
|
|
66
|
+
console.log(`Final total records processed: ${totalProcessed}`);
|
|
67
|
+
}
|
|
68
|
+
await this.config?.onComplete?.();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new Error(`Error processing results: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Processes a batch of records
|
|
76
|
+
* @param {unknown[]} batch - Array of records to process
|
|
77
|
+
* @returns {Promise<void>} Promise that resolves when batch is processed
|
|
78
|
+
*/
|
|
79
|
+
async processBatch(batch) {
|
|
80
|
+
try {
|
|
81
|
+
await this.config.onData(batch);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(`Error processing batch: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validates the batch size configuration
|
|
89
|
+
* @param {number} [batchSize] - The batch size to validate
|
|
90
|
+
* @returns {number} The validated batch size
|
|
91
|
+
* @throws {Error} If batch size exceeds maximum allowed
|
|
92
|
+
*/
|
|
93
|
+
validateBatchSize(batchSize) {
|
|
94
|
+
if (!batchSize)
|
|
95
|
+
return MAX_BATCH_SIZE;
|
|
96
|
+
if (batchSize > MAX_BATCH_SIZE) {
|
|
97
|
+
throw new Error(`Batch size cannot be greater than ${MAX_BATCH_SIZE}`);
|
|
98
|
+
}
|
|
99
|
+
return batchSize;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Processes Athena query results by mapping them to objects
|
|
104
|
+
*/
|
|
105
|
+
export class MappedQueryResultProcessor {
|
|
106
|
+
config;
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new MappedQueryResultProcessor
|
|
109
|
+
* @param {MappedQueryResultProcessorParams} config - Configuration parameters
|
|
110
|
+
*/
|
|
111
|
+
constructor(config) {
|
|
112
|
+
this.config = config;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Processes query results by mapping them to objects
|
|
116
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
117
|
+
* @returns {Promise<Record<string, string>[]>} Promise resolving to array of mapped objects
|
|
118
|
+
*/
|
|
119
|
+
async processResults(queryExecutionId) {
|
|
120
|
+
console.log("Fetching results for QueryExecutionId:", queryExecutionId);
|
|
121
|
+
try {
|
|
122
|
+
const resultSet = await this.fetchQueryResults(queryExecutionId);
|
|
123
|
+
return this.extractRows(resultSet);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw new Error(`Error processing results: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Fetches query results from Athena
|
|
131
|
+
* @param {string} queryExecutionId - The ID of the query execution
|
|
132
|
+
* @returns {Promise<ResultSet>} Promise resolving to Athena ResultSet
|
|
133
|
+
* @throws {Error} If results are empty or undefined
|
|
134
|
+
*/
|
|
135
|
+
async fetchQueryResults(queryExecutionId) {
|
|
136
|
+
const response = await this.config.athenaClient.send(new GetQueryResultsCommand({ QueryExecutionId: queryExecutionId }));
|
|
137
|
+
if (!response.ResultSet) {
|
|
138
|
+
throw new Error("Query results are empty or undefined");
|
|
139
|
+
}
|
|
140
|
+
return response.ResultSet;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extracts column headers from result rows
|
|
144
|
+
* @param {Row[]} rows - Array of result rows
|
|
145
|
+
* @returns {string[]} Array of header names
|
|
146
|
+
* @throws {Error} If no headers are found
|
|
147
|
+
*/
|
|
148
|
+
extractHeaders(rows) {
|
|
149
|
+
const headers = rows[0]?.Data?.map((column) => column.VarCharValue || "");
|
|
150
|
+
if (!headers || headers.length === 0) {
|
|
151
|
+
throw new Error("No headers found in the result set");
|
|
152
|
+
}
|
|
153
|
+
return headers;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Maps a row to an object using column headers as keys
|
|
157
|
+
* @param {Row} row - The row to map
|
|
158
|
+
* @param {string[]} headers - Array of column headers
|
|
159
|
+
* @returns {Record<string, string>} Object with header-value pairs
|
|
160
|
+
*/
|
|
161
|
+
mapRowToObject(row, headers) {
|
|
162
|
+
const mappedRow = {};
|
|
163
|
+
row.Data?.forEach((value, index) => {
|
|
164
|
+
const header = headers[index];
|
|
165
|
+
if (header) {
|
|
166
|
+
mappedRow[header] = value.VarCharValue || "";
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return mappedRow;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Extracts Athena ResultSet data into an array of key-value objects
|
|
173
|
+
* Extracts Athena ResultSet data into an array of key-value objects
|
|
174
|
+
* @private
|
|
175
|
+
* @param {ResultSet} resultSet - The ResultSet object returned by Athena
|
|
176
|
+
* @returns {Record<string, string>[]} Array of objects representing query rows
|
|
177
|
+
* @throws {Error} If no headers are found
|
|
178
|
+
*/
|
|
179
|
+
extractRows(resultSet) {
|
|
180
|
+
const { Rows } = resultSet;
|
|
181
|
+
if (!Rows || Rows.length === 0) {
|
|
182
|
+
console.error("No rows found in result set");
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
const headers = this.extractHeaders(Rows);
|
|
186
|
+
if (!headers.length) {
|
|
187
|
+
throw new Error("No headers found in the result set");
|
|
188
|
+
}
|
|
189
|
+
return Rows.slice(1).map((row) => this.mapRowToObject(row, headers));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=demo.processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.processor.js","sourceRoot":"","sources":["../src/demo.processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,17 @@ import { finished } from 'node:stream/promises';
|
|
|
2
2
|
import { GetQueryResultsCommand, } from '@aws-sdk/client-athena';
|
|
3
3
|
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
4
4
|
import { parse } from 'csv-parse';
|
|
5
|
+
import { EMPTY, defer, firstValueFrom, from, of, throwError, timer, } from 'rxjs';
|
|
5
6
|
import { MAX_BATCH_SIZE, } from './types.js';
|
|
7
|
+
import { catchError, concatWith, expand, filter, switchMap, } from 'rxjs/operators';
|
|
8
|
+
function validateBatchSize(maxBatchSize, batchSize) {
|
|
9
|
+
if (!batchSize)
|
|
10
|
+
return maxBatchSize;
|
|
11
|
+
if (batchSize > maxBatchSize) {
|
|
12
|
+
throw new Error(`Batch size cannot be greater than ${maxBatchSize}`);
|
|
13
|
+
}
|
|
14
|
+
return batchSize;
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Processes Athena query results stored in S3
|
|
8
18
|
*/
|
|
@@ -42,10 +52,10 @@ export class S3QueryResultProcessor {
|
|
|
42
52
|
async #processStreamingResults(stream) {
|
|
43
53
|
const batch = [];
|
|
44
54
|
let totalProcessed = 0;
|
|
45
|
-
const parser = stream.pipe(parse({ ...(this.config.csvParseOptions ?? {}) }));
|
|
55
|
+
const parser = stream.pipe(parse({ columns: true, ...(this.config.csvParseOptions ?? {}) }));
|
|
46
56
|
try {
|
|
47
57
|
parser.on('readable', async () => {
|
|
48
|
-
|
|
58
|
+
let record = parser.read();
|
|
49
59
|
while (record !== null) {
|
|
50
60
|
// Work with each record
|
|
51
61
|
batch.push(record);
|
|
@@ -56,6 +66,7 @@ export class S3QueryResultProcessor {
|
|
|
56
66
|
batch.length = 0; // Clear array efficiently
|
|
57
67
|
console.log(`Total records processed: ${totalProcessed}`);
|
|
58
68
|
}
|
|
69
|
+
record = parser.read();
|
|
59
70
|
}
|
|
60
71
|
});
|
|
61
72
|
// Wait for parsing to complete
|
|
@@ -93,12 +104,7 @@ export class S3QueryResultProcessor {
|
|
|
93
104
|
* @throws {Error} If batch size exceeds maximum allowed
|
|
94
105
|
*/
|
|
95
106
|
#validateBatchSize(batchSize) {
|
|
96
|
-
|
|
97
|
-
return MAX_BATCH_SIZE;
|
|
98
|
-
if (batchSize > MAX_BATCH_SIZE) {
|
|
99
|
-
throw new Error(`Batch size cannot be greater than ${MAX_BATCH_SIZE}`);
|
|
100
|
-
}
|
|
101
|
-
return batchSize;
|
|
107
|
+
return validateBatchSize(this.#batchSize, batchSize);
|
|
102
108
|
}
|
|
103
109
|
/**
|
|
104
110
|
* Fetches an object from S3
|
|
@@ -143,12 +149,16 @@ export class S3QueryResultProcessor {
|
|
|
143
149
|
*/
|
|
144
150
|
export class MappedQueryResultProcessor {
|
|
145
151
|
config;
|
|
152
|
+
#batchSize = MAX_BATCH_SIZE;
|
|
153
|
+
#shouldPaginate = true;
|
|
146
154
|
/**
|
|
147
155
|
* Creates a new MappedQueryResultProcessor
|
|
148
156
|
* @param {MappedQueryResultProcessorParams} config - Configuration parameters
|
|
149
157
|
*/
|
|
150
158
|
constructor(config) {
|
|
151
159
|
this.config = config;
|
|
160
|
+
this.#batchSize = this.#validateBatchSize(config.MaxResults);
|
|
161
|
+
this.#shouldPaginate = config.paginateResults ?? true;
|
|
152
162
|
}
|
|
153
163
|
/**
|
|
154
164
|
* Processes query results by mapping them to objects
|
|
@@ -158,8 +168,8 @@ export class MappedQueryResultProcessor {
|
|
|
158
168
|
async processResults(queryExecutionId) {
|
|
159
169
|
console.log('Fetching results for QueryExecutionId:', queryExecutionId);
|
|
160
170
|
try {
|
|
161
|
-
const
|
|
162
|
-
return this.#extractRows(resultSet);
|
|
171
|
+
const resultSets = await firstValueFrom(this.#fetchQueryResults(queryExecutionId, undefined));
|
|
172
|
+
return resultSets.flatMap((resultSet) => this.#extractRows(resultSet));
|
|
163
173
|
}
|
|
164
174
|
catch (error) {
|
|
165
175
|
throw new Error(`Error processing results: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -168,15 +178,32 @@ export class MappedQueryResultProcessor {
|
|
|
168
178
|
/**
|
|
169
179
|
* Fetches query results from Athena
|
|
170
180
|
* @param {string} queryExecutionId - The ID of the query execution
|
|
171
|
-
* @
|
|
172
|
-
* @
|
|
181
|
+
* @param {string | undefined} nextToken - The pagination token
|
|
182
|
+
* @returns {Promise<ResultSet[]>} Promise resolving to Athena ResultSet
|
|
183
|
+
* @throws {Error} If results are undefined
|
|
173
184
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
185
|
+
#fetchQueryResults(queryExecutionId, nextToken) {
|
|
186
|
+
return defer(() => from(this.config.athenaClient.send(new GetQueryResultsCommand({
|
|
187
|
+
QueryExecutionId: queryExecutionId,
|
|
188
|
+
MaxResults: this.#batchSize,
|
|
189
|
+
NextToken: nextToken,
|
|
190
|
+
})))).pipe(switchMap((response) => {
|
|
191
|
+
if (!response.ResultSet) {
|
|
192
|
+
return throwError(() => new Error(`Query results are undefined for QueryExecutionId: ${queryExecutionId}`));
|
|
193
|
+
}
|
|
194
|
+
const resultObservable = of(response.ResultSet);
|
|
195
|
+
if (this.#shouldPaginate && response.NextToken) {
|
|
196
|
+
console.log('Fetching next page of results...');
|
|
197
|
+
return resultObservable.pipe(concatWith(of(response.NextToken)));
|
|
198
|
+
}
|
|
199
|
+
console.log('Query results fetching complete');
|
|
200
|
+
return resultObservable; // Only emit the current ResultSet
|
|
201
|
+
}), expand((resultOrNextToken) => typeof resultOrNextToken === 'string' // If it's a NextToken, fetch the next page
|
|
202
|
+
? timer(500).pipe(switchMap(() => this.#fetchQueryResults(queryExecutionId, resultOrNextToken)))
|
|
203
|
+
: EMPTY), filter((resultOrNextToken) => typeof resultOrNextToken !== 'string'), catchError((error) => {
|
|
204
|
+
console.error(`Error fetching query results for QueryExecutionId: ${queryExecutionId}`, error);
|
|
205
|
+
return throwError(() => new Error(`Failed to fetch query results for QueryExecutionId: ${queryExecutionId}. ${error.message}`));
|
|
206
|
+
}));
|
|
180
207
|
}
|
|
181
208
|
/**
|
|
182
209
|
* Extracts column headers from result rows
|
|
@@ -227,5 +254,14 @@ export class MappedQueryResultProcessor {
|
|
|
227
254
|
}
|
|
228
255
|
return Rows.slice(1).map((row) => this.#mapRowToObject(row, headers));
|
|
229
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Validates the batch size configuration
|
|
259
|
+
* @param {number} [batchSize] - The batch size to validate
|
|
260
|
+
* @returns {number} The validated batch size
|
|
261
|
+
* @throws {Error} If batch size exceeds maximum allowed
|
|
262
|
+
*/
|
|
263
|
+
#validateBatchSize(batchSize) {
|
|
264
|
+
return validateBatchSize(this.#batchSize, batchSize);
|
|
265
|
+
}
|
|
230
266
|
}
|
|
231
267
|
//# sourceMappingURL=query-results-processor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-results-processor.js","sourceRoot":"","sources":["../src/query-results-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EACL,sBAAsB,GAGvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACjC,OAAO,EACL,cAAc,GAIf,MAAM,YAAY,CAAA;AAEnB;;GAEG;AACH,MAAM,OAAO,sBAAsB;IASJ;IARpB,UAAU,GAAG,cAAc,CAAA;IAC3B,SAAS,CAAU;IACnB,iBAAiB,CAAQ;IAElC;;;OAGG;IACH,YAA6B,MAAoC;QAApC,WAAM,GAAN,MAAM,CAA8B;QAC/D,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC3D,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,gBAAgB,CAAA;QAChD,IAAI,CAAC,SAAS,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,gBAAwB;QAC3C,iCAAiC;QACjC,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,iBAAiB,IAAI,gBAAgB,MAAM,CAAA;QACtE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;QACpD,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,UAAU,CAAC,CAAA;QAE1D,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC7D,OAAO,IAAI,CAAC,wBAAwB,CAAC,cAAc,CAAC,CAAA;IACtD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,wBAAwB,CAAC,MAAgB;QAC7C,MAAM,KAAK,GAAc,EAAE,CAAA;QAC3B,IAAI,cAAc,GAAG,CAAC,CAAA;QAEtB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CACxB,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"query-results-processor.js","sourceRoot":"","sources":["../src/query-results-processor.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const safeUrlDecode: (value: string) => string;
|
|
3
|
+
export declare const urlEncodedString: (schema: z.ZodString) => z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>;
|
|
4
|
+
export declare const datePreprocessor: z.ZodEffects<z.ZodString, string, unknown>;
|
|
5
|
+
export declare const CsiUriQuerySchema: z.ZodObject<{
|
|
6
|
+
type: z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>;
|
|
7
|
+
siteId: z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>;
|
|
8
|
+
currency: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
9
|
+
idorder: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
10
|
+
revenue: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
11
|
+
created_at: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodString, string, unknown>>>;
|
|
12
|
+
shipping: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
13
|
+
discount: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
14
|
+
referrer: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
15
|
+
_idv: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
16
|
+
_id: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
17
|
+
idsite: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
18
|
+
quicktransaction: z.ZodOptional<z.ZodEnum<["0", "1"]>>;
|
|
19
|
+
agent: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
20
|
+
audit_key: z.ZodNullable<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>;
|
|
21
|
+
device_type: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
22
|
+
parent_idv: z.ZodDefault<z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>>;
|
|
23
|
+
country: z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>;
|
|
24
|
+
items_count: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
25
|
+
url: z.ZodOptional<z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, unknown>>;
|
|
26
|
+
date: z.ZodOptional<z.ZodEffects<z.ZodString, string, unknown>>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
currency: string;
|
|
29
|
+
idorder: string;
|
|
30
|
+
revenue: string;
|
|
31
|
+
created_at: string;
|
|
32
|
+
shipping: string;
|
|
33
|
+
discount: string;
|
|
34
|
+
referrer: string;
|
|
35
|
+
_idv: string;
|
|
36
|
+
_id: string;
|
|
37
|
+
idsite: string;
|
|
38
|
+
agent: string;
|
|
39
|
+
audit_key: string | null;
|
|
40
|
+
device_type: string;
|
|
41
|
+
parent_idv: string;
|
|
42
|
+
items_count: string;
|
|
43
|
+
type?: string | undefined;
|
|
44
|
+
siteId?: string | undefined;
|
|
45
|
+
quicktransaction?: "0" | "1" | undefined;
|
|
46
|
+
country?: string | undefined;
|
|
47
|
+
url?: string | undefined;
|
|
48
|
+
date?: string | undefined;
|
|
49
|
+
}, {
|
|
50
|
+
type?: unknown;
|
|
51
|
+
siteId?: unknown;
|
|
52
|
+
currency?: unknown;
|
|
53
|
+
idorder?: unknown;
|
|
54
|
+
revenue?: string | undefined;
|
|
55
|
+
created_at?: unknown;
|
|
56
|
+
shipping?: unknown;
|
|
57
|
+
discount?: string | undefined;
|
|
58
|
+
referrer?: unknown;
|
|
59
|
+
_idv?: unknown;
|
|
60
|
+
_id?: unknown;
|
|
61
|
+
idsite?: string | undefined;
|
|
62
|
+
quicktransaction?: "0" | "1" | undefined;
|
|
63
|
+
agent?: unknown;
|
|
64
|
+
audit_key?: unknown;
|
|
65
|
+
device_type?: unknown;
|
|
66
|
+
parent_idv?: unknown;
|
|
67
|
+
country?: unknown;
|
|
68
|
+
items_count?: string | undefined;
|
|
69
|
+
url?: unknown;
|
|
70
|
+
date?: unknown;
|
|
71
|
+
}>;
|
|
72
|
+
export type CsiUriQuery = z.infer<typeof CsiUriQuerySchema>;
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Helper function to decode URL components safely
|
|
3
|
+
export const safeUrlDecode = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
// Handle different levels of URL encoding
|
|
6
|
+
return decodeURIComponent(decodeURIComponent(value.replace(/\+/g, " ")));
|
|
7
|
+
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
try {
|
|
11
|
+
// Try single decoding if double fails
|
|
12
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Return original if both fail
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
// Create a preprocessor for URL-encoded strings
|
|
21
|
+
export const urlEncodedString = (schema) => z.preprocess((val) => {
|
|
22
|
+
if (typeof val !== "string")
|
|
23
|
+
return val;
|
|
24
|
+
return safeUrlDecode(val);
|
|
25
|
+
}, schema.optional().default(""));
|
|
26
|
+
// Create a preprocessor for dates
|
|
27
|
+
export const datePreprocessor = z.preprocess((val) => {
|
|
28
|
+
if (typeof val !== "string")
|
|
29
|
+
return val;
|
|
30
|
+
const decoded = safeUrlDecode(val);
|
|
31
|
+
try {
|
|
32
|
+
return new Date(decoded).toISOString();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return val;
|
|
36
|
+
}
|
|
37
|
+
}, z.string());
|
|
38
|
+
// Zod schema with all fields optional
|
|
39
|
+
export const CsiUriQuerySchema = z.object({
|
|
40
|
+
type: urlEncodedString(z.string()).optional(),
|
|
41
|
+
siteId: urlEncodedString(z.string().regex(/^\d+$/, "Site ID must be numeric")).optional(),
|
|
42
|
+
currency: urlEncodedString(z.string()).optional().default(""),
|
|
43
|
+
idorder: urlEncodedString(z.string()).optional().default(""),
|
|
44
|
+
revenue: z.string().optional().default(""),
|
|
45
|
+
created_at: datePreprocessor.optional().default(""),
|
|
46
|
+
shipping: urlEncodedString(z.string()).optional().default(""),
|
|
47
|
+
discount: z.string().optional().default(""),
|
|
48
|
+
referrer: urlEncodedString(z.string()).optional().default(""),
|
|
49
|
+
_idv: urlEncodedString(z.string()).optional().default(""),
|
|
50
|
+
_id: urlEncodedString(z.string()).optional().default(""),
|
|
51
|
+
idsite: z.string().optional().default(""),
|
|
52
|
+
quicktransaction: z.enum(["0", "1"]).optional(),
|
|
53
|
+
agent: urlEncodedString(z.string()).optional().default(""),
|
|
54
|
+
audit_key: urlEncodedString(z.string()).nullable(),
|
|
55
|
+
device_type: urlEncodedString(z.string()).optional().default(""),
|
|
56
|
+
parent_idv: urlEncodedString(z.string()).optional().default(""),
|
|
57
|
+
country: urlEncodedString(z.string().length(2, "Country must be a 2-letter code")).optional(),
|
|
58
|
+
items_count: z.string().optional().default(""),
|
|
59
|
+
url: urlEncodedString(z.string().url()).optional(),
|
|
60
|
+
date: datePreprocessor.optional(),
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface S3QueryResultProcessorParams {
|
|
|
46
46
|
onComplete?: (data?: unknown) => Promise<void>;
|
|
47
47
|
/**
|
|
48
48
|
* Optional: The maximum number of records to process in a single batch
|
|
49
|
+
* should be less than or equal to 999
|
|
49
50
|
* @default 999
|
|
50
51
|
*/
|
|
51
52
|
batchSize?: number;
|
|
@@ -61,9 +62,20 @@ export interface S3QueryResultProcessorParams {
|
|
|
61
62
|
}
|
|
62
63
|
export interface MappedQueryResultProcessorParams {
|
|
63
64
|
/**
|
|
64
|
-
*
|
|
65
|
+
* Athena client for executing queries
|
|
65
66
|
*/
|
|
66
67
|
athenaClient: AthenaClient;
|
|
68
|
+
/**
|
|
69
|
+
* Optional: Maximum number of records per query
|
|
70
|
+
* should be less than or equal to 999
|
|
71
|
+
* @default 999
|
|
72
|
+
*/
|
|
73
|
+
MaxResults?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Should results be paginated. Athena has a limit of 1000 records per query, so this is useful for large datasets
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
paginateResults?: boolean;
|
|
67
79
|
}
|
|
68
80
|
/**
|
|
69
81
|
*@description Options for configuring the JsonFileAppender
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*@description Interface for mapped data row structure
|
|
3
|
+
*/
|
|
4
|
+
interface MappedDataRow {
|
|
5
|
+
partition_date: string;
|
|
6
|
+
hour: string;
|
|
7
|
+
idorder: string;
|
|
8
|
+
idsite: string;
|
|
9
|
+
siteid: string;
|
|
10
|
+
sid: string;
|
|
11
|
+
cs_uri_query: string;
|
|
12
|
+
cs_referer: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @description Processes mapped data and writes to JSON files grouped by date
|
|
16
|
+
* @param {MappedDataRow[]} mappedData - Data to be processed
|
|
17
|
+
* @param {string} directory - Output directory for files
|
|
18
|
+
* @returns {Promise<string[]>} Array of created filenames
|
|
19
|
+
*/
|
|
20
|
+
export declare function processToJson(mappedData: MappedDataRow[], directory: string): Promise<string[]>;
|
|
21
|
+
export {};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { JsonFileAppender } from "./json-file-appender.js";
|
|
2
|
+
import { CsiUriQuerySchema } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
*@description Parses a CSI_URI query string into a structured object
|
|
5
|
+
* @param {string} queryString - The query string to parse
|
|
6
|
+
* @returns {CsiUriQuery} Parsed and validated query object
|
|
7
|
+
*/
|
|
8
|
+
const parseCsiUriQuery = (queryString) => {
|
|
9
|
+
const cleanQueryString = queryString
|
|
10
|
+
.replace(/^cs_uri_query:\s*['"]?/, "")
|
|
11
|
+
.replace(/['"]$/, "");
|
|
12
|
+
// Create params object
|
|
13
|
+
const params = {};
|
|
14
|
+
// Split and parse, handling both & and ; separators
|
|
15
|
+
// biome-ignore lint/complexity/noForEach: <explanation>
|
|
16
|
+
cleanQueryString.split(/[&;]/).forEach((param) => {
|
|
17
|
+
const [key, ...values] = param.split("=");
|
|
18
|
+
if (key) {
|
|
19
|
+
// Join values back together in case the value contained = signs
|
|
20
|
+
params[key.trim()] = values.join("=");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
// Validate and return
|
|
24
|
+
const parsed = CsiUriQuerySchema.parse(params);
|
|
25
|
+
return parsed;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
*@description Converts pixel log data to conversion format
|
|
29
|
+
* @param {MappedDataRow} mappedRow - Raw mapped data
|
|
30
|
+
* @param {CsiUriQuery} csiUriQuery - Parsed URI query parameters
|
|
31
|
+
*/
|
|
32
|
+
const pixelLog = (mappedRow, csiUriQuery) => {
|
|
33
|
+
const conversion = {
|
|
34
|
+
created_at: `${new Date(mappedRow.partition_date).toISOString().split("T")[0]} ${mappedRow.hour}:00`,
|
|
35
|
+
date: new Date(mappedRow.partition_date).toISOString().split("T")[0] ?? "",
|
|
36
|
+
idorder: mappedRow.idorder,
|
|
37
|
+
revenue: csiUriQuery.revenue,
|
|
38
|
+
currency: csiUriQuery.currency,
|
|
39
|
+
parent_idv: csiUriQuery._idv,
|
|
40
|
+
_idv: csiUriQuery._idv,
|
|
41
|
+
nginxLogs: true,
|
|
42
|
+
};
|
|
43
|
+
return conversion;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* @description Processes mapped data and writes to JSON files grouped by date
|
|
47
|
+
* @param {MappedDataRow[]} mappedData - Data to be processed
|
|
48
|
+
* @param {string} directory - Output directory for files
|
|
49
|
+
* @returns {Promise<string[]>} Array of created filenames
|
|
50
|
+
*/
|
|
51
|
+
export async function processToJson(mappedData, directory) {
|
|
52
|
+
console.log(`Processing ${mappedData.length} rows to directory: ${directory}`);
|
|
53
|
+
const fileNames = [];
|
|
54
|
+
try {
|
|
55
|
+
// Group data by date
|
|
56
|
+
const dateGroup = mappedData.reduce((acc, row) => {
|
|
57
|
+
const csiUriQuery = parseCsiUriQuery(row.cs_uri_query);
|
|
58
|
+
const log = pixelLog(row, csiUriQuery);
|
|
59
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
60
|
+
const date = log.created_at.split(" ")[0];
|
|
61
|
+
// ignore log if idorder is empty or _idv is empty or idv value is undefined or revenue cannot be parsed as number
|
|
62
|
+
if (!log.idorder ||
|
|
63
|
+
log.idorder === "undefined" ||
|
|
64
|
+
!log._idv ||
|
|
65
|
+
log._idv === "undefined" ||
|
|
66
|
+
!log.revenue ||
|
|
67
|
+
Number.isNaN(Number(log.revenue))) {
|
|
68
|
+
return acc;
|
|
69
|
+
}
|
|
70
|
+
if (!acc[date]) {
|
|
71
|
+
acc[date] = [];
|
|
72
|
+
}
|
|
73
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
74
|
+
acc[date].push(log);
|
|
75
|
+
return acc;
|
|
76
|
+
}, {});
|
|
77
|
+
console.log(`Grouped data into ${Object.keys(dateGroup).length} dates`);
|
|
78
|
+
const jsonFileAppenderPromises = Object.entries(dateGroup).map(async ([date, data]) => {
|
|
79
|
+
const filename = `conversion_${date}.json`;
|
|
80
|
+
console.log(`Processing ${date}: ${data.length} records`);
|
|
81
|
+
fileNames.push(filename);
|
|
82
|
+
const jsonFileAppender = new JsonFileAppender({
|
|
83
|
+
fileName: filename,
|
|
84
|
+
directory: directory,
|
|
85
|
+
});
|
|
86
|
+
await jsonFileAppender.flush(data);
|
|
87
|
+
console.log(`Completed writing ${filename}`);
|
|
88
|
+
});
|
|
89
|
+
await Promise.all(jsonFileAppenderPromises);
|
|
90
|
+
console.log("All files processed successfully");
|
|
91
|
+
return fileNames;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error("Error in processToJson:", error);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stilyng94/athena-query-client",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "An Athena client to query data from Athena utilizing Aws-S3 or direct results from Athena query",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@aws-sdk/client-athena": "^3.696.0",
|
|
30
30
|
"@aws-sdk/client-s3": "^3.717.0",
|
|
31
31
|
"csv-parse": "^5.6.0",
|
|
32
|
+
"pino": "^9.6.0",
|
|
32
33
|
"rxjs": "^7.8.1"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|