@redpanda-data/docs-extensions-and-macros 3.10.1 → 3.11.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/README.adoc CHANGED
@@ -52,6 +52,24 @@ The `collect-bloblang-samples` extension processes Bloblang examples from YAML f
52
52
 
53
53
  It validates, sorts, and attaches the processed examples as a JSON object to the Antora page attributes. The extension ensures examples have unique titles, mandatory fields (`input` and `mapping`), and are sorted in alphabetical order.
54
54
 
55
+ The processed examples are added as JSON to the `page-bloblang-samples` attribute. For example:
56
+
57
+ [,json]
58
+ ----
59
+ {
60
+ "hello-world.yaml": {
61
+ "title": "Hello world",
62
+ "input": "{\n \"message\": \"hello world\"\n}\n",
63
+ "mapping": "root.message = this.message.uppercase()\n"
64
+ },
65
+ "array-processing.yaml": {
66
+ "title": "Array processing",
67
+ "input": "{\n \"numbers\": [1, 2, 3, 4, 5]\n}\n",
68
+ "mapping": "root.even_numbers = this.numbers.filter(n -> n % 2 == 0)"
69
+ }
70
+ }
71
+ ----
72
+
55
73
  ==== Environment variables
56
74
 
57
75
  This extension does not require any environment variables.
@@ -82,26 +100,6 @@ mapping: |
82
100
  root.message = this.message.uppercase()
83
101
  ----
84
102
 
85
- ==== Sample output
86
-
87
- The processed examples are added as JSON to the `page-bloblang-samples` attribute. For example:
88
-
89
- [,json]
90
- ----
91
- {
92
- "hello-world.yaml": {
93
- "title": "Hello world",
94
- "input": "{\n \"message\": \"hello world\"\n}\n",
95
- "mapping": "root.message = this.message.uppercase()\n"
96
- },
97
- "array-processing.yaml": {
98
- "title": "Array processing",
99
- "input": "{\n \"numbers\": [1, 2, 3, 4, 5]\n}\n",
100
- "mapping": "root.even_numbers = this.numbers.filter(n -> n % 2 == 0)"
101
- }
102
- }
103
- ----
104
-
105
103
  === Add pages to root
106
104
 
107
105
  The `add-pages-to-root` extension allows you to copy files from your Antora content catalog to the root of the site during the build process. This is particularly useful for files like `llms.txt` or any custom files that need to be directly accessible at the site's root level.
@@ -183,7 +181,7 @@ This extension does not require any environment variables.
183
181
 
184
182
  There are no configurable options for this extension.
185
183
 
186
- ==== Registration Example
184
+ ==== Registration example
187
185
 
188
186
  ```yaml
189
187
  antora:
@@ -191,6 +189,110 @@ antora:
191
189
  - require: '@redpanda-data/docs-extensions-and-macros/extensions/generate-rp-connect-categories'
192
190
  ```
193
191
 
