Skip to content

Commit 84f4601

Browse files
📝 Add docstrings to DOC-950
Docstrings generation was requested by @JakeSCahill. * #133 (comment) The following files were modified: * `tools/cloud-regions/generate-cloud-regions.js` * `tools/cloud-tier-table/generate-cloud-tier-table.js` * `tools/cloud-tier-table/generate-discrepancy-report.js`
1 parent 874e3fa commit 84f4601

File tree

3 files changed

+148
-46
lines changed

3 files changed

+148
-46
lines changed

tools/cloud-regions/generate-cloud-regions.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,10 @@ function processCloudRegions(yamlText) {
193193
}
194194

195195
/**
196-
* Processes cloud regions data and organizes it by cluster type (BYOC/Dedicated) for tabs output.
197-
*
198-
* @param {string} yamlText - The YAML content to parse and process.
199-
* @return {Object} An object with clusterTypes array, each containing providers organized by cluster type.
200-
* @throws {Error} If the YAML is malformed or missing the required `regions` array.
196+
* Parse cloud-regions YAML and group available regions by cluster type (BYOC or Dedicated) and provider for tabbed output.
197+
* @param {string} yamlText - YAML document containing `regions` and optional `products`; regions must include `cloudProvider`, `name`, and `redpandaProductAvailability` entries.
198+
* @returns {{clusterTypes: Array<{name: string, providers: Array<{name: string, regions: Array<{name: string}>}>}>}} An object with a `clusterTypes` array; each entry lists a cluster type (`BYOC` or `Dedicated`) and its providers (only providers with at least one region), each containing sorted region names.
199+
* @throws {Error} If the YAML is malformed or does not contain a top-level `regions` array.
201200
*/
202201
function processCloudRegionsForTabs(yamlText) {
203202
let data;
@@ -297,10 +296,10 @@ function processCloudRegionsForTabs(yamlText) {
297296
}
298297

299298
/**
300-
* Fetches, processes, and renders cloud region and tier data from a GitHub YAML file.
299+
* Generate rendered cloud region and tier output from a GitHub-hosted YAML file.
301300
*
302-
* Retrieves YAML data from GitHub using the GitHub API (to avoid caching issues),
303-
* parses and filters it to include only public cloud regions and tiers, and renders the result in the requested format.
301+
* Fetches the YAML from the specified repository path, parses and filters it to include only public providers/regions/tiers,
302+
* and renders the result in the requested format. When `options.tabs` is true, returns separate rendered outputs per cluster type.
304303
*
305304
* @param {Object} options - Options for generating cloud regions.
306305
* @param {string} options.owner - GitHub repository owner.
@@ -309,10 +308,10 @@ function processCloudRegionsForTabs(yamlText) {
309308
* @param {string} [options.ref='main'] - Git reference (branch, tag, or commit SHA).
310309
* @param {string} [options.format='md'] - The output format (e.g., 'md' for Markdown).
311310
* @param {string} [options.token] - Optional GitHub token for authentication.
312-
* @param {string} [options.template] - Optional path to custom Handlebars template.
313-
* @param {boolean} [options.tabs=false] - Whether to generate AsciiDoc with tabs organized by cluster type.
314-
* @returns {string} The rendered cloud regions output.
315-
* @throws {Error} If fetching, processing, or rendering fails, or if no valid providers or regions are found.
311+
* @param {string} [options.template] - Optional path to a custom Handlebars template.
312+
* @param {boolean} [options.tabs=false] - When true, produce separate rendered outputs organized by cluster type (keys are cluster type names lowercased).
313+
* @returns {string|Object} Rendered output as a string, or when `options.tabs` is true an object mapping lowercase cluster type names to rendered strings.
314+
* @throws {Error} If fetching, parsing, processing, or rendering fails, or if no valid providers/regions remain after filtering.
316315
*/
317316
async function generateCloudRegions({ owner, repo, path, ref = 'main', format = 'md', token, template, tabs = false }) {
318317
let yamlText;
@@ -368,4 +367,4 @@ async function generateCloudRegions({ owner, repo, path, ref = 'main', format =
368367
module.exports = {
369368
generateCloudRegions,
370369
processCloudRegions,
371-
};
370+
};

tools/cloud-tier-table/generate-cloud-tier-table.js

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ const LIMIT_KEYS = [
3636
'kafka_topics_max',
3737
];
3838

39-
// Map header keys to human readable labels
39+
/**
40+
* Map a header key to a human-readable label.
41+
* @param {string} key - The header key to convert.
42+
* @returns {string} The human-readable label for `key`, or `key` unchanged if no mapping exists.
43+
*/
4044
function humanLabel(key) {
4145
if (key === 'cloud_provider') return 'Cloud Provider';
4246
if (key === 'machine_type') return 'Machine Type';
@@ -48,14 +52,24 @@ function humanLabel(key) {
4852
return key;
4953
}
5054

51-
// Map provider values to human readable
55+
/**
56+
* Convert a provider identifier into a human-friendly provider name.
57+
* @param {*} val - Provider identifier (e.g., 'aws', 'gcp', 'azure'); comparison is case-insensitive. Falsy values produce an empty string.
58+
* @returns {string} `'AWS'`, `'GCP'`, or `'Azure'` for known providers; empty string for falsy input; otherwise returns the original value.
59+
*/
5260
function humanProvider(val) {
5361
if (!val) return '';
5462
const map = { aws: 'AWS', gcp: 'GCP', azure: 'Azure' };
5563
return map[String(val).toLowerCase()] || val;
5664
}
5765

58-
// Fetch public tiers from master-data.yaml
66+
/**
67+
* Loads master-data.yaml (from an HTTP URL or local path), validates its products list, and returns a normalized list of public tiers.
68+
*
69+
* @param {string} masterDataUrl - HTTP URL (GitHub API file response expected) or local filesystem path to the master-data.yaml file.
70+
* @returns {Array<Object>} An array of public tier objects with the following fields: `displayName`, `configProfileName`, `cloudProvider`, `advertisedMaxIngress`, `advertisedMaxEgress`, `advertisedMaxPartitionCount`, and `advertisedMaxClientCount`.
71+
* @throws {Error} If masterDataUrl is missing or not a string, fetching or file reading fails, YAML parsing fails, the products array is missing/invalid, or no valid public tiers are found.
72+
*/
5973
async function fetchPublicTiers(masterDataUrl) {
6074
try {
6175
if (!masterDataUrl || typeof masterDataUrl !== 'string') {
@@ -161,6 +175,15 @@ async function fetchPublicTiers(masterDataUrl) {
161175
}
162176
}
163177

178+
/**
179+
* Load and parse YAML from a local file path, HTTP(S) URL, or the special GitHub install-pack API directory.
180+
*
181+
* When given the GitHub install-pack API directory URL, selects the latest versioned YAML file (names like `1.2.yml` or `1.2.3.yaml`) and parses it. For HTTP(S) inputs, fetches the URL and parses the response body. For local file paths, reads and parses the file contents. If GITHUB_TOKEN is set, requests include Authorization and User-Agent headers.
182+
*
183+
* @param {string} input - Local filesystem path, HTTP(S) URL, or the GitHub API directory URL 'https://api.github.com/repos/redpanda-data/cloudv2/contents/install-pack'.
184+
* @returns {Object} The parsed YAML content as a JavaScript object.
185+
* @throws {Error} If `input` is not a valid string, the network or filesystem access fails, no suitable versioned YAML is found in the install-pack directory, or the YAML content cannot be parsed into an object.
186+
*/
164187
async function parseYaml(input) {
165188
try {
166189
if (!input || typeof input !== 'string') {
@@ -333,10 +356,11 @@ function extractVersion(profileName) {
333356
}
334357

335358
/**
336-
* Find the highest version config profile for a given base name
337-
* @param {Object} configProfiles - All config profiles
338-
* @param {string} targetProfile - The target profile name from master data
339-
* @returns {string} The highest version profile name
359+
* Selects the highest-versioned config profile name matching the given target profile.
360+
*
361+
* @param {Object} configProfiles - Map of profile names to profile definitions.
362+
* @param {string} targetProfile - The target profile name to match (from master data).
363+
* @returns {string} The matching profile name with the largest numeric `-vN` suffix, or `targetProfile` if no versioned variants are found or on failure.
340364
*/
341365
function findHighestVersionProfile(configProfiles, targetProfile) {
342366
try {
@@ -384,6 +408,18 @@ function findHighestVersionProfile(configProfiles, targetProfile) {
384408
}
385409
}
386410

411+
/**
412+
* Builds table row objects by merging public tier metadata with matching config profiles.
413+
*
414+
* Each returned row maps the tier display name to the requested limit keys (either `customLimits` or the module's default keys).
415+
* Missing values are represented as the string "N/A". Duplicate rows with the same tier name and resolved config profile are removed.
416+
*
417+
* @param {Object} tiers - Parsed tiers YAML object; must contain a `config_profiles` object mapping profile names to definitions.
418+
* @param {Array<Object>} publicTiers - Array of public tier descriptors; each entry must include `displayName` and `configProfileName`.
419+
* @param {Array<string>} [customLimits] - Optional list of limit keys to extract; when omitted the module's default LIMIT_KEYS are used.
420+
* @returns {Array<Object>} An array of row objects. Each row has a `tier` property (display name) and entries for each requested limit key.
421+
* @throws {Error} If inputs are invalid or row construction fails (e.g., missing `config_profiles`, non-array `publicTiers`, or other fatal processing errors).
422+
*/
387423
function buildTableRows(tiers, publicTiers, customLimits) {
388424
try {
389425
// Use custom limits if provided, otherwise use default LIMIT_KEYS
@@ -519,6 +555,13 @@ function buildTableRows(tiers, publicTiers, customLimits) {
519555
}
520556
}
521557

558+
/**
559+
* Render an array of tier rows as a Markdown table.
560+
*
561+
* @param {Array<Object>} rows - Array of row objects where each row has a `tier` property and keys matching entries in `limitKeys`.
562+
* @param {Array<string>} [limitKeys=LIMIT_KEYS] - Ordered list of keys to include as columns after the "Tier" column; each key's value is taken from the corresponding property on a row.
563+
* @returns {string} A Markdown-formatted table with a header row ("Tier" followed by the provided keys' labels) and one row per entry in `rows`.
564+
*/
522565
function toMarkdown(rows, limitKeys = LIMIT_KEYS) {
523566
const headers = ['Tier', ...limitKeys.map(humanLabel)];
524567
const lines = [];
@@ -530,6 +573,13 @@ function toMarkdown(rows, limitKeys = LIMIT_KEYS) {
530573
return lines.join('\n');
531574
}
532575

576+
/**
577+
* Render table rows as an AsciiDoc table.
578+
*
579+
* @param {Array<Object>} rows - Array of row objects; each object must include a `tier` property and values for the keys listed in `limitKeys`.
580+
* @param {Array<string>} [limitKeys=LIMIT_KEYS] - Ordered list of keys to include as columns (excluding the leading "Tier" column).
581+
* @returns {string} An AsciiDoc-formatted table containing a header row ("Tier" plus humanized `limitKeys`) and one data row per entry in `rows`.
582+
*/
533583
function toAsciiDoc(rows, limitKeys = LIMIT_KEYS) {
534584
const headers = ['Tier', ...limitKeys.map(humanLabel)];
535585
let out = '[options="header"]\n|===\n';
@@ -541,6 +591,13 @@ function toAsciiDoc(rows, limitKeys = LIMIT_KEYS) {
541591
return out;
542592
}
543593

594+
/**
595+
* Render rows as CSV with a "Tier" column followed by the provided limit keys.
596+
*
597+
* @param {Array<Object>} rows - Array of row objects where each object contains a `tier` property and keys matching `limitKeys`.
598+
* @param {Array<string>} [limitKeys=LIMIT_KEYS] - Ordered list of keys to include as CSV columns after the "Tier" column.
599+
* @returns {string} CSV-formatted text with a header row and one line per input row; values are quoted and internal quotes doubled.
600+
*/
544601
function toCSV(rows, limitKeys = LIMIT_KEYS) {
545602
const headers = ['Tier', ...limitKeys];
546603
const esc = v => {
@@ -555,6 +612,18 @@ function toCSV(rows, limitKeys = LIMIT_KEYS) {
555612
return lines.join('\n');
556613
}
557614

615+
/**
616+
* Render the provided rows into HTML using a Handlebars template.
617+
*
618+
* The template is compiled from the file at `templatePath` and is invoked with a context
619+
* containing: `rows`, `headers` (array of {name, index_plus_one}), `limitKeys`, `cloudProviders`,
620+
* and `uniqueTiers`.
621+
*
622+
* @param {Array<Object>} rows - Table row objects to render; each row is passed through to the template.
623+
* @param {string} templatePath - Filesystem path to a Handlebars template.
624+
* @param {Array<string>} [limitKeys=LIMIT_KEYS] - Ordered list of limit keys used to build headers and passed to the template.
625+
* @returns {string} The rendered HTML string.
626+
*/
558627
function toHTML(rows, templatePath, limitKeys = LIMIT_KEYS) {
559628
const fs = require('fs');
560629
const handlebars = require('handlebars');
@@ -579,6 +648,18 @@ function toHTML(rows, templatePath, limitKeys = LIMIT_KEYS) {
579648
});
580649
}
581650

651+
/**
652+
* Generate a cloud tier table from local or remote YAML input and master-data, rendered in the requested format.
653+
*
654+
* @param {Object} options - Function options.
655+
* @param {string} options.input - Path or URL to the input YAML (or the special GitHub install-pack API directory URL) containing tiers/config profiles.
656+
* @param {string} [options.output] - Output destination (not used by this function; included for CLI compatibility).
657+
* @param {string} [options.format='html'] - Output format: 'html', 'md' (Markdown), 'adoc' (AsciiDoc), or 'csv'.
658+
* @param {string} [options.template] - Path to a Handlebars template to use for rendering; for 'html' format a default template is used when this is not provided.
659+
* @param {string} [options.masterData] - URL or filesystem path to master-data.yaml used to fetch public tier definitions.
660+
* @param {string[]} [options.limits] - Custom ordered list of limit keys to include; when omitted the default LIMIT_KEYS set is used.
661+
* @returns {string} Generated table content in the requested format (HTML, Markdown, AsciiDoc, or CSV).
662+
*/
582663
async function generateCloudTierTable({
583664
input,
584665
output,
@@ -623,4 +704,4 @@ module.exports = {
623704
findHighestVersionProfile,
624705
parseYaml,
625706
fetchPublicTiers
626-
};
707+
};

tools/cloud-tier-table/generate-discrepancy-report.js

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ const { generateCloudTierTable } = require('./generate-cloud-tier-table.js');
44
const Papa = require('papaparse');
55

66
/**
7-
* Calculate percentage difference between two values
8-
* @param {number} advertised - Advertised value
9-
* @param {number} actual - Actual config value
10-
* @returns {number} Percentage difference (positive = actual is higher, negative = actual is lower)
7+
* Compute the percentage difference from an advertised value to an actual value.
8+
* @param {number} advertised - The reference (advertised) value.
9+
* @param {number} actual - The observed or configured value to compare.
10+
* @returns {number|null} The percentage difference ((actual - advertised) / advertised * 100); `null` if `advertised` is falsy or zero.
1111
*/
1212
function calculatePercentageDiff(advertised, actual) {
1313
if (!advertised || advertised === 0) return null;
1414
return ((actual - advertised) / advertised) * 100;
1515
}
1616

1717
/**
18-
* Format bytes per second values for display
19-
* @param {number} bps - Bytes per second
20-
* @returns {string} Formatted string
18+
* Convert a bytes-per-second value into a human-readable Mbps or Kbps string.
19+
* @param {number} bps - Bytes per second; falsy values (e.g., 0, null, undefined) produce `'N/A'`.
20+
* @returns {string} A formatted throughput string like `"<n.n> Mbps"` or `"<n.n> Kbps"`, or `'N/A'` when input is falsy.
2121
*/
2222
function formatThroughput(bps) {
2323
if (!bps) return 'N/A';
@@ -30,19 +30,21 @@ function formatThroughput(bps) {
3030
}
3131

3232
/**
33-
* Format numbers with commas
34-
* @param {number} num - Number to format
35-
* @returns {string} Formatted number
33+
* Format a number with locale-specific thousands separators.
34+
*
35+
* If `num` is null or undefined the function returns `"N/A"`; a numeric `0` is formatted normally.
36+
* @param {number} num - The number to format.
37+
* @returns {string} The number formatted with locale-specific separators, or `"N/A"` if input is null or undefined.
3638
*/
3739
function formatNumber(num) {
3840
if (!num && num !== 0) return 'N/A';
3941
return num.toLocaleString();
4042
}
4143

4244
/**
43-
* Determine severity of discrepancy
44-
* @param {number} percentDiff - Percentage difference
45-
* @returns {string} Severity level
45+
* Classifies a percentage difference into a severity level.
46+
* @param {number} percentDiff - Percentage difference (positive if actual > advertised, negative if actual < advertised).
47+
* @returns {string} `unknown` if `percentDiff` is null or undefined; otherwise `minor` for absolute percentage <= 5, `moderate` for <= 25, `major` for <= 50, and `critical` for > 50.
4648
*/
4749
function getSeverity(percentDiff) {
4850
if (percentDiff === null || percentDiff === undefined) return 'unknown';
@@ -92,12 +94,13 @@ function safeParseInt(tier, key, tierName) {
9294
}
9395

9496
/**
95-
* Analyze a single metric and create discrepancy entry
96-
* @param {string} metricName - Name of the metric
97-
* @param {number} advertised - Advertised value
98-
* @param {number} actual - Actual configuration value
99-
* @param {Function} formatter - Function to format the values for display
100-
* @returns {Object} Discrepancy analysis object
97+
* Create a discrepancy analysis entry for a single metric.
98+
*
99+
* @param {string} metricName - Human-readable metric name.
100+
* @param {number} advertised - Advertised/configured value to compare against.
101+
* @param {number} actual - Actual observed or configured value.
102+
* @param {(value: number) => string} formatter - Formatter that converts numeric values to display strings.
103+
* @returns {{metric: string, advertised: number, advertisedFormatted: string, actual: number, actualFormatted: string, percentageDiff: number|null, severity: string, emoji: string}} An object containing the metric name, raw and formatted advertised/actual values, the percentage difference (or `null` if unavailable), severity label, and a severity emoji.
101104
*/
102105
function analyzeMetric(metricName, advertised, actual, formatter) {
103106
const percentageDiff = calculatePercentageDiff(advertised, actual);
@@ -116,9 +119,19 @@ function analyzeMetric(metricName, advertised, actual, formatter) {
116119
}
117120

118121
/**
119-
* Generate discrepancy analysis for a single tier
120-
* @param {Object} tier - Tier data object
121-
* @returns {Object} Discrepancy analysis
122+
* Builds a discrepancy analysis object for a cloud tier.
123+
*
124+
* @param {Object} tier - Tier data object containing configuration and advertised values. Expected keys include:
125+
* `Tier` or `tier_name`, `cloud_provider`, `machine_type`, `nodes_count`,
126+
* `advertisedMaxIngress`, `advertisedMaxEgress`, `advertisedMaxPartitionCount`, `advertisedMaxClientCount`,
127+
* `kafka_throughput_limit_node_in_bps`, `kafka_throughput_limit_node_out_bps`,
128+
* `topic_partitions_per_shard`, `kafka_connections_max`.
129+
* @returns {Object} An analysis object with:
130+
* - `tierName` (string): tier identifier,
131+
* - `cloudProvider` (string),
132+
* - `machineType` (string),
133+
* - `nodeCount` (number),
134+
* - `discrepancies` (Array): list of per-metric analysis objects (metric name, advertised value, actual/config value, formatted values, percentageDiff, severity, emoji).
122135
*/
123136
function analyzeTierDiscrepancies(tier) {
124137
const tierName = tier.Tier || tier.tier_name;
@@ -176,9 +189,18 @@ function analyzeTierDiscrepancies(tier) {
176189
}
177190

178191
/**
179-
* Generate a comprehensive discrepancy report
180-
* @param {Object} options - Options object
181-
* @returns {string} Formatted report
192+
* Generate a comprehensive discrepancy report for Redpanda Cloud Tier configurations.
193+
*
194+
* Produces a report that compares advertised tier values against actual configuration values
195+
* and classifies discrepancies by severity. The report can be returned as Markdown (default)
196+
* or as a JSON string containing the generated date, a summary, and per-tier analyses.
197+
*
198+
* @param {Object} [options] - Options to control input sources and output format.
199+
* @param {string} [options.input] - URL or path to the install-pack source used to derive tier data.
200+
* @param {string} [options.masterData] - URL or path to master-data.yaml used to resolve advertised values.
201+
* @param {string} [options.format] - Output format: 'markdown' or 'json' (case-insensitive). Defaults to 'markdown'.
202+
* @returns {string} The generated report as a formatted string (Markdown or JSON).
203+
* @throws {Error} If CSV parsing fails, an unsupported format is requested, or report generation encounters an error.
182204
*/
183205
async function generateDiscrepancyReport(options = {}) {
184206
const {

0 commit comments

Comments
 (0)