@pagopa/dx-savemoney 0.2.6 → 0.3.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 (54) hide show
  1. package/README.md +33 -27
  2. package/dist/azure/analyzer.d.ts +18 -5
  3. package/dist/azure/analyzer.d.ts.map +1 -1
  4. package/dist/azure/analyzer.js +295 -48
  5. package/dist/azure/analyzer.js.map +1 -1
  6. package/dist/azure/analyzers/advisor.d.ts +68 -0
  7. package/dist/azure/analyzers/advisor.d.ts.map +1 -0
  8. package/dist/azure/analyzers/advisor.js +234 -0
  9. package/dist/azure/analyzers/advisor.js.map +1 -0
  10. package/dist/azure/analyzers/index.d.ts +3 -1
  11. package/dist/azure/analyzers/index.d.ts.map +1 -1
  12. package/dist/azure/analyzers/index.js +2 -1
  13. package/dist/azure/analyzers/index.js.map +1 -1
  14. package/dist/azure/analyzers/registry.d.ts +8 -0
  15. package/dist/azure/analyzers/registry.d.ts.map +1 -1
  16. package/dist/azure/analyzers/registry.js +10 -0
  17. package/dist/azure/analyzers/registry.js.map +1 -1
  18. package/dist/azure/analyzers/subscription.d.ts +53 -0
  19. package/dist/azure/analyzers/subscription.d.ts.map +1 -0
  20. package/dist/azure/analyzers/subscription.js +18 -0
  21. package/dist/azure/analyzers/subscription.js.map +1 -0
  22. package/dist/azure/config.d.ts.map +1 -1
  23. package/dist/azure/config.js +1 -0
  24. package/dist/azure/config.js.map +1 -1
  25. package/dist/azure/index.d.ts +1 -0
  26. package/dist/azure/index.d.ts.map +1 -1
  27. package/dist/azure/index.js +1 -0
  28. package/dist/azure/index.js.map +1 -1
  29. package/dist/azure/report.d.ts.map +1 -1
  30. package/dist/azure/report.js +178 -29
  31. package/dist/azure/report.js.map +1 -1
  32. package/dist/azure/types.d.ts +28 -1
  33. package/dist/azure/types.d.ts.map +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/schema.d.ts +4 -0
  37. package/dist/schema.d.ts.map +1 -1
  38. package/dist/schema.js +9 -0
  39. package/dist/schema.js.map +1 -1
  40. package/package.json +3 -1
  41. package/src/azure/__tests__/analyzer-tags.test.ts +74 -0
  42. package/src/azure/__tests__/report.test.ts +27 -0
  43. package/src/azure/analyzer.ts +421 -65
  44. package/src/azure/analyzers/__tests__/advisor.test.ts +367 -0
  45. package/src/azure/analyzers/advisor.ts +324 -0
  46. package/src/azure/analyzers/index.ts +9 -1
  47. package/src/azure/analyzers/registry.ts +12 -0
  48. package/src/azure/analyzers/subscription.ts +56 -0
  49. package/src/azure/config.ts +1 -0
  50. package/src/azure/index.ts +1 -0
  51. package/src/azure/report.ts +206 -35
  52. package/src/azure/types.ts +29 -1
  53. package/src/index.ts +1 -1
  54. package/src/schema.ts +9 -0
@@ -1,11 +1,17 @@
1
1
  /**
2
2
  * Azure report generation
3
3
  */
4
+ import Table from "cli-table3";
5
+ // Fixed column widths — keeps the Reason column within a readable width
6
+ // while allowing cli-table3 to word-wrap long content across multiple lines.
7
+ const COL_WIDTHS = [30, 35, 25, 8, 55];
4
8
  // ANSI color codes — only applied when stdout is a TTY to avoid cluttering redirected output
5
9
  const isTTY = process.stdout.isTTY ?? false;
6
10
  const RED = isTTY ? "\x1b[31m" : "";
7
11
  const YELLOW = isTTY ? "\x1b[33m" : "";