192
+ === Generate index data
193
+
194
+ The `generate-index-data` extension creates structured index data about doc pages based on configurable filters. The indexed data is saved to a specified attribute in all component versions, enabling the dynamic generation of categorized links and descriptions within your docs using UI templates.
195
+
196
+ This extension allows you to define multiple indexing criteria, such as component, URL filter, and environment type.
197
+
198
+ The generated data is an array of objects, where each object represents a component version. Each object contains the following properties:
199
+
200
+ - `component` (string):
201
+ The name of the Antora component.
202
+
203
+ - `version` (string):
204
+ The version of the component.
205
+
206
+ - `pages` (array):
207
+ A list of pages that match the indexing criteria. Each page contains:
208
+ ** `title` (string): The title of the doc page.
209
+ ** `url` (string): The URL of the doc page relative to the site root.
210
+ ** `description` (string): A brief description sourced from the `:description:` attribute in the AsciiDoc file. Defaults to an empty string if not provided.
211
+
212
+ Example:
213
+
214
+ ```json
215
+ [
216
+ {
217
+ "component": "ROOT",
218
+ "version": "24.3",
219
+ "pages": [
220
+ {
221
+ "title": "Manage Debug Bundles in Redpanda Console",
222
+ "url": "/current/console/ui/generate-bundle/",
223
+ "description": "Learn how to generate, download, and delete debug bundles in Redpanda Console for comprehensive cluster diagnostics."
224
+ },
225
+ ]
226
+ }
227
+ ]
228
+ ```
229
+
230
+ ==== Environment variables
231
+
232
+ This extension does not require any environment variables.
233
+
234
+ ==== Configuration options
235
+
236
+ The extension accepts the following options in the Antora playbook.
237
+
238
+ NOTE: Ensure filters are well-defined to minimize unnecessary processing. Avoid overly broad configurations in `data.sets`.
239
+
240
+ - `data.sets` (required): An object defining one or more indexing configurations. Each configuration (or set) accepts the following options:
241
+
242
+ ** `component` (string, required): The Antora component to search for pages.
243
+
244
+ ** `attribute_name` (string, required): The attribute name to assign the generated index data. This allows pages and templates to reference the index.
245
+
246
+ ** `filter` (string, optional): A substring to match within page URLs.
247
+
248
+ ** `env_type` (string, optional): Matches pages with environment-specific attributes (e.g., Docker, Kubernetes).
249
+
250
+ ** `output_file` (string, optional): Save the generated index data as a JSON file at the specified path. If not provided, no file is created.
251
+
252
+ ==== Example configuration
253
+
254
+ Here's an example configuration to enable the generate-index-data-extension:
255
+
256
+ ```yaml
257
+ antora:
258
+ extensions:
259
+ - require: '@redpanda-data/docs-extensions-and-macros/extensions/generate-index-data-extension'
260
+ data:
261
+ sets:
262
+ console_ui:
263
+ component: ROOT # Search the ROOT component
264
+ filter: console/ui # Filter pages containing this substring in their URL
265
+ attribute_name: console-ui-index # Save the result in this attribute
266
+ output_file: redpanda-labs/console-ui-index.json # Save data to this file
267
+ docker_labs:
268
+ component: redpanda-labs
269
+ filter: docker-compose
270
+ env_type: Docker
271
+ attribute_name: docker-labs-index
272
+ ```
273
+
274
+ ==== Use the generated data
275
+
276
+ The index data can be referenced in AsciiDoc pages by specifying the following required attributes:
277
+
278
+ ```asciidoc
279
+ = CONSOLE UI
280
+ :page-index-data: console-ui-index <1>
281
+ :page-role: index-list <2>
282
+ ```
283
+
284
+ <1> The attribute whose data you want to display on the page. This must match an attribute configured in the extension.
285
+ <2> The page role. This role specfies the UI template that renders the data in the `page-index-data` on the page.
286
+
287
+ You can optionally display pages only if they match the component and version of the current Asciidoc page by adding the `:page-match-component-version:` attribute.
288
+
289
+ ```asciidoc
290
+ = CONSOLE UI
291
+ :page-index-data: console-ui-index
292
+ :page-role: index-list
293
+ :page-match-component-version: ''
294
+ ```
295
+
194
296
  === Redpanda Connect tag modifier
195
297
 
196
298
  This extension updates the playbook to use the latest release tag for the Redpanda Connect documentation. It ensures that the Redpanda Connect documentation is always pulled from the latest release tag available on GitHub.
