@stackql/provider-utils 0.4.0 → 0.4.2

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.md CHANGED
@@ -1,42 +1,33 @@
1
1
  # StackQL Provider Utils
2
2
 
3
- A comprehensive toolkit for transforming OpenAPI specs into StackQL providers. Includes parsing, mapping, validation, testing, and documentation generation utilities.
3
+ ![NPM Version](https://img.shields.io/npm/v/%40stackql%2Fprovider-utils)
4
+ ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/stackql/stackql/total?style=plastic&label=stackql%20downloads)
5
+
6
+ A comprehensive toolkit for transforming OpenAPI specifications into StackQL providers. This library streamlines the process of parsing, mapping, validating, testing, and generating documentation for StackQL providers.
4
7
 
5
8
  ## Table of Contents
6
9
 
7
10
  - [Prerequisites](#prerequisites)
8
11
  - [Installation](#installation)
9
- - [Local Development Setup](#local-development-setup)
10
- - [Testing with Node.js](#testing-with-nodejs)
11
- - [Using the Documentation Generator](#using-the-documentation-generator)
12
- - [API Reference](#api-reference)
12
+ - [Directory Structure](#directory-structure)
13
+ - [Provider Development Workflow](#provider-development-workflow)
14
+ - [`providerdev.split`](docs/split.md) - Divide a large OpenAPI specification into smaller, service-specific files
15
+ - [`providerdev.analyze`](docs/analyze.md) - Examine split API specifications to generate mapping recommendations
16
+ - [`providerdev.generate`](docs/generate.md) - Create StackQL provider extensions from specifications and mappings
17
+ - [`docgen.generateDocs`](docs/docgen.md) - Generate comprehensive documentation for StackQL providers
13
18
  - [Contributing](#contributing)
19
+ - [License](#license)
20
+ - [Support](#support)
14
21
 
15
22
  ## Prerequisites
16
23
 
17
- ### For Node.js
18
24
  - Node.js >= 20
19
- - npm or yarn
20
- - StackQL server (for documentation generation)
21
-
22
- ### Installing StackQL
23
-
24
- Download and install StackQL from [stackql.io/downloads](https://stackql.io/downloads)
25
-
26
- ```bash
27
- # macOS
28
- brew install stackql
29
-
30
- # Linux
31
- curl -L https://bit.ly/stackql-zip -O && unzip stackql-zip
32
-
33
- # Windows
34
- # Download from https://stackql.io/downloads
35
- ```
25
+ - `npm` or `yarn`
26
+ - [`stackql`](https://stackql.io/docs/installing-stackql) for testing
36
27
 
37
28
  ## Installation
38
29
 
39
- ### For Node.js Projects
30
+ Add `@stackql/provider-utils` to your `package.json`:
40
31
 
41
32
  ```bash
42
33
  npm install @stackql/provider-utils
@@ -44,229 +35,55 @@ npm install @stackql/provider-utils
44
35
  yarn add @stackql/provider-utils
45
36
  ```
46
37
 
47
- ## Local Development Setup
48
-
49
- 1. Clone the repository:
50
- ```bash
51
- git clone https://github.com/stackql/stackql-provider-utils.git
52
- cd stackql-provider-utils
53
- ```
54
-
55
- 2. Install dependencies (Node.js):
56
- ```bash
57
- npm install
58
- ```
59
-
60
- ## Testing with Node.js
61
-
62
- ### 1. Create a Test Script
63
-
64
- Create a file `test-docgen.js`:
65
-
66
- ```javascript
67
- import { docgen } from './src/index.js';
68
-
69
- // Test the documentation generator
70
- async function testDocGen() {
71
- try {
72
- const result = await docgen.generateDocs({
73
- providerName: 'myservice',
74
- providerDir: './test-data/output/src/myservice/v00.00.00000',
75
- outputDir: './test-output',
76
- providerDataDir: './test-data/provider-data',
77
- });
78
-
79
- console.log('Documentation generated successfully:', result);
80
- } catch (error) {
81
- console.error('Error generating documentation:', error);
82
- }
83
- }
84
-
85
- testDocGen();
86
- ```
87
-
88
- ### 2. Set Up Test Data
89
-
90
- Create the required directory structure:
91
-
92
- ```bash
93
- mkdir -p test-data/output/src/myservice/v00.00.00000/services
94
- mkdir -p test-data/provider-data
95
- ```
96
-
97
- Add test files:
98
-
99
- `test-data/provider-data/headerContent1.txt`:
100
- ```
101
- ---
102
- title: myservice
103
- hide_title: false
104
- hide_table_of_contents: false
105
- keywords:
106
- - myservice
107
- - stackql
108
- - infrastructure-as-code
109
- - configuration-as-data
110
- description: Query and manage myservice resources using SQL
111
- ---
112
-
113
- # myservice Provider
114
-
115
- The myservice provider for StackQL allows you to query, deploy, and manage myservice resources using SQL.
116
- ```
117
-
118
- `test-data/provider-data/headerContent2.txt`:
119
- ```
120
- See the [myservice provider documentation](https://myservice.com/docs) for more information.
121
- ```
122
-
123
- `test-data/output/src/myservice/v00.00.00000/services/example.yaml`:
124
- ```yaml
125
- openapi: 3.0.0
126
- info:
127
- title: Example Service
128
- version: 1.0.0
129
- paths:
130
- /examples:
131
- get:
132
- operationId: listExamples
133
- responses:
134
- '200':
135
- description: Success
136
- components:
137
- x-stackQL-resources:
138
- examples:
139
- id: myservice.example.examples
140
- name: examples
141
- title: Examples
142
- methods:
143
- list:
144
- operation:
145
- $ref: '#/paths/~1examples/get'
146
- response:
147
- mediaType: application/json
148
- openAPIDocKey: '200'
149
- sqlVerbs:
150
- select:
151
- - $ref: '#/components/x-stackQL-resources/examples/methods/list'
152
- ```
38
+ ## Directory Structure
153
39
 
154
- ### 3. Run the Test
40
+ A typical project structure for the development of a `stackql` provider would be...
155
41
 
156
42
  ```bash
157
- node tests/docgen/test-docgen.js snowflake
158
- node tests/docgen/test-docgen.js google
159
- node tests/docgen/test-docgen.js homebrew
160
- ```
161
-
162
-
163
- node tests/providerdev/test-split.js okta tests/providerdev/split-source/okta/management-minimal.yaml path
164
- node tests/providerdev/test-analyze.js okta
165
- node tests/providerdev/test-generate.js okta
166
-
167
- ## Using the Documentation Generator
168
-
169
- ### Basic Example
170
-
171
- ```javascript
172
- import { docgen } from '@stackql/provider-utils';
173
-
174
- const options = {
175
- providerName: 'github',
176
- providerDir: './output/src/github/v00.00.00000',
177
- outputDir: './docs',
178
- providerDataDir: './config/provider-data',
179
- };
180
-
181
- const result = await docgen.generateDocs(options);
182
- console.log(`Generated docs for ${result.totalServices} services and ${result.totalResources} resources`);
183
- console.log(`Output location: ${result.outputPath}`);
184
- ```
185
-
186
- ### Options
187
-
188
- | Option | Type | Description | Default |
189
- |--------|------|-------------|---------|
190
- | `providerName` | string | Name of the provider (e.g., 'github', 'aws') | Required |
191
- | `providerDir` | string | Path to provider spec directory | Required |
192
- | `outputDir` | string | Directory for generated documentation | Required |
193
- | `providerDataDir` | string | Directory containing provider header files | Required |
194
-
195
- ## Directory Structure Requirements
196
-
197
- ### Provider Data Directory
198
- ```
199
- provider-data/
200
- ├── headerContent1.txt # Provider introduction
201
- ├── headerContent2.txt # Additional provider info
202
- ```
203
-
204
- ### Provider Spec Directory
205
- ```
206
- output/src/{provider}/v00.00.00000/
207
- ├── provider.yaml
208
- └── services/
209
- ├── service1.yaml
210
- ├── service2.yaml
211
- └── ...
212
- ```
213
-
214
- ### Generated Output
215
- ```
216
- docs/{provider}-docs/
217
- ├── index.md
218
- └── {service}/
219
- ├── index.md
220
- └── {resource}/
221
- └── index.md
222
- ```
223
-
224
- ## Troubleshooting
225
-
226
- ### Missing Provider Data
227
- - Ensure `headerContent1.txt` and `headerContent2.txt` exist in provider data directory
228
- - Check file permissions
229
-
230
- ### Empty Documentation
231
- - Verify provider specs have `x-stackQL-resources` components
232
- - Check that resources have proper method definitions
233
-
234
- ## API Reference
235
-
236
- ### `docgen.generateDocs(options)`
237
-
238
- Generates documentation for a StackQL provider.
239
-
240
- **Parameters:**
241
- - `options` (Object): Configuration options
242
-
243
- **Returns:**
244
- - Promise<Object>: Result object containing:
245
- - `totalServices`: Number of services processed
246
- - `totalResources`: Number of resources documented
247
- - `outputPath`: Path to generated documentation
248
-
249
- **Example:**
250
- ```javascript
251
- const result = await docgen.generateDocs({
252
- providerName: 'aws',
253
- providerDir: './providers/src/aws/v00.00.00000',
254
- outputDir: './documentation',
255
- providerDataDir: './config/aws',
256
- });
257
- ```
258
-
259
- ### `docgen.createResourceIndexContent(...)`
260
-
261
- Creates markdown content for a single resource. This is a lower-level function used internally by `generateDocs`.
43
+ .
44
+ ├── bin # convinience scripts
45
+ │   ├── ...
46
+ ├── provider-dev
47
+ │   ├── config
48
+ │   │   └── all_services.csv # mappings generated or updated by the `providerdev.analyze` function, used by `providerdev.generate`
49
+ │   ├── docgen
50
+ │   │   └── provider-data # provider metadata used by `docgen.generateDocs`
51
+ │   │   ├── headerContent1.txt
52
+ │   │   └── headerContent2.txt
53
+ │   ├── downloaded # used to store the original spec for the provider
54
+ │   │   └── management-minimal.yaml
55
+ │   ├── openapi # output from `providerdev.generate`, this is the stackql provider
56
+ │   │   └── src
57
+ │   │   └── okta
58
+ │   │   └── v00.00.00000
59
+ │   │   ├── provider.yaml
60
+ │   │   └── services
61
+ │   │   ├── agentpools.yaml
62
+ │   │   ├── ...
63
+ │   ├── scripts # optional scripts for pre or post processing if required
64
+ │   │   └── post_processing.sh
65
+ │   └── source # output from `providerdev.split` if used, this is the source used with the mappings to generate the provider
66
+ │   ├── agentpools.yaml
67
+ │   ├── ...
68
+ └── website # docusaurus site
69
+ ├── docs # output from `docgen.generateDocs`
70
+ │   ├── ...
71
+ ```
72
+
73
+ > see [__stackql-provider-okta__](https://github.com/stackql/stackql-provider-okta) for a working example.
74
+
75
+ ## Provider Development Workflow
76
+
77
+ The library provides a streamlined workflow for creating StackQL providers from OpenAPI specifications:
78
+
79
+ 1. [`providerdev.split`](docs/split.md) - Divide a large OpenAPI specification into smaller, service-specific files
80
+ 2. [`providerdev.analyze`](docs/analyze.md) - Examine split API specifications to generate mapping recommendations
81
+ 3. [`providerdev.generate`](docs/generate.md) - Create StackQL provider extensions from specifications and mappings
82
+ 4. [`docgen.generateDocs`](docs/docgen.md) - Generate comprehensive documentation for StackQL providers
262
83
 
263
84
  ## Contributing
264
85
 
265
- 1. Fork the repository
266
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
267
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
268
- 4. Push to the branch (`git push origin feature/amazing-feature`)
269
- 5. Open a Pull Request
86
+ Contributions are welcome!
270
87
 
271
88
  ## License
272
89
 
@@ -276,4 +93,5 @@ MIT
276
93
 
277
94
  - [StackQL Documentation](https://stackql.io/docs)
278
95
  - [GitHub Issues](https://github.com/stackql/stackql-provider-utils/issues)
279
- - [StackQL Discord](https://discord.gg/stackql)
96
+ - [StackQL Community Slack](https://stackqlcommunity.slack.com/join/shared_invite/zt-1cbdq9s5v-CkY65IMAesCgFqjN6FU6hg)
97
+ - [StackQL Discord](https://discord.com/invite/xVXZ9d5NxN)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackql/provider-utils",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Utilities for building StackQL providers from OpenAPI specifications.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -7,6 +7,13 @@ import { docView } from './view.js';
7
7
 
8
8
  const mdCodeAnchor = "`";
9
9
 
10
+ // List of meaningless descriptions to filter out (all lowercase)
11
+ const meaninglessDescriptions = [
12
+ 'ok',
13
+ 'successful response',
14
+ 'success'
15
+ ];
16
+
10
17
  export function createFieldsSection(resourceType, resourceData, dereferencedAPI) {
11
18
  let content = '## Fields\n\n';
12
19
 
@@ -41,14 +48,14 @@ export function createFieldsSection(resourceType, resourceData, dereferencedAPI)
41
48
  // Start the TabItem
42
49
  content += `<TabItem value="${methodName}">\n\n`;
43
50
 
44
- // Add the method description if available
45
- if (methodData.respDescription
46
- && methodData.respDescription.trim().toUpperCase() !== 'OK'
47
- && methodData.respDescription.trim() !== 'Successful response'
48
- ) {
51
+ // Add the method description if available and not in the meaningless list
52
+ if (methodData.respDescription &&
53
+ !meaninglessDescriptions.includes(methodData.respDescription.trim().toLowerCase()) &&
54
+ methodData.respDescription.trim().length > 0) {
49
55
  content += `${sanitizeHtml(methodData.respDescription)}\n\n`;
50
56
  }
51
57
 
58
+
52
59
  // Add the table header
53
60
  content += `<table>
54
61
  <thead>
@@ -4,6 +4,24 @@ import {
4
4
  sanitizeHtml
5
5
  } from '../helpers.js';
6
6
 
7
+ const getRequiredBodyParams = (methodDetails, accessType) => {
8
+ // Only process request body for insert, update, replace, and exec
9
+ if (!['insert', 'update', 'replace', 'exec'].includes(accessType)) {
10
+ return [];
11
+ }
12
+
13
+ // Get required body params if they exist
14
+ const requiredBodyProps = methodDetails.requestBody?.required ? methodDetails.requestBody.required : [];
15
+
16
+ // For insert, update, and replace, prefix with data__
17
+ if (['insert', 'update', 'replace'].includes(accessType)) {
18
+ return requiredBodyProps.map(prop => `data__${prop}`);
19
+ } else {
20
+ // For exec, don't prefix
21
+ return requiredBodyProps;
22
+ }
23
+ };
24
+
7
25
  export function createMethodsSection(resourceData, dereferencedAPI) {
8
26
 
9
27
  let content = `\n## Methods\n\n`;
@@ -37,10 +55,16 @@ export function createMethodsSection(resourceData, dereferencedAPI) {
37
55
  for (const [methodName, methodDetails] of Object.entries(methods)) {
38
56
  console.info(`Adding ${accessType} method to table: ${methodName}`);
39
57
 
58
+ // Get required params from both the standard params and the request body
59
+ const reqParamsArr = Object.keys(methodDetails.requiredParams || {});
60
+ const reqBodyParamsArr = getRequiredBodyParams(methodDetails, accessType);
61
+
62
+ // Combine both types of required parameters
63
+ const allReqParamsArr = [...reqParamsArr, ...reqBodyParamsArr];
64
+
40
65
  // Format required params as comma-delimited list with hyperlinks
41
- const requiredParamsArr = Object.keys(methodDetails.requiredParams || {});
42
- const requiredParamsStr = requiredParamsArr.length > 0
43
- ? requiredParamsArr.map(param => `<a href="#parameter-${param}"><code>${param}</code></a>`).join(', ')
66
+ const requiredParamsStr = allReqParamsArr.length > 0
67
+ ? allReqParamsArr.map(param => `<a href="#parameter-${param}"><code>${param}</code></a>`).join(', ')
44
68
  : '';
45
69
 
46
70
  // Format optional params as comma-delimited list with hyperlinks
@@ -48,7 +72,7 @@ export function createMethodsSection(resourceData, dereferencedAPI) {
48
72
  const optionalParamsStr = optionalParamsArr.length > 0
49
73
  ? optionalParamsArr.map(param => `<a href="#parameter-${param}"><code>${param}</code></a>`).join(', ')
50
74
  : '';
51
-
75
+
52
76
  // Add the method row to the table
53
77
  content += `
54
78
  <tr>
@@ -95,6 +95,23 @@ function findExistingMapping(spec, pathRef) {
95
95
  };
96
96
  }
97
97
 
98
+ /**
99
+ * Escape and sanitize a CSV field value
100
+ * @param {string} value - Field value to escape
101
+ * @returns {string} - Escaped value
102
+ */
103
+ function escapeCsvField(value) {
104
+ if (!value) return '';
105
+
106
+ // If the value contains commas, double quotes, or newlines, wrap it in quotes
107
+ // and escape any existing double quotes by doubling them
108
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
109
+ return `"${value.replace(/"/g, '""')}"`;
110
+ }
111
+
112
+ return value;
113
+ }
114
+
98
115
  /**
99
116
  * Analyze OpenAPI specs and generate mapping CSV
100
117
  * @param {Object} options - Options for analysis
@@ -159,7 +176,7 @@ export async function analyze(options) {
159
176
 
160
177
  // Only write header if creating a new file
161
178
  if (!fileExists) {
162
- writer.write('filename,path,operationId,formatted_op_id,verb,response_object,tags,formatted_tags,stackql_resource_name,stackql_method_name,stackql_verb\n');
179
+ writer.write('filename,path,operationId,formatted_op_id,verb,response_object,tags,formatted_tags,stackql_resource_name,stackql_method_name,stackql_verb,op_description\n');
163
180
  }
164
181
 
165
182
  const files = fs.readdirSync(inputDir);
@@ -216,15 +233,27 @@ export async function analyze(options) {
216
233
  // Find existing mapping if available
217
234
  const { resourceName, methodName, sqlVerb } = findExistingMapping(spec, pathRef);
218
235
 
219
- // Escape commas in fields
220
- const escapedPath = pathKey.includes(',') ? `"${pathKey}"` : pathKey;
221
- const escapedOperationId = operationId.includes(',') ? `"${operationId}"` : operationId;
222
- const escapedFormattedOpId = formattedOpId.includes(',') ? `"${formattedOpId}"` : formattedOpId;
223
- const escapedTags = tagsStr.includes(',') ? `"${tagsStr}"` : tagsStr;
224
- const escapedFormattedTags = formattedTags.includes(',') ? `"${formattedTags}"` : formattedTags;
236
+ // Get operation description
237
+ const opDescription = operation.summary || operation.description || '';
238
+
239
+ // Escape fields that might contain commas, quotes, or other special characters
240
+ const escapedFields = {
241
+ filename: escapeCsvField(filename),
242
+ path: escapeCsvField(pathKey),
243
+ operationId: escapeCsvField(operationId),
244
+ formattedOpId: escapeCsvField(formattedOpId),
245
+ verb: escapeCsvField(verb),
246
+ responseRef: escapeCsvField(responseRef),
247
+ tagsStr: escapeCsvField(tagsStr),
248
+ formattedTags: escapeCsvField(formattedTags),
249
+ resourceName: escapeCsvField(resourceName),
250
+ methodName: escapeCsvField(methodName),
251
+ sqlVerb: escapeCsvField(sqlVerb),
252
+ opDescription: escapeCsvField(opDescription)
253
+ };
225
254
 
226
255
  // Write row
227
- writer.write(`${filename},${escapedPath},${escapedOperationId},${escapedFormattedOpId},${verb},${responseRef},${escapedTags},${escapedFormattedTags},${resourceName},${methodName},${sqlVerb}\n`);
256
+ writer.write(`${escapedFields.filename},${escapedFields.path},${escapedFields.operationId},${escapedFields.formattedOpId},${escapedFields.verb},${escapedFields.responseRef},${escapedFields.tagsStr},${escapedFields.formattedTags},${escapedFields.resourceName},${escapedFields.methodName},${escapedFields.sqlVerb},${escapedFields.opDescription}\n`);
228
257
  }
229
258
  }
230
259
  }
@@ -11,7 +11,6 @@ import {
11
11
 
12
12
  // Constants
13
13
  const OPERATIONS = ["get", "post", "put", "delete", "patch", "options", "head", "trace"];
14
- const NON_OPERATIONS = ["parameters", "servers", "summary", "description"];
15
14
  const COMPONENTS_CHILDREN = ["schemas", "responses", "parameters", "examples", "requestBodies", "headers", "securitySchemes", "links", "callbacks"];
16
15
 
17
16
  /**
@@ -42,9 +41,10 @@ function isOperationExcluded(exclude, opItem) {
42
41
  * @param {string} svcDiscriminator - Service discriminator
43
42
  * @param {Object[]} allTags - All tags from API doc
44
43
  * @param {boolean} debug - Debug flag
44
+ * @param {Object} svcNameOverrides - Service name overrides
45
45
  * @returns {[string, string]} - [service name, service description]
46
46
  */
47
- function retServiceNameAndDesc(providerName, opItem, pathKey, svcDiscriminator, allTags, debug) {
47
+ function retServiceNameAndDesc(providerName, opItem, pathKey, svcDiscriminator, allTags, debug, svcNameOverrides) {
48
48
  let service = "default";
49
49
  let serviceDesc = `${providerName} API`;
50
50
 
@@ -84,9 +84,25 @@ function retServiceNameAndDesc(providerName, opItem, pathKey, svcDiscriminator,
84
84
  return ["skip", ""];
85
85
  }
86
86
 
87
+ // Apply service name overrides if present
88
+ if (svcNameOverrides && svcNameOverrides[service]) {
89
+ const newName = svcNameOverrides[service];
90
+ if (debug) {
91
+ logger.debug(`Overriding service name: ${service} -> ${newName}`);
92
+ }
93
+
94
+ // Update service description for path-based services
95
+ if (svcDiscriminator === "path") {
96
+ serviceDesc = `${providerName} ${newName} API`;
97
+ }
98
+
99
+ service = newName;
100
+ }
101
+
87
102
  return [service, serviceDesc];
88
103
  }
89
104
 
105
+
90
106
  /**
91
107
  * Initialize service map
92
108
  * @param {Object} services - Services map
@@ -192,43 +208,6 @@ function getPathLevelRefs(pathItem) {
192
208
  return refs;
193
209
  }
194
210
 
195
- /**
196
- * Add referenced components to service
197
- * @param {Set<string>} refs - Set of refs
198
- * @param {Object} service - Service object
199
- * @param {Object} components - Components from API doc
200
- * @param {boolean} debug - Debug flag
201
- */
202
- function addRefsToComponents(refs, service, components, debug) {
203
- for (const ref of refs) {
204
- const parts = ref.split('/');
205
-
206
- // Only process refs that point to components
207
- if (parts.length >= 4 && parts[1] === "components") {
208
- const componentType = parts[2];
209
- const componentName = parts[3];
210
-
211
- // Check if component type exists in service
212
- if (!service.components[componentType]) {
213
- service.components[componentType] = {};
214
- }
215
-
216
- // Skip if component already added
217
- if (service.components[componentType][componentName]) {
218
- continue;
219
- }
220
-
221
- // Add component if it exists in source document
222
- if (components[componentType] && components[componentType][componentName]) {
223
- service.components[componentType][componentName] = components[componentType][componentName];
224
- if (debug) {
225
- logger.debug(`Added component ${componentType}/${componentName}`);
226
- }
227
- }
228
- }
229
- }
230
- }
231
-
232
211
  /**
233
212
  * Add missing type: object to schema objects
234
213
  * @param {any} obj - Object to process
@@ -338,7 +317,8 @@ export async function split(options) {
338
317
  svcDiscriminator = "tag",
339
318
  exclude = null,
340
319
  overwrite = true,
341
- verbose = false
320
+ verbose = false,
321
+ svcNameOverrides = {} // Add this new parameter with default empty object
342
322
  } = options;
343
323
 
344
324
  // Setup logging based on verbosity
@@ -351,6 +331,13 @@ export async function split(options) {
351
331
  logger.info(`Output: ${outputDir}`);
352
332
  logger.info(`Service Discriminator: ${svcDiscriminator}`);
353
333
 
334
+ if (Object.keys(svcNameOverrides).length > 0) {
335
+ logger.info(`Using ${Object.keys(svcNameOverrides).length} service name overrides`);
336
+ if (verbose) {
337
+ logger.debug(`Service name overrides: ${JSON.stringify(svcNameOverrides, null, 2)}`);
338
+ }
339
+ }
340
+
354
341
  // Process exclude list
355
342
  const excludeList = exclude ? exclude.split(",") : [];
356
343
 
@@ -409,12 +396,11 @@ export async function split(options) {
409
396
  continue;
410
397
  }
411
398
 
412
- // Determine service name
413
399
  const [service, serviceDesc] = retServiceNameAndDesc(
414
400
  providerName, opItem, pathKey, svcDiscriminator,
415
- apiDocObj.tags || [], verbose
416
- );
417
-
401
+ apiDocObj.tags || [], verbose, svcNameOverrides
402
+ );
403
+
418
404
  // Skip if service is marked to skip
419
405
  if (service === 'skip') {
420
406
  logger.warn(`⭐️ Skipping service: ${service}`);