8
12
  const BLUE = isTTY ? "\x1b[34m" : "";
13
+ const GREEN = isTTY ? "\x1b[32m" : "";
14
+ const CYAN = isTTY ? "\x1b[36m" : "";
9
15
  const BOLD = isTTY ? "\x1b[1m" : "";
10
16
  const RESET = isTTY ? "\x1b[0m" : "";
11
17
  const DIM = isTTY ? "\x1b[2m" : "";
@@ -30,66 +36,209 @@ export async function generateReport(report, format) {
30
36
  console.log(JSON.stringify(report, null, 2));
31
37
  return;
32
38
  }
33
- // For other formats, we extract the summary data.
34
- const summaryReport = report.map((r) => ({
35
- costRisk: r.analysis.costRisk,
36
- location: r.resource.location ?? "",
37
- name: r.resource.name ?? "unknown",
38
- reason: r.analysis.reason,
39
- resourceGroup: r.resource.id?.split("/")[4],
40
- subscriptionId: r.resource.id?.split("/")[2] ?? "unknown",
41
- suspectedUnused: r.analysis.suspectedUnused,
42
- type: r.resource.type ?? "unknown",
43
- }));
44
39
  if (format === "json") {
40
+ const summaryReport = report.map((r) => ({
41
+ costRisk: r.analysis.costRisk,
42
+ location: r.resource.location ?? "",
43
+ name: r.resource.name ?? "unknown",
44
+ reason: r.analysis.reason,
45
+ resourceGroup: r.resource.id?.split("/")[4],
46
+ subscriptionId: r.resource.id?.split("/")[2] ?? "unknown",
47
+ suspectedUnused: r.analysis.suspectedUnused,
48
+ type: r.resource.type ?? "unknown",
49
+ }));
45
50
  console.log(JSON.stringify(summaryReport, null, 2));
46
51
  }
47
52
  else if (format === "lint") {
48
53
  generateLintReport(report);
49
54
  }
50
55
  else {
51
- console.table(summaryReport.map((r) => ({
52
- Name: r.name,
53
- Reason: r.reason,
54
- "Resource Group": r.resourceGroup || "N/A",
55
- Risk: r.costRisk,
56
- Type: r.type,
57
- })), ["Name", "Type", "Resource Group", "Risk", "Reason"]);
56
+ const summaryReport = report.map((r) => ({
57
+ costRisk: r.analysis.costRisk,
58
+ location: r.resource.location ?? "",
59
+ name: r.resource.name ?? "unknown",
60
+ reason: r.analysis.reason,
61
+ resourceGroup: r.resource.id?.split("/")[4],
62
+ subscriptionId: r.resource.id?.split("/")[2] ?? "unknown",
63
+ suspectedUnused: r.analysis.suspectedUnused,
64
+ type: r.resource.type ?? "unknown",
65
+ }));
66
+ const table = new Table({
67
+ colWidths: [...COL_WIDTHS],
68
+ head: ["Name", "Type", "Resource Group", "Risk", "Reason"],
69
+ wordWrap: true,
70
+ });
71
+ for (const r of summaryReport) {
72
+ table.push([
73
+ r.name,
74
+ r.type,
75
+ r.resourceGroup || "N/A",
76
+ r.costRisk,
77
+ r.reason,
78
+ ]);
79
+ }
80
+ console.log(table.toString());
81
+ // Same summary block the lint format prints, so the table view also
82
+ // surfaces totals, source breakdown and estimated savings at a glance.
83
+ printSummary(computeSummary(report));
84
+ }
85
+ }
86
+ /**
87
+ * Walks the report once to compute totals, source breakdown and savings
88
+ * per currency without mutating it. Used by the table format, which has
89
+ * no per-line walk of its own.
90
+ */
91
+ function computeSummary(report) {
92
+ const summary = {
93
+ counts: { high: 0, low: 0, medium: 0 },
94
+ savingsByCurrency: new Map(),
95
+ sourceCounts: {},
96
+ };
97
+ for (const entry of report) {
98
+ const lines = entry.findings?.length
99
+ ? entry.findings.map((f) => ({
100
+ savings: f.estimatedMonthlySavings,
101
+ severity: f.severity,
102
+ source: f.source,
103
+ }))
104
+ : splitReasons(entry.analysis.reason).map(() => ({
105
+ savings: undefined,
106
+ severity: entry.analysis.costRisk,
107
+ source: "custom",
108
+ }));
109
+ for (const line of lines) {
110
+ summary.counts[line.severity]++;
111
+ summary.sourceCounts[line.source] =
112
+ (summary.sourceCounts[line.source] ?? 0) + 1;
113
+ if (line.savings) {
114
+ summary.savingsByCurrency.set(line.savings.currency, (summary.savingsByCurrency.get(line.savings.currency) ?? 0) +
115
+ line.savings.amount);
116
+ }
117
+ }
118
+ }
119
+ return summary;
120
+ }
121
+ /**
122
+ * Formats a monetary amount with an ISO 4217 currency code, falling back
123
+ * to the raw code prefix when the runtime locale data lacks the currency
124
+ * symbol.
125
+ */
126
+ function formatAmount(amount, currency) {
127
+ try {
128
+ return new Intl.NumberFormat("en-US", {
129
+ currency,
130
+ maximumFractionDigits: 2,
131
+ minimumFractionDigits: 2,
132
+ style: "currency",
133
+ }).format(amount);
134
+ }
135
+ catch {
136
+ return `${currency} ${amount.toFixed(2)}`;
58
137
  }
59
138
  }