@@ -0,0 +1,193 @@
1
+ 'use strict';
2
+ const _ = require('lodash');
3
+
4
+ module.exports.register = function ({ config }) {
5
+ const logger = this.getLogger('generate-index-data-extension');
6
+
7
+ this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => {
8
+ // Ensure data.sets exists and is an object
9
+ const setsConfig = _.get(config, 'data.sets', {});
10
+ if (!setsConfig || Object.keys(setsConfig).length === 0) {
11
+ logger.info('No index sets defined in the configuration. Skipping index data generation.');
12
+ return;
13
+ }
14
+
15
+ try {
16
+ // Process each defined index set
17
+ for (const [setName, setParams] of Object.entries(setsConfig)) {
18
+ logger.info(`Processing index set: ${setName}`);
19
+ const { component, filter, env_type, output_file, attribute_name } = setParams;
20
+
21
+ // Validate required parameters
22
+ const requiredParams = ['component', 'attribute_name'];
23
+
24
+ const missingParams = requiredParams.filter(param => !setParams[param]);
25
+
26
+ if (missingParams.length > 0) {
27
+ logger.warn(`One or more required parameters are missing: ${missingParams.join(', ')} for set "${setName}". Skipping.`);
28
+ continue;
29
+ }
30
+
31
+ // Gather items based on the target version
32
+ const items = gatherItems(contentCatalog, {
33
+ component,
34
+ filter,
35
+ env_type,
36
+ }, logger);
37
+
38
+ if (!items.length) {
39
+ continue;
40
+ }
41
+
42
+ const uniqueItems = deduplicateAndSortItems(items);
43
+ await addDataAttributeToComponents(uniqueItems, contentCatalog, attribute_name, logger);
44
+ if (output_file) createIndexFile(uniqueItems, siteCatalog, output_file, logger);
45
+ }
46
+ } catch (error) {
47
+ logger.error(`Failed to generate indexes: ${error.stack}`);
48
+ }
49
+ });
50
+ };
51
+
52
+ /**
53
+ * Gathers items (pages) from the contentCatalog based on the provided setParams.
54
+ * setParams should contain:
55
+ * - component (string, required): Component name to search
56
+ * - filter (string, optional): A substring to match in the URL or other criteria
57
+ * - env_type (string, optional): Deployment type to match ('Docker', 'Kubernetes', 'Linux', 'Redpanda Cloud')
58
+ */
59
+ function gatherItems(contentCatalog, setParams, logger) {
60
+ const { component, filter, env_type } = setParams;
61
+
62
+ // Find the component in the catalog
63
+ const componentObj = contentCatalog.getComponents().find(comp => comp.name === component);
64
+ if (!componentObj) {
65
+ logger.warn(`Component "${component}" not found in the content catalog.`);
66
+ return [];
67
+ }
68
+
69
+ const result = [];
70
+
71
+ // Iterate through all versions of the component
72
+ componentObj.versions.forEach(versionObj => {
73
+ const versionPages = contentCatalog.findBy({ component, family: 'page', version: versionObj.version });
74
+
75
+ logger.debug(`Gathering pages for component "${component}" version "${versionObj.version}". Found ${versionPages.length} pages.`);
76
+
77
+ // Filter pages based on criteria
78
+ const matchedPages = versionPages.filter(page => {
79
+ const deploymentType = getDeploymentType(page.asciidoc.attributes);
80
+ const urlMatches = filter ? page.pub.url.includes(filter) : true;
81
+ const envMatches = env_type ? (deploymentType === env_type) : true;
82
+
83
+ return urlMatches && envMatches;
84
+ });
85
+
86
+ // Map matched pages to the desired structure
87
+ const pages = matchedPages.map(page => ({
88
+ title: page.asciidoc.doctitle,
89
+ url: page.pub.url,
90
+ description: page.asciidoc.attributes.description || ''
91
+ }));
92
+
93
+ // Add the component and version structure, even if pages is empty
94
+ result.push({
95
+ component,
96
+ version: versionObj.version,
97
+ pages
98
+ });
99
+
100
+ logger.debug(`Processed ${pages.length} pages for version "${versionObj.version}".`);
101
+ });
102
+
103
+ logger.debug(`Completed gathering items for component "${component}".`);
104
+ return result;
105
+ }
106
+
107
+ /**
108
+ * Deduplicates items by their URL and returns them in alphabetical order by title.
109
+ */
110
+ function deduplicateAndSortItems(items) {
111
+ return items.map(item => {
112
+ const seenUrls = new Set();
113
+
114
+ // Deduplicate pages by URL
115
+ const deduplicatedPages = item.pages.filter(page => {
116
+ if (seenUrls.has(page.url)) {
117
+ return false;
118
+ }
119
+ seenUrls.add(page.url);
120
+ return true;
121
+ });
122
+
123
+ // Sort pages alphabetically by title
124
+ const sortedPages = deduplicatedPages.sort((a, b) => {
125
+ const titleA = a.title.toLowerCase();
126
+ const titleB = b.title.toLowerCase();
127
+ if (titleA < titleB) return -1;
128
+ if (titleA > titleB) return 1;
129
+ return 0;
130
+ });
131
+
132
+ return {
133
+ ...item,
134
+ pages: sortedPages
135
+ };
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Add the gathered data as an attribute to all components.
141
+ *
142
+ * @param {Array} data - The deduplicated items to set as an attribute.
143
+ * @param {Object} contentCatalog - The content catalog from Antora.
144
+ * @param {string} attributeName - The name of the attribute to set.
145
+ * @param {Object} logger - The Antora logger.
146
+ */
147
+ async function addDataAttributeToComponents(data, contentCatalog, attributeName, logger) {
148
+ try {
149
+ const jsonData = JSON.stringify(data, null, 2);
150
+ const components = await contentCatalog.getComponents();
151
+
152
+ components.forEach(component => {
153
+ component.versions.forEach(({ name, asciidoc }) => {
154
+ asciidoc.attributes[attributeName] = jsonData;
155
+ logger.debug(`Set attribute "${attributeName}" on component "${name}".`);
156
+ });
157
+ });
158
+
159
+ logger.info(`Added "${attributeName}" attribute to relevant component versions.`);
160
+ } catch (error) {
161
+ logger.error(`Failed to add data attribute "${attributeName}": ${error.message}`);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Create a JSON file in the output with the given data.
167
+ */
168
+ function createIndexFile(data, siteCatalog, outputFile, logger) {
169
+ if (!outputFile) {
170
+ logger.info('No output_file specified, skipping JSON file creation.');
171
+ return;
172
+ }
173
+
174
+ const jsonData = JSON.stringify(data, null, 2);
175
+ siteCatalog.addFile({
176
+ contents: Buffer.from(jsonData, 'utf8'),
177
+ out: {
178
+ path: outputFile
179
+ },
180
+ });
181
+ logger.info(`Created "${outputFile}" with indexed data.`);
182
+ }
183
+
184
+ /**
185
+ * Determine the deployment type from AsciiDoc attributes.
186
+ */
187
+ function getDeploymentType(attributes) {
188
+ return attributes['env-kubernetes'] ? 'Kubernetes'
189
+ : attributes['env-linux'] ? 'Linux'
190
+ : attributes['env-docker'] ? 'Docker'
191
+ : attributes['env-cloud'] ? 'Redpanda Cloud'
192
+ : attributes['page-cloud'] ? 'Redpanda Cloud' : '';
193
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "3.10.1",
3
+ "version": "3.11.0",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -33,6 +33,7 @@
33
33
  "./extensions/add-pages-to-root": "./extensions/add-pages-to-root.js",
34
34
  "./extensions/collect-bloblang-samples": "./extensions/collect-bloblang-samples.js",
35
35
  "./extensions/generate-rp-connect-categories": "./extensions/generate-rp-connect-categories.js",
36
+ "./extensions/generate-index-data": "./extensions/generate-index-data.js",
36
37
  "./extensions/generate-rp-connect-info": "./extensions/generate-rp-connect-info.js",
37
38
  "./extensions/add-global-attributes": "./extensions/add-global-attributes.js",
38
39
  "./extensions/version-fetcher/set-latest-version": "./extensions/version-fetcher/set-latest-version.js",