@adobe/spacecat-shared-athena-client 1.0.3 → 1.1.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [@adobe/spacecat-shared-athena-client-v1.1.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-athena-client-v1.0.4...@adobe/spacecat-shared-athena-client-v1.1.0) (2025-07-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * add traffic queries for traffic analysis to shared location ([#847](https://github.com/adobe/spacecat-shared/issues/847)) ([4eecc72](https://github.com/adobe/spacecat-shared/commit/4eecc7289ba630e37fea923262b13e70528ce250))
7
+
8
+ # [@adobe/spacecat-shared-athena-client-v1.0.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-athena-client-v1.0.3...@adobe/spacecat-shared-athena-client-v1.0.4) (2025-07-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * add pagination for athena ([#848](https://github.com/adobe/spacecat-shared/issues/848)) ([2007cfa](https://github.com/adobe/spacecat-shared/commit/2007cfad79f1921ad015c9c0301cf790f9b9405d))
14
+
1
15
  # [@adobe/spacecat-shared-athena-client-v1.0.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-athena-client-v1.0.2...@adobe/spacecat-shared-athena-client-v1.0.3) (2025-07-12)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-athena-client",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Shared modules of the Spacecat Services - AWS Athena Client",
5
5
  "type": "module",
6
6
  "engines": {
package/src/index.js CHANGED
@@ -14,8 +14,8 @@ import {
14
14
  AthenaClient,
15
15
  StartQueryExecutionCommand,
16
16
  GetQueryExecutionCommand,
17
- GetQueryResultsCommand,
18
17
  QueryExecutionState,
18
+ paginateGetQueryResults,
19
19
  } from '@aws-sdk/client-athena';
20
20
  import { instrumentAWSClient, hasText } from '@adobe/spacecat-shared-utils';
21
21
 
@@ -220,29 +220,40 @@ export class AWSAthenaClient {
220
220
  * @returns {Promise<Array>} – Parsed query results
221
221
  */
222
222
  async query(sql, database, description = 'Athena query', opts = {}) {
223
- const {
224
- backoffMs = this.backoffMs,
225
- maxRetries = this.maxRetries,
226
- pollIntervalMs = this.pollIntervalMs,
227
- maxPollAttempts = this.maxPollAttempts,
228
- } = opts;
223
+ const queryExecutionId = await this.execute(sql, database, description, opts);
229
224
 
230
- this.log.info(`[Athena Client] Executing: ${description}`);
225
+ this.log.debug(`[Athena Client] Fetching paginated results for QueryExecutionId=${queryExecutionId}`);
231
226
 
232
- const queryExecutionId = await this.#startQueryWithRetry(
233
- sql,
234
- database,
235
- description,
236
- backoffMs,
237
- maxRetries,
238
- );
227
+ const paginationConfig = {
228
+ client: this.client,
229
+ };
239
230
 
240
- await this.#pollToCompletion(queryExecutionId, description, pollIntervalMs, maxPollAttempts);
231
+ const input = {
232
+ QueryExecutionId: queryExecutionId,
233
+ };
241
234
 
242
- const results = await this.client.send(
243
- new GetQueryResultsCommand({ QueryExecutionId: queryExecutionId }),
244
- );
235
+ const paginator = paginateGetQueryResults(paginationConfig, input);
236
+
237
+ const allResults = [];
238
+ let pageCount = 0;
239
+ let totalRows = 0;
245
240
 
246
- return AWSAthenaClient.#parseAthenaResults(results);
241
+ /* c8 ignore start */
242
+ for await (const page of paginator) {
243
+ pageCount += 1;
244
+ const pageRows = page.ResultSet?.Rows?.length || 0;
245
+ totalRows += pageRows;
246
+
247
+ this.log.debug(`[Athena Client] Processing page ${pageCount} with ${pageRows} rows`);
248
+
249
+ const pageResults = AWSAthenaClient.#parseAthenaResults(page);
250
+ allResults.push(...pageResults);
251
+ }
252
+ /* c8 ignore stop */
253
+
254
+ this.log.info(`[Athena Client] Fetched ${totalRows} total rows across ${pageCount} pages`);
255
+ return allResults;
247
256
  }
248
257
  }
258
+
259
+ export { getTrafficAnalysisQuery, getTrafficAnalysisQueryPlaceholders } from './queries.js';
package/src/queries.js ADDED
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright 2025 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+ import { getStaticContent } from '@adobe/spacecat-shared-utils';
13
+
14
+ const TRAFIC_ANALYSIS_PATH = 'static/queries/traffic-analysis.sql.tpl';
15
+
16
+ /**
17
+ * Loads the traffic analysis query template and applies placeholders.
18
+ * @param {Object} placeholders - Key-value pairs to replace in the query template.
19
+ * @param {Object} log - Logger (optional)
20
+ * @returns {Promise<string|null>} The templated SQL string or null on error.
21
+ */
22
+ export async function getTrafficAnalysisQuery(placeholders = {}) {
23
+ return getStaticContent(placeholders, TRAFIC_ANALYSIS_PATH);
24
+ }
25
+
26
+ /**
27
+ * Scans the query template and returns a sorted array of unique placeholder (strings).
28
+ * @returns {Promise<string[]>} Array of unique placeholder keys found in the template.
29
+ */
30
+ export async function getTrafficAnalysisQueryPlaceholders() {
31
+ const raw = await getStaticContent({}, TRAFIC_ANALYSIS_PATH);
32
+ const matches = raw.match(/{{\s*([\w]+)\s*}}/g);
33
+ return [...new Set((matches).map((m) => m.replace(/{{\s*|\s*}}/g, '')))].sort();
34
+ }
@@ -0,0 +1,55 @@
1
+ WITH raw AS (
2
+ SELECT
3
+ trf_type,
4
+ path,
5
+ {{pageTypeCase}},
6
+ trf_channel,
7
+ utm_campaign,
8
+ trf_platform,
9
+ device,
10
+ pageviews,
11
+ clicked,
12
+ engaged,
13
+ lcp,
14
+ cls,
15
+ inp
16
+ FROM {{tableName}}
17
+ WHERE siteid = '{{siteId}}'
18
+ AND ({{temporalCondition}})
19
+ ),
20
+
21
+ agg AS (
22
+ SELECT
23
+ {{dimensionColumns}},
24
+ COUNT(*) AS row_count,
25
+ CAST(SUM(pageviews) AS BIGINT) AS pageviews,
26
+ CAST(SUM(clicked) AS BIGINT) AS clicks,
27
+ CAST(SUM(engaged) AS BIGINT) AS engagements,
28
+ approx_percentile(lcp, 0.70) AS p70_lcp,
29
+ approx_percentile(cls, 0.70) AS p70_cls,
30
+ approx_percentile(inp, 0.70) AS p70_inp
31
+ FROM raw
32
+ GROUP BY
33
+ {{groupBy}}
34
+ ),
35
+
36
+ grand_total AS (
37
+ SELECT CAST(SUM(pageviews) AS BIGINT) AS total_pv FROM agg
38
+ )
39
+
40
+ SELECT
41
+ {{dimensionColumnsPrefixed}},
42
+ a.pageviews,
43
+
44
+ CAST(a.pageviews AS double) / NULLIF(t.total_pv, 0) AS pct_pageviews,
45
+ CAST(a.clicks AS double) / NULLIF(a.row_count, 0) AS click_rate,
46
+ CAST(a.engagements AS double) / NULLIF(a.row_count, 0) AS engagement_rate,
47
+ 1 - CAST(a.engagements AS double) / NULLIF(a.row_count, 0) AS bounce_rate,
48
+
49
+ a.p70_lcp,
50
+ a.p70_cls,
51
+ a.p70_inp
52
+
53
+ FROM agg a
54
+ CROSS JOIN grand_total t
55
+ ORDER BY a.pageviews DESC