@beesolve/analytics-processor 0.4.0 → 0.5.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.
Files changed (75) hide show
  1. package/DOCS.md +23 -3
  2. package/dist/cdk.js +46 -0
  3. package/dist/dailyRollup/dailyRollup.mjs.map +2 -2
  4. package/dist/hourly/hourly.mjs +3 -3
  5. package/dist/hourly/hourly.mjs.map +3 -3
  6. package/dist/index.d.ts +233 -3
  7. package/dist/index.js +509 -40
  8. package/dist/monthlyRollup/monthlyRollup.mjs.map +2 -2
  9. package/dist/sitemap/bowser-UJEXXM6S.mjs +2 -0
  10. package/dist/sitemap/bowser-UJEXXM6S.mjs.map +7 -0
  11. package/dist/sitemap/chunk-3UGRDNDZ.mjs +6 -0
  12. package/dist/sitemap/chunk-3UGRDNDZ.mjs.map +7 -0
  13. package/dist/sitemap/chunk-BH7ZEBAB.mjs +2 -0
  14. package/dist/sitemap/chunk-BH7ZEBAB.mjs.map +7 -0
  15. package/dist/sitemap/chunk-CFZ5JWD3.mjs +2 -0
  16. package/dist/sitemap/chunk-CFZ5JWD3.mjs.map +7 -0
  17. package/dist/sitemap/chunk-KRS5YV2Z.mjs +2 -0
  18. package/dist/sitemap/chunk-KRS5YV2Z.mjs.map +7 -0
  19. package/dist/sitemap/chunk-LB33VBGK.mjs +2 -0
  20. package/dist/sitemap/chunk-LB33VBGK.mjs.map +7 -0
  21. package/dist/sitemap/chunk-OC3UN3OS.mjs +2 -0
  22. package/dist/sitemap/chunk-OC3UN3OS.mjs.map +7 -0
  23. package/dist/sitemap/chunk-PD2XGJQD.mjs +10 -0
  24. package/dist/sitemap/chunk-PD2XGJQD.mjs.map +7 -0
  25. package/dist/sitemap/chunk-QPD5NJHJ.mjs +2 -0
  26. package/dist/sitemap/chunk-QPD5NJHJ.mjs.map +7 -0
  27. package/dist/sitemap/chunk-REMPUQ6B.mjs +19 -0
  28. package/dist/sitemap/chunk-REMPUQ6B.mjs.map +7 -0
  29. package/dist/sitemap/chunk-SNCZ4GCT.mjs +2 -0
  30. package/dist/sitemap/chunk-SNCZ4GCT.mjs.map +7 -0
  31. package/dist/sitemap/chunk-TZ6TJSKE.mjs +10 -0
  32. package/dist/sitemap/chunk-TZ6TJSKE.mjs.map +7 -0
  33. package/dist/sitemap/chunk-YMGL4RSC.mjs +2 -0
  34. package/dist/sitemap/chunk-YMGL4RSC.mjs.map +7 -0
  35. package/dist/sitemap/chunk-YY2HYMK3.mjs +4 -0
  36. package/dist/sitemap/chunk-YY2HYMK3.mjs.map +7 -0
  37. package/dist/sitemap/client-WGCJRHWA.mjs +2 -0
  38. package/dist/sitemap/client-WGCJRHWA.mjs.map +7 -0
  39. package/dist/sitemap/dist-es-3FAKVHIH.mjs +2 -0
  40. package/dist/sitemap/dist-es-3FAKVHIH.mjs.map +7 -0
  41. package/dist/sitemap/dist-es-A36NQ5CO.mjs +2 -0
  42. package/dist/sitemap/dist-es-A36NQ5CO.mjs.map +7 -0
  43. package/dist/sitemap/dist-es-F7GXZJIC.mjs +3 -0
  44. package/dist/sitemap/dist-es-F7GXZJIC.mjs.map +7 -0
  45. package/dist/sitemap/dist-es-GRXF7RTK.mjs +2 -0
  46. package/dist/sitemap/dist-es-GRXF7RTK.mjs.map +7 -0
  47. package/dist/sitemap/dist-es-OKUHAI6O.mjs +2 -0
  48. package/dist/sitemap/dist-es-OKUHAI6O.mjs.map +7 -0
  49. package/dist/sitemap/dist-es-P7BXOYHO.mjs +2 -0
  50. package/dist/sitemap/dist-es-P7BXOYHO.mjs.map +7 -0
  51. package/dist/sitemap/dist-es-SQ53M5JX.mjs +7 -0
  52. package/dist/sitemap/dist-es-SQ53M5JX.mjs.map +7 -0
  53. package/dist/sitemap/dist-es-TCGA3UGX.mjs +2 -0
  54. package/dist/sitemap/dist-es-TCGA3UGX.mjs.map +7 -0
  55. package/dist/sitemap/dist-es-UXKF5GQY.mjs +3 -0
  56. package/dist/sitemap/dist-es-UXKF5GQY.mjs.map +7 -0
  57. package/dist/sitemap/dist-es-YJ4PQZC7.mjs +2 -0
  58. package/dist/sitemap/dist-es-YJ4PQZC7.mjs.map +7 -0
  59. package/dist/sitemap/event-streams-6V3Q6GHC.mjs +2 -0
  60. package/dist/sitemap/event-streams-6V3Q6GHC.mjs.map +7 -0
  61. package/dist/sitemap/loadCognitoIdentity-RIENBHOX.mjs +2 -0
  62. package/dist/sitemap/loadCognitoIdentity-RIENBHOX.mjs.map +7 -0
  63. package/dist/sitemap/loadSso-DTQ4R4CV.mjs +2 -0
  64. package/dist/sitemap/loadSso-DTQ4R4CV.mjs.map +7 -0
  65. package/dist/sitemap/loadSts-GZDTRQPT.mjs +2 -0
  66. package/dist/sitemap/loadSts-GZDTRQPT.mjs.map +7 -0
  67. package/dist/sitemap/signin-6D536OAO.mjs +2 -0
  68. package/dist/sitemap/signin-6D536OAO.mjs.map +7 -0
  69. package/dist/sitemap/sitemap.mjs +3 -0
  70. package/dist/sitemap/sitemap.mjs.map +7 -0
  71. package/dist/sitemap/sso-oidc-T5CK3NX3.mjs +2 -0
  72. package/dist/sitemap/sso-oidc-T5CK3NX3.mjs.map +7 -0
  73. package/dist/sitemap/sts-62ECLMPL.mjs +2 -0
  74. package/dist/sitemap/sts-62ECLMPL.mjs.map +7 -0
  75. package/package.json +2 -1
package/DOCS.md CHANGED
@@ -1,18 +1,38 @@
1
1
  # @beesolve/analytics-processor - Documentation
2
2
 
3
- **Keywords:** CloudFront analytics, Athena, DynamoDB totals, breakdowns, rollups, AnalyticsProcessor construct, grantRead, processHour, hourKey, windowKeys, Grain, Dimension, backfill
3
+ **Keywords:** CloudFront analytics, Athena, DynamoDB totals, breakdowns, rollups, AnalyticsProcessor construct, grantRead, processHour, hourKey, windowKeys, Grain, Dimension, backfill, sitemap URL stats, SitemapUrlStats, getForUrl, getForMonth, canonicalPath
4
4
 
5
5
  > This documentation is published inside the installed package and matches the installed version. Prefer it over prior knowledge or older examples found online.
6
6
 
7
7
  > Full source and ADRs: https://github.com/BeeSolve/packages/tree/main/packages/analytics-processor
8
8
 
9
- This package does not ship how-to guides. See the [README](./README.md) for the API
10
- reference and usage, and the GitHub repository for source and examples.
9
+ See the [README](./README.md) for the API reference and usage, the how-to guides below for
10
+ task-focused walkthroughs, and the GitHub repository for source and examples.
11
+
12
+ ## Sitemap URL stats
13
+
14
+ The hourly breakdown keeps only a top-100-by-hits `path` list per hour; everything else
15
+ collapses into `__other__`, so genuine articles are lost under bot traffic. Sitemap URL stats
16
+ are a separate, parallel model that accumulates per-day hit counts for a known set of URLs
17
+ (the site's sitemap) and stores one item per `(path, year-month)`.
18
+
19
+ - **Config**: set `sitemapUrl` on the shared config item (`Config.setSitemapUrl`); the hourly
20
+ sitemap processor fetches and resolves it to a path set.
21
+ - **Processor**: an hourly Lambda scans one lagged hour and `ADD`s each sitemap path's hits
22
+ into that day's `dNN` + the `hits` month rollup, guarded by a per-hour applied-marker so the
23
+ hour counts exactly once.
24
+ - **Read model**: `SitemapUrlStats` with `getForUrl` (one URL across months) and
25
+ `getForMonth` (all URLs in a month, for month-over-month comparison).
26
+
27
+ See [the how-to guide](./docs/how-to/sitemap-url-stats.md) and
28
+ [ADR-007](./docs/adr-007-sitemap-url-stats.md) for details.
11
29
 
12
30
  ## How-To Guides
13
31
 
14
32
  - [Reduce Athena cost](./docs/how-to/reduce-athena-cost.md) - deliver only the 10 fields
15
33
  the processor needs via the exported `requiredRecordFields`
34
+ - [Track per-article hits with sitemap URL stats](./docs/how-to/sitemap-url-stats.md) - the
35
+ `sitemapUrl` config, the hourly sitemap processor, and the `SitemapUrlStats` read model
16
36
 
17
37
  ## Further Reading
18
38
 
package/dist/cdk.js CHANGED
@@ -85,6 +85,48 @@ class AnalyticsProcessor extends Construct {
85
85
  resources: [props.athenaResultsBucketArn, `${props.athenaResultsBucketArn}/*`]
86
86
  }));