139
+ /**
140
+ * Renders an estimated monthly savings value as the short "(€ 12.50/mo)"
141
+ * label that ships next to each lint line. Returns an empty string when
142
+ * the analyzer didn't provide an estimate.
143
+ */
144
+ function formatSavings(savings) {
145
+ if (!savings)
146
+ return "";
147
+ return `(${formatAmount(savings.amount, savings.currency)}/mo)`;
148
+ }
60
149
  /**
61
150
  * Renders a linter-style report to stdout, grouping findings by resource.
62
151
  *
152
+ * When `Finding[]` is attached to a report (Phase 1+), each finding is
153
+ * rendered with its `source` badge (e.g. `[advisor]`) and the estimated
154
+ * monthly savings, when known. For older payloads without `findings` we
155
+ * fall back to splitting `analysis.reason` like before so the format stays
156
+ * backward compatible.
157
+ *
63
158
  * Example output:
64
159
  *
65
160
  * /subscriptions/.../virtualMachines/my-vm
66
- * ✖ HIGH VM is deallocated.
67
- * ✖ HIGH No tags found.
161
+ * ✖ HIGH [advisor] Right-size your VM. (€ 12.50/mo)
162
+ * ✖ HIGH [custom] No tags found.
68
163
  *
69
164
  * Summary: 3 issues found (2 high, 0 medium, 1 low)
165
+ * Estimated monthly savings: € 12.50
70
166
  */