87
87
  }
88
+ const sitemap = new Nodejs24Function(this, "Sitemap", {
89
+ description: "CloudFront analytics sitemap processor — fetches sitemap, recomputes per-URL monthly hits",
90
+ entry: `${builtDir}sitemap/`,
91
+ handler: "sitemap.handler",
92
+ memorySize,
93
+ timeout,
94
+ environment: {
95
+ ...sharedEnvironment,
96
+ ATHENA_DATABASE: props.athenaDatabase,
97
+ ATHENA_V2_TABLE: props.athenaV2Table,
98
+ ATHENA_WORKGROUP: props.athenaWorkgroup,
99
+ ...props.athenaOutputLocation != null ? { ATHENA_OUTPUT_LOCATION: props.athenaOutputLocation } : {}
100
+ }
101
+ });
102
+ this.table.grantReadWriteData(sitemap);
103
+ sitemap.addToRolePolicy(new PolicyStatement({
104
+ effect: Effect.ALLOW,
105
+ actions: [
106
+ "athena:StartQueryExecution",
107
+ "athena:GetQueryExecution",
108
+ "athena:GetQueryResults",
109
+ "athena:StopQueryExecution"
110
+ ],
111
+ resources: ["*"]
112
+ }));
113
+ sitemap.addToRolePolicy(new PolicyStatement({
114
+ effect: Effect.ALLOW,
115
+ actions: ["glue:GetTable", "glue:GetDatabase", "glue:GetPartitions"],
116
+ resources: ["*"]
117
+ }));
118
+ sitemap.addToRolePolicy(new PolicyStatement({
119
+ effect: Effect.ALLOW,
120
+ actions: ["s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation"],
121
+ resources: [props.logBucketArn, `${props.logBucketArn}/*`]
122
+ }));
123
+ if (props.athenaResultsBucketArn != null) {
124
+ sitemap.addToRolePolicy(new PolicyStatement({
125
+ effect: Effect.ALLOW,
126
+ actions: ["s3:PutObject", "s3:GetObject", "s3:GetBucketLocation", "s3:ListBucket"],
127
+ resources: [props.athenaResultsBucketArn, `${props.athenaResultsBucketArn}/*`]
128
+ }));
129
+ }
88
130
  const dailyRollup = new Nodejs24Function(this, "DailyRollup", {
89
131
  description: "CloudFront analytics daily rollup — sums hourly DynamoDB items",
90
132
  entry: `${builtDir}dailyRollup/`,
@@ -107,6 +149,10 @@ class AnalyticsProcessor extends Construct {
107
149
  schedule: Schedule.rate(Duration.hours(1)),
108
150
  targets: [new LambdaFunctionTarget(hourly)]
109
151
  });
152
+ new Rule(this, "SitemapSchedule", {
153
+ schedule: Schedule.rate(Duration.hours(1)),
154
+ targets: [new LambdaFunctionTarget(sitemap)]
155
+ });
110
156
  new Rule(this, "DailyRollupSchedule", {
111
157
  schedule: Schedule.cron({ minute: "15", hour: "0" }),
112
158
  targets: [new LambdaFunctionTarget(dailyRollup)]