71
167
  function generateLintReport(report) {
72
- const counts = { high: 0, low: 0, medium: 0 };
168
+ const summary = {
169
+ counts: { high: 0, low: 0, medium: 0 },
170
+ savingsByCurrency: new Map(),
171
+ sourceCounts: {},
172
+ };
73
173
  for (const entry of report) {
74
174
  const resourceId = entry.resource.id ?? `unknown/${entry.resource.name ?? "unknown"}`;
75
- const risk = entry.analysis.costRisk;
76
- const findings = splitReasons(entry.analysis.reason);
77
175
  console.log(`${BOLD}${resourceId}${RESET}`);
78
- for (const finding of findings) {
79
- const icon = RISK_ICON[risk];
80
- const color = RISK_COLOR[risk];
81
- const label = `${color}${risk.toUpperCase().padEnd(6)}${RESET}`;
82
- console.log(` ${icon} ${label} ${DIM}${finding}${RESET}`);
83
- counts[risk]++;
176
+ const lines = entry.findings?.length
177
+ ? entry.findings.map((f) => ({
178
+ extra: formatSavings(f.estimatedMonthlySavings),
179
+ severity: f.severity,
180
+ source: f.source,
181
+ text: f.reason,
182
+ }))
183
+ : splitReasons(entry.analysis.reason).map((text) => ({
184
+ extra: "",
185
+ severity: entry.analysis.costRisk,
186
+ source: "custom",
187
+ text,
188
+ }));
189
+ for (const line of lines) {
190
+ const icon = RISK_ICON[line.severity];
191
+ const color = RISK_COLOR[line.severity];
192
+ const label = `${color}${line.severity.toUpperCase().padEnd(6)}${RESET}`;
193
+ const sourceBadge = `${CYAN}[${line.source}]${RESET}`;
194
+ const extra = line.extra ? ` ${GREEN}${line.extra}${RESET}` : "";
195
+ console.log(` ${icon} ${label} ${sourceBadge} ${DIM}${line.text}${RESET}${extra}`);
196
+ summary.counts[line.severity]++;
197
+ summary.sourceCounts[line.source] =
198
+ (summary.sourceCounts[line.source] ?? 0) + 1;
199
+ }
200
+ if (entry.findings) {
201
+ for (const f of entry.findings) {
202
+ if (f.estimatedMonthlySavings) {
203
+ const { amount, currency } = f.estimatedMonthlySavings;
204
+ summary.savingsByCurrency.set(currency, (summary.savingsByCurrency.get(currency) ?? 0) + amount);
205
+ }
206
+ }
84
207
  }
85
208
  console.log();
86
209
  }
210
+ printSummary(summary);
211
+ }
212
+ /**
213
+ * Prints the shared trailer (issues, sources, estimated savings) used by
214
+ * both the lint and the table format.
215
+ *
216
+ * Savings are kept grouped by currency intentionally: Azure Advisor
217
+ * returns each recommendation in the subscription's native billing
218
+ * currency, so the same report can carry EUR and USD figures at the
219
+ * same time when subscriptions are billed in different regions. We do
220
+ * NOT convert across currencies — the rates would be both volatile and
221
+ * out of scope for this tool.
222
+ */
223
+ function printSummary(summary) {
224
+ const { counts, savingsByCurrency, sourceCounts } = summary;
87
225
  const total = counts.high + counts.medium + counts.low;
88
226
  const summaryLine = `${BOLD}Summary:${RESET} ${total} issue${total !== 1 ? "s" : ""} found` +
89
227
  ` ${RED}(${counts.high} high${RESET}` +
90
228
  `, ${YELLOW}${counts.medium} medium${RESET}` +
91
229
  `, ${BLUE}${counts.low} low${RESET})`;
92
230
  console.log(summaryLine);
231
+ const sourceBreakdown = Object.entries(sourceCounts)
232
+ .sort(([a], [b]) => a.localeCompare(b))
233
+ .map(([source, n]) => `${n} ${source}`)
234
+ .join(", ");
235
+ if (sourceBreakdown) {
236
+ console.log(`${BOLD}Sources:${RESET} ${sourceBreakdown}`);
237
+ }
238
+ if (savingsByCurrency.size > 0) {
239
+ const parts = [...savingsByCurrency.entries()].map(([currency, amount]) => `${GREEN}${formatAmount(amount, currency)}${RESET}`);
240
+ console.log(`${BOLD}Estimated monthly savings:${RESET} ${parts.join(", ")}`);
241
+ }
93
242
  }
94
243
  /**
95
244
  * Splits a concatenated reason string (sentences separated by ". ") into
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/azure/report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/azure/report.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  * Azure-specific types
3
3
  */
4
4
  import type * as armResources from "@azure/arm-resources";
5
+ import type { Finding } from "../finding.js";
5
6
  import type { AnalysisResult, BaseConfig, CostRisk, Thresholds } from "../types.js";
6
7
  /**
7
8
  * Azure configuration extending base config
@@ -18,6 +19,14 @@ export type AzureConfig = BaseConfig & {
18
19
  * If omitted, all resources are analyzed.
19
20
  */
20
21
  filterTags?: Map<string, string>;
22
+ /**
23
+ * Which finding sources to include in the run.
24
+ * - `"custom"` → enables the per-resource analyzer plugins
25
+ * - `"advisor"` → enables the Azure Advisor subscription-level analyzer
26
+ *
27
+ * Defaults to `["advisor", "custom"]` when omitted (i.e. all sources).
28
+ */
29
+ sources?: AzureSource[];
21
30
  subscriptionIds: string[];
22
31
  /**
23
32
  * Analysis thresholds. Defaults from DEFAULT_THRESHOLDS are used when not provided.
@@ -26,10 +35,22 @@ export type AzureConfig = BaseConfig & {
26
35
  verbose?: boolean;
27
36
  };
28
37
  /**
29
- * Detailed report for a single Azure resource with full resource object
38
+ * Detailed report for a single Azure resource with full resource object.
39
+ *
40
+ * Phase 1 introduces the optional `findings` field carrying the unified
41
+ * `Finding[]` model alongside the legacy `analysis` summary. The two are
42
+ * kept in sync by the orchestrator so existing report formats keep
43
+ * working untouched while new consumers (GUI, JSON exports, Phase 2
44
+ * pricing aggregation) can read structured findings directly.
30
45
  */
31
46
  export type AzureDetailedResourceReport = {
32
47
  analysis: AnalysisResult;
48
+ /**
49
+ * Structured findings attached to the resource. Always populated by the
50
+ * orchestrator (possibly empty). Optional only for backward compatibility
51
+ * with serialised payloads produced before Phase 1.
52
+ */
53
+ findings?: Finding[];
33
54
  resource: armResources.GenericResource;
34
55
  };
35
56
  /**
@@ -45,4 +66,10 @@ export type AzureResourceReport = {
45
66
  suspectedUnused: boolean;
46
67
  type: string;
47
68
  };
69
+ /**
70
+ * Finding sources that are valid for Azure analysis.
71
+ * Narrowed from `FindingSource` to exclude "aws", which is not a valid
72
+ * filter for Azure runs and would silently produce an empty report.
73
+ */
74
+ export type AzureSource = "advisor" | "custom";
48
75
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * This tool does NOT modify, tag, or delete any resources.
14
14
  */
15
15
  export { type Analyzer, type AnalyzerContext, type AzureClients, createDefaultAnalyzers, } from "./azure/analyzers/index.js";
16
- export type { AzureConfig } from "./azure/types.js";
16
+ export type { AzureConfig, AzureSource } from "./azure/types.js";
17
17
  import * as azureModule from "./azure/index.js";
18
18
  export declare const azure: typeof azureModule;
19
19
  export { type MetricsCache, type MonitorClientLike } from "./azure/utils.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,eAAO,MAAM,KAAK,oBAAc,CAAC;AAEjC,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG7E,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAE3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,eAAO,MAAM,KAAK,oBAAc,CAAC;AAEjC,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG7E,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,cAAc,YAAY,CAAC;AAE3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE1E"}
package/dist/schema.d.ts CHANGED
@@ -88,6 +88,10 @@ export declare const ConfigSchema: z.ZodObject<{
88
88
  azure: z.ZodObject<{
89
89
  concurrency: z.ZodOptional<z.ZodNumber>;
90
90
  preferredLocation: z.ZodDefault<z.ZodString>;
91
+ sources: z.ZodDefault<z.ZodArray<z.ZodEnum<{
92
+ custom: "custom";
93
+ advisor: "advisor";
94
+ }>>>;
91
95
  subscriptionIds: z.ZodArray<z.ZodString>;
92
96
  thresholds: z.ZodPipe<z.ZodOptional<z.ZodObject<{
93
97
  appService: z.ZodPipe<z.ZodOptional<z.ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqBlB,CAAC;AAyBZ;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAmD,CAAC;AAI7E,2DAA2D;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqBlB,CAAC;AAkCZ;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAmD,CAAC;AAI7E,2DAA2D;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
package/dist/schema.js CHANGED
@@ -87,6 +87,15 @@ const AzureSectionSchema = z
87
87
  */
88
88
  concurrency: z.number().int().positive().optional(),
89
89
  preferredLocation: z.string().default("italynorth"),
90
+ /**
91
+ * Which finding sources to include. Defaults to all known sources.
92
+ * Authors can narrow the run to e.g. `["advisor"]` to fetch only
93
+ * Azure Advisor recommendations, or `["custom"]` to skip Advisor.
94
+ */
95
+ sources: z
96
+ .array(z.enum(["advisor", "custom"]))
97
+ .nonempty()
98
+ .default(["advisor", "custom"]),
90
99
  subscriptionIds: z
91
100
  .array(z.string())
92
101
  .min(1, "Config file must contain at least one entry in 'azure.subscriptionIds'"),
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,+EAA+E;AAE/E,MAAM,kBAAkB,GAAG,CAAC;KACzB,MAAM,CAAC;IACN,wEAAwE;IACxE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACjC,iGAAiG;IACjG,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC;CAC1D,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,0BAA0B,GAAG,CAAC;KACjC,MAAM,CAAC;IACN,6EAA6E;IAC7E,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACjC,iFAAiF;IACjF,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,qGAAqG;IACrG,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1C,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,4BAA4B,GAAG,CAAC;KACnC,MAAM,CAAC;IACN,2GAA2G;IAC3G,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IAC3C,wFAAwF;IACxF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;IAC3C,+GAA+G;IAC/G,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;CACzC,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,uBAAuB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,sFAAsF;IACtF,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3C,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,wBAAwB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,oGAAoG;IACpG,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;CACzC,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,0BAA0B,GAAG,CAAC;KACjC,MAAM,CAAC;IACN,uFAAuF;IACvF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACxC,+EAA+E;IAC/E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;CAClC,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,gFAAgF;AAEhF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,UAAU,EAAE,0BAA0B,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAChE,0BAA0B,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAC1C;IACD,YAAY,EAAE,4BAA4B,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CACpE,4BAA4B,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAC5C;IACD,QAAQ,EAAE,wBAAwB,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5D,wBAAwB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CACxC;IACD,UAAU,EAAE,0BAA0B,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAChE,0BAA0B,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAC1C;IACD,OAAO,EAAE,uBAAuB,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1D,uBAAuB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CACvC;IACD,EAAE,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAChD,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAClC;CACF,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,gFAAgF;AAEhF,MAAM,kBAAkB,GAAG,CAAC;KACzB,MAAM,CAAC;IACN;;;OAGG;IACH,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnD,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;IACnD;;;;OAIG;IACH,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;SACpC,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjC,eAAe,EAAE,CAAC;SACf,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CACF,CAAC,EACD,wEAAwE,CACzE;IACH,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CACtD,gBAAgB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAChC;IACD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CACtD,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-savemoney",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Azure resource analyzer for finding unused or cost-inefficient resources.",
6
6
  "repository": {
@@ -27,6 +27,7 @@
27
27
  "DX"
28
28
  ],
29
29
  "dependencies": {
30
+ "@azure/arm-advisor": "^3.2.0",
30
31
  "@azure/arm-appcontainers": "^3.0.0",
31
32
  "@azure/arm-appservice": "^17.0.0",
32
33
  "@azure/arm-compute": "^23.3.0",
@@ -35,6 +36,7 @@
35
36
  "@azure/arm-resources": "^7.0.0",
36
37
  "@azure/identity": "^4.13.1",
37
38
  "@logtape/logtape": "^1.3.8",
39
+ "cli-table3": "^0.6.5",
38
40
  "js-yaml": "^4.1.1",
39
41
  "p-limit": "^7.3.0",
40
42
  "zod": "^4.4.2"
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Unit tests for tag-filter behavior on Advisor findings.
3
+ *
4
+ * These tests exercise the pure helper used by the Azure analyzer
5
+ * orchestrator to keep filtering semantics explicit and stable.
6
+ */
7
+
8
+ import { describe, expect, it } from "vitest";
9
+
10
+ import type { Finding } from "../../finding.js";
11
+
12
+ import { shouldIncludeAdvisorFindingForTags } from "../analyzer.js";
13
+
14
+ function mkFinding(resourceId: string, source: Finding["source"]): Finding {
15
+ return {
16
+ category: "cost",
17
+ code: `${source}.test`,
18
+ reason: "Test finding.",
19
+ resourceId,
20
+ severity: "low",
21
+ source,
22
+ };
23
+ }
24
+
25
+ describe("shouldIncludeAdvisorFindingForTags", () => {
26
+ it("includes all findings when no tag filter is active", () => {
27
+ const finding = mkFinding(
28
+ "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1",
29
+ "advisor",
30
+ );
31
+
32
+ expect(
33
+ shouldIncludeAdvisorFindingForTags(finding, new Set<string>(), false),
34
+ ).toBe(true);
35
+ });
36
+
37
+ it("keeps subscription-level Advisor findings global even with tag filters", () => {
38
+ const finding = mkFinding("/subscriptions/sub1", "advisor");
39
+
40
+ expect(
41
+ shouldIncludeAdvisorFindingForTags(finding, new Set<string>(), true),
42
+ ).toBe(true);
43
+ });
44
+
45
+ it("includes resource-level Advisor findings only when resource id is tag-matched", () => {
46
+ const finding = mkFinding(
47
+ "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1",
48
+ "advisor",
49
+ );
50
+
51
+ const taggedResourceIds = new Set<string>([
52
+ "/subscriptions/sub1/resourcegroups/rg1/providers/microsoft.compute/virtualmachines/vm1",
53
+ ]);
54
+
55
+ expect(
56
+ shouldIncludeAdvisorFindingForTags(finding, taggedResourceIds, true),
57
+ ).toBe(true);
58
+ });
59
+
60
+ it("excludes resource-level Advisor findings when resource id is not tag-matched", () => {
61
+ const finding = mkFinding(
62
+ "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm2",
63
+ "advisor",
64
+ );
65
+
66
+ const taggedResourceIds = new Set<string>([
67
+ "/subscriptions/sub1/resourcegroups/rg1/providers/microsoft.compute/virtualmachines/vm1",
68
+ ]);
69
+
70
+ expect(
71
+ shouldIncludeAdvisorFindingForTags(finding, taggedResourceIds, true),
72
+ ).toBe(false);
73
+ });
74
+ });
@@ -136,3 +136,30 @@ describe("generateReport — lint format", () => {
136
136
  expect(calls[0]).toContain("0 issues found");
137
137
  });
138
138
  });
139
+
140
+ describe("generateReport — table format", () => {
141
+ let logSpy: ReturnType<typeof vi.spyOn>;
142
+
143
+ beforeEach(() => {
144
+ logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
145
+ });
146
+
147
+ afterEach(() => {
148
+ vi.restoreAllMocks();
149
+ });
150
+
151
+ it("renders without throwing for an empty report", async () => {
152
+ await expect(generateReport([], "table")).resolves.toBeUndefined();
153
+ // Table is always printed; the summary line ("0 issues found") follows.
154
+ expect(logSpy.mock.calls.length).toBeGreaterThanOrEqual(1);
155
+ const output = logSpy.mock.calls.map((c) => String(c[0])).join("\n");
156
+ expect(output).toContain("0 issues found");
157
+ });
158
+
159
+ it("includes resource name and reason in the rendered table", async () => {
160
+ await generateReport([HIGH_ENTRY], "table");
161
+ const output = logSpy.mock.calls.map((c) => String(c[0])).join("\n");
162
+ expect(output).toContain("vm-high");
163
+ expect(output).toContain("VM is deallocated");
164
+ });
165
+ });