@stackql/provider-utils 0.1.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/LICENSE +21 -0
- package/README.md +320 -0
- package/package.json +52 -0
- package/src/docgen/generator.js +298 -0
- package/src/docgen/helpers.js +572 -0
- package/src/docgen/index.js +5 -0
- package/src/docgen/resource/examples/delete-example.js +74 -0
- package/src/docgen/resource/examples/exec-example.js +121 -0
- package/src/docgen/resource/examples/insert-example.js +211 -0
- package/src/docgen/resource/examples/select-example.js +73 -0
- package/src/docgen/resource/examples/update-example.js +154 -0
- package/src/docgen/resource/examples.js +31 -0
- package/src/docgen/resource/fields.js +82 -0
- package/src/docgen/resource/methods.js +90 -0
- package/src/docgen/resource/overview.js +39 -0
- package/src/docgen/resource/parameters.js +76 -0
- package/src/docgen/resource-content.js +25 -0
- package/src/index.js +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 StackQL Studios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# StackQL Provider Utils
|
|
2
|
+
|
|
3
|
+
A comprehensive toolkit for transforming OpenAPI specs into StackQL providers. Includes parsing, mapping, validation, testing, and documentation generation utilities.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Prerequisites](#prerequisites)
|
|
8
|
+
- [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)
|
|
13
|
+
- [Contributing](#contributing)
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
### For Node.js
|
|
18
|
+
- 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
|
+
```
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### For Node.js Projects
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @stackql/provider-utils
|
|
43
|
+
# or
|
|
44
|
+
yarn add @stackql/provider-utils
|
|
45
|
+
```
|
|
46
|
+
|
|
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
|
+
stackqlConfig: {
|
|
78
|
+
host: 'localhost',
|
|
79
|
+
port: 5444,
|
|
80
|
+
user: 'stackql',
|
|
81
|
+
database: 'stackql'
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log('Documentation generated successfully:', result);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error('Error generating documentation:', error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
testDocGen();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. Set Up Test Data
|
|
95
|
+
|
|
96
|
+
Create the required directory structure:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
mkdir -p test-data/output/src/myservice/v00.00.00000/services
|
|
100
|
+
mkdir -p test-data/provider-data
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Add test files:
|
|
104
|
+
|
|
105
|
+
`test-data/provider-data/headerContent1.txt`:
|
|
106
|
+
```
|
|
107
|
+
---
|
|
108
|
+
title: myservice
|
|
109
|
+
hide_title: false
|
|
110
|
+
hide_table_of_contents: false
|
|
111
|
+
keywords:
|
|
112
|
+
- myservice
|
|
113
|
+
- stackql
|
|
114
|
+
- infrastructure-as-code
|
|
115
|
+
- configuration-as-data
|
|
116
|
+
description: Query and manage myservice resources using SQL
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
# myservice Provider
|
|
120
|
+
|
|
121
|
+
The myservice provider for StackQL allows you to query, deploy, and manage myservice resources using SQL.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`test-data/provider-data/headerContent2.txt`:
|
|
125
|
+
```
|
|
126
|
+
See the [myservice provider documentation](https://myservice.com/docs) for more information.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`test-data/output/src/myservice/v00.00.00000/services/example.yaml`:
|
|
130
|
+
```yaml
|
|
131
|
+
openapi: 3.0.0
|
|
132
|
+
info:
|
|
133
|
+
title: Example Service
|
|
134
|
+
version: 1.0.0
|
|
135
|
+
paths:
|
|
136
|
+
/examples:
|
|
137
|
+
get:
|
|
138
|
+
operationId: listExamples
|
|
139
|
+
responses:
|
|
140
|
+
'200':
|
|
141
|
+
description: Success
|
|
142
|
+
components:
|
|
143
|
+
x-stackQL-resources:
|
|
144
|
+
examples:
|
|
145
|
+
id: myservice.example.examples
|
|
146
|
+
name: examples
|
|
147
|
+
title: Examples
|
|
148
|
+
methods:
|
|
149
|
+
list:
|
|
150
|
+
operation:
|
|
151
|
+
$ref: '#/paths/~1examples/get'
|
|
152
|
+
response:
|
|
153
|
+
mediaType: application/json
|
|
154
|
+
openAPIDocKey: '200'
|
|
155
|
+
sqlVerbs:
|
|
156
|
+
select:
|
|
157
|
+
- $ref: '#/components/x-stackQL-resources/examples/methods/list'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 3. Start StackQL Server
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# In a separate terminal
|
|
164
|
+
stackql srv \
|
|
165
|
+
--pgsrv.port=5444 \
|
|
166
|
+
--pgsrv.tls=false \
|
|
167
|
+
--loglevel=INFO
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 4. Run the Test
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
sh start-stackql-server.sh tests/docgen
|
|
174
|
+
node tests/docgen/test-docgen.js
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Using the Documentation Generator
|
|
178
|
+
|
|
179
|
+
### Basic Example
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
import { docgen } from '@stackql/provider-utils';
|
|
183
|
+
|
|
184
|
+
const options = {
|
|
185
|
+
providerName: 'github',
|
|
186
|
+
providerDir: './output/src/github/v00.00.00000',
|
|
187
|
+
outputDir: './docs',
|
|
188
|
+
providerDataDir: './config/provider-data',
|
|
189
|
+
stackqlConfig: {
|
|
190
|
+
host: 'localhost',
|
|
191
|
+
port: 5444,
|
|
192
|
+
user: 'stackql',
|
|
193
|
+
database: 'stackql'
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const result = await docgen.generateDocs(options);
|
|
198
|
+
console.log(`Generated docs for ${result.totalServices} services and ${result.totalResources} resources`);
|
|
199
|
+
console.log(`Output location: ${result.outputPath}`);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Options
|
|
203
|
+
|
|
204
|
+
| Option | Type | Description | Default |
|
|
205
|
+
|--------|------|-------------|---------|
|
|
206
|
+
| `providerName` | string | Name of the provider (e.g., 'github', 'aws') | Required |
|
|
207
|
+
| `providerDir` | string | Path to provider spec directory | Required |
|
|
208
|
+
| `outputDir` | string | Directory for generated documentation | Required |
|
|
209
|
+
| `providerDataDir` | string | Directory containing provider header files | Required |
|
|
210
|
+
| `stackqlConfig` | object | StackQL server connection configuration | See below |
|
|
211
|
+
|
|
212
|
+
#### StackQL Config Options
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
{
|
|
216
|
+
host: 'localhost', // StackQL server host
|
|
217
|
+
port: 5444, // StackQL server port
|
|
218
|
+
user: 'stackql', // Database user
|
|
219
|
+
database: 'stackql' // Database name
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Directory Structure Requirements
|
|
224
|
+
|
|
225
|
+
### Provider Data Directory
|
|
226
|
+
```
|
|
227
|
+
provider-data/
|
|
228
|
+
├── headerContent1.txt # Provider introduction
|
|
229
|
+
├── headerContent2.txt # Additional provider info
|
|
230
|
+
└── stackql-provider-registry.mdx (optional)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Provider Spec Directory
|
|
234
|
+
```
|
|
235
|
+
output/src/{provider}/v00.00.00000/
|
|
236
|
+
├── provider.yaml
|
|
237
|
+
└── services/
|
|
238
|
+
├── service1.yaml
|
|
239
|
+
├── service2.yaml
|
|
240
|
+
└── ...
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Generated Output
|
|
244
|
+
```
|
|
245
|
+
docs/{provider}-docs/
|
|
246
|
+
├── index.md
|
|
247
|
+
├── stackql-provider-registry.mdx
|
|
248
|
+
└── providers/
|
|
249
|
+
└── {provider}/
|
|
250
|
+
└── {service}/
|
|
251
|
+
├── index.md
|
|
252
|
+
└── {resource}/
|
|
253
|
+
└── index.md
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Troubleshooting
|
|
257
|
+
|
|
258
|
+
### StackQL Server Connection Issues
|
|
259
|
+
- Ensure StackQL server is running: `stackql srv --pgsrv.port=5444`
|
|
260
|
+
- Check if port 5444 is available
|
|
261
|
+
- Verify connection settings in `stackqlConfig`
|
|
262
|
+
|
|
263
|
+
### Missing Provider Data
|
|
264
|
+
- Ensure `headerContent1.txt` and `headerContent2.txt` exist in provider data directory
|
|
265
|
+
- Check file permissions
|
|
266
|
+
|
|
267
|
+
### Empty Documentation
|
|
268
|
+
- Verify provider specs have `x-stackQL-resources` components
|
|
269
|
+
- Check that resources have proper method definitions
|
|
270
|
+
|
|
271
|
+
## API Reference
|
|
272
|
+
|
|
273
|
+
### `docgen.generateDocs(options)`
|
|
274
|
+
|
|
275
|
+
Generates documentation for a StackQL provider.
|
|
276
|
+
|
|
277
|
+
**Parameters:**
|
|
278
|
+
- `options` (Object): Configuration options
|
|
279
|
+
|
|
280
|
+
**Returns:**
|
|
281
|
+
- Promise<Object>: Result object containing:
|
|
282
|
+
- `totalServices`: Number of services processed
|
|
283
|
+
- `totalResources`: Number of resources documented
|
|
284
|
+
- `outputPath`: Path to generated documentation
|
|
285
|
+
|
|
286
|
+
**Example:**
|
|
287
|
+
```javascript
|
|
288
|
+
const result = await docgen.generateDocs({
|
|
289
|
+
providerName: 'aws',
|
|
290
|
+
providerDir: './providers/src/aws/v00.00.00000',
|
|
291
|
+
outputDir: './documentation',
|
|
292
|
+
providerDataDir: './config/aws',
|
|
293
|
+
stackqlConfig: {
|
|
294
|
+
host: 'localhost',
|
|
295
|
+
port: 5444
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `docgen.createResourceIndexContent(...)`
|
|
301
|
+
|
|
302
|
+
Creates markdown content for a single resource. This is a lower-level function used internally by `generateDocs`.
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
|
|
306
|
+
1. Fork the repository
|
|
307
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
308
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
309
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
310
|
+
5. Open a Pull Request
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|
|
315
|
+
|
|
316
|
+
## Support
|
|
317
|
+
|
|
318
|
+
- [StackQL Documentation](https://stackql.io/docs)
|
|
319
|
+
- [GitHub Issues](https://github.com/stackql/stackql-provider-utils/issues)
|
|
320
|
+
- [StackQL Discord](https://discord.gg/stackql)
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stackql/provider-utils",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Utilities for building StackQL providers from OpenAPI specifications.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./docgen": {
|
|
12
|
+
"import": "./src/docgen/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"lint": "eslint src"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"stackql",
|
|
26
|
+
"openapi",
|
|
27
|
+
"infrastructure-as-code",
|
|
28
|
+
"sql",
|
|
29
|
+
"cloud",
|
|
30
|
+
"provider",
|
|
31
|
+
"documentation"
|
|
32
|
+
],
|
|
33
|
+
"author": "StackQL Studios",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@apidevtools/swagger-parser": "^10.1.1",
|
|
37
|
+
"@stackql/deno-openapi-dereferencer": "npm:@jsr/stackql__deno-openapi-dereferencer@^0.3.0",
|
|
38
|
+
"js-yaml": "^4.1.0",
|
|
39
|
+
"pluralize": "^8.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"eslint": "^8.0.0",
|
|
43
|
+
"jest": "^29.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=16.0.0"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/stackql/stackql-provider-utils.git"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// src/docgen/generator.js
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
6
|
+
import { createResourceIndexContent } from './resource-content.js';
|
|
7
|
+
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
8
|
+
import * as deno_openapi_dereferencer from "@stackql/deno-openapi-dereferencer";
|
|
9
|
+
|
|
10
|
+
export async function generateDocs(options) {
|
|
11
|
+
const {
|
|
12
|
+
providerName,
|
|
13
|
+
providerDir, // e.g., 'output/src/heroku/v00.00.00000'
|
|
14
|
+
outputDir, // e.g., 'docs'
|
|
15
|
+
providerDataDir, // e.g., 'config/provider-data'
|
|
16
|
+
// stackqlConfig = {
|
|
17
|
+
// host: 'localhost',
|
|
18
|
+
// port: 5444,
|
|
19
|
+
// user: 'stackql',
|
|
20
|
+
// database: 'stackql'
|
|
21
|
+
// }
|
|
22
|
+
} = options;
|
|
23
|
+
|
|
24
|
+
console.log(`documenting ${providerName}...`);
|
|
25
|
+
|
|
26
|
+
const docsDir = path.join(outputDir, `${providerName}-docs`);
|
|
27
|
+
|
|
28
|
+
// Clean existing docs
|
|
29
|
+
fs.existsSync(`${docsDir}/index.md`) && fs.unlinkSync(`${docsDir}/index.md`);
|
|
30
|
+
fs.existsSync(`${docsDir}/providers`) && fs.rmSync(`${docsDir}/providers`, { recursive: true, force: true });
|
|
31
|
+
|
|
32
|
+
// Check for provider data files
|
|
33
|
+
console.log(providerDataDir);
|
|
34
|
+
try {
|
|
35
|
+
const files = fs.readdirSync(providerDataDir);
|
|
36
|
+
console.log('Files in providerDataDir:', files);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('Error reading providerDataDir:', err.message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const headerContent1Path = path.join(providerDataDir, 'headerContent1.txt');
|
|
45
|
+
const headerContent2Path = path.join(providerDataDir, 'headerContent2.txt');
|
|
46
|
+
const mdxPath = path.join(providerDataDir, 'stackql-provider-registry.mdx');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(headerContent1Path) || !fs.existsSync(headerContent2Path)) {
|
|
49
|
+
throw new Error(`Missing headerContent1.txt or headerContent2.txt in ${providerDataDir}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const headerContent1 = fs.readFileSync(headerContent1Path, 'utf8');
|
|
53
|
+
const headerContent2 = fs.readFileSync(headerContent2Path, 'utf8');
|
|
54
|
+
|
|
55
|
+
// Initialize counters
|
|
56
|
+
let servicesForIndex = [];
|
|
57
|
+
let totalServicesCount = 0;
|
|
58
|
+
let totalResourcesCount = 0;
|
|
59
|
+
|
|
60
|
+
// Process services
|
|
61
|
+
const serviceDir = path.join(providerDir, 'services');
|
|
62
|
+
console.log(`Processing services in ${serviceDir}...`);
|
|
63
|
+
const serviceFiles = fs.readdirSync(serviceDir).filter(file => path.extname(file) === '.yaml');
|
|
64
|
+
|
|
65
|
+
for (const file of serviceFiles) {
|
|
66
|
+
const serviceName = path.basename(file, '.yaml').replace(/-/g, '_');
|
|
67
|
+
console.log(`Processing service: ${serviceName}`);
|
|
68
|
+
servicesForIndex.push(serviceName);
|
|
69
|
+
const filePath = path.join(serviceDir, file);
|
|
70
|
+
totalServicesCount++;
|
|
71
|
+
const serviceFolder = `${docsDir}/providers/${providerName}/${serviceName}`;
|
|
72
|
+
await createDocsForService(filePath, providerName, serviceName, serviceFolder);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`Processed ${totalServicesCount} services`);
|
|
76
|
+
|
|
77
|
+
// Count total resources
|
|
78
|
+
totalResourcesCount = fs.readdirSync(`${docsDir}/providers/${providerName}`, { withFileTypes: true })
|
|
79
|
+
.filter(dirent => dirent.isDirectory())
|
|
80
|
+
.map(dirent => fs.readdirSync(`${docsDir}/providers/${providerName}/${dirent.name}`).length)
|
|
81
|
+
.reduce((a, b) => a + b, 0);
|
|
82
|
+
|
|
83
|
+
console.log(`Processed ${totalResourcesCount} resources`);
|
|
84
|
+
|
|
85
|
+
// Create provider index
|
|
86
|
+
servicesForIndex = [...new Set(servicesForIndex)];
|
|
87
|
+
servicesForIndex.sort();
|
|
88
|
+
|
|
89
|
+
const half = Math.ceil(servicesForIndex.length / 2);
|
|
90
|
+
const firstColumnServices = servicesForIndex.slice(0, half);
|
|
91
|
+
const secondColumnServices = servicesForIndex.slice(half);
|
|
92
|
+
|
|
93
|
+
const indexContent = `${headerContent1}
|
|
94
|
+
|
|
95
|
+
:::info Provider Summary
|
|
96
|
+
|
|
97
|
+
<div class="row">
|
|
98
|
+
<div class="providerDocColumn">
|
|
99
|
+
<span>total services: <b>${totalServicesCount}</b></span><br />
|
|
100
|
+
<span>total resources: <b>${totalResourcesCount}</b></span><br />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
:::
|
|
105
|
+
|
|
106
|
+
${headerContent2}
|
|
107
|
+
|
|
108
|
+
## Services
|
|
109
|
+
<div class="row">
|
|
110
|
+
<div class="providerDocColumn">
|
|
111
|
+
${servicesToMarkdown(providerName, firstColumnServices)}
|
|
112
|
+
</div>
|
|
113
|
+
<div class="providerDocColumn">
|
|
114
|
+
${servicesToMarkdown(providerName, secondColumnServices)}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// Write index
|
|
120
|
+
const indexPath = path.join(docsDir, 'index.md');
|
|
121
|
+
fs.writeFileSync(indexPath, indexContent);
|
|
122
|
+
console.log(`Index file created at ${indexPath}`);
|
|
123
|
+
|
|
124
|
+
// Copy MDX file if exists
|
|
125
|
+
if (fs.existsSync(mdxPath)) {
|
|
126
|
+
fs.copyFileSync(mdxPath, path.join(docsDir, 'stackql-provider-registry.mdx'));
|
|
127
|
+
console.log(`MDX file copied`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
totalServices: totalServicesCount,
|
|
132
|
+
totalResources: totalResourcesCount,
|
|
133
|
+
outputPath: docsDir
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Process each service sequentially
|
|
138
|
+
async function createDocsForService(yamlFilePath, providerName, serviceName, serviceFolder) {
|
|
139
|
+
|
|
140
|
+
const data = yaml.load(fs.readFileSync(yamlFilePath, 'utf8'));
|
|
141
|
+
|
|
142
|
+
// Create a new SwaggerParser instance
|
|
143
|
+
let parser = new SwaggerParser();
|
|
144
|
+
const api = await parser.parse(yamlFilePath);
|
|
145
|
+
const ignorePaths = ["$.components.x-stackQL-resources"];
|
|
146
|
+
let dereferencedAPI;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
dereferencedAPI = await deno_openapi_dereferencer.dereferenceApi(api, "$", ignorePaths);
|
|
150
|
+
dereferencedAPI = await deno_openapi_dereferencer.flattenAllOf(dereferencedAPI);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error("error in dereferencing or flattening:", error);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Create service directory
|
|
156
|
+
if (!fs.existsSync(serviceFolder)) {
|
|
157
|
+
fs.mkdirSync(serviceFolder, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const resourcesObj = data.components['x-stackQL-resources'];
|
|
161
|
+
|
|
162
|
+
if (!resourcesObj) {
|
|
163
|
+
console.warn(`No resources found in ${yamlFilePath}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const resources = [];
|
|
168
|
+
for (let resourceName in resourcesObj) {
|
|
169
|
+
|
|
170
|
+
let resourceData = resourcesObj[resourceName];
|
|
171
|
+
if (!resourceData.id) {
|
|
172
|
+
console.warn(`No 'id' defined for resource: ${resourceName} in service: ${serviceName}`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
resources.push({
|
|
177
|
+
name: resourceName,
|
|
178
|
+
resourceData,
|
|
179
|
+
dereferencedAPI
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Process service index
|
|
184
|
+
const serviceIndexPath = path.join(serviceFolder, 'index.md');
|
|
185
|
+
const serviceIndexContent = await createServiceIndexContent(providerName, serviceName, resources);
|
|
186
|
+
fs.writeFileSync(serviceIndexPath, serviceIndexContent);
|
|
187
|
+
|
|
188
|
+
// Split into columns and process resources one by one
|
|
189
|
+
const halfLength = Math.ceil(resources.length / 2);
|
|
190
|
+
const firstColumn = resources.slice(0, halfLength);
|
|
191
|
+
const secondColumn = resources.slice(halfLength);
|
|
192
|
+
|
|
193
|
+
// Process each resource in first column
|
|
194
|
+
for (const resource of firstColumn) {
|
|
195
|
+
await processResource(providerName, serviceFolder, serviceName, resource);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Process each resource in second column
|
|
199
|
+
for (const resource of secondColumn) {
|
|
200
|
+
await processResource(providerName, serviceFolder, serviceName, resource);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log(`Generated documentation for ${serviceName}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function processResource(providerName, serviceFolder, serviceName, resource) {
|
|
207
|
+
console.log(`Processing resource: ${resource.name}`);
|
|
208
|
+
|
|
209
|
+
const resourceFolder = path.join(serviceFolder, resource.name);
|
|
210
|
+
if (!fs.existsSync(resourceFolder)) {
|
|
211
|
+
fs.mkdirSync(resourceFolder, { recursive: true });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const resourceIndexPath = path.join(resourceFolder, 'index.md');
|
|
215
|
+
const resourceIndexContent = await createResourceIndexContent(
|
|
216
|
+
providerName,
|
|
217
|
+
serviceName,
|
|
218
|
+
resource.name,
|
|
219
|
+
resource.resourceData,
|
|
220
|
+
resource.dereferencedAPI,
|
|
221
|
+
);
|
|
222
|
+
fs.writeFileSync(resourceIndexPath, resourceIndexContent);
|
|
223
|
+
|
|
224
|
+
// After writing the file, force garbage collection if available (optional)
|
|
225
|
+
if (global.gc) {
|
|
226
|
+
global.gc();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function createServiceIndexContent(providerName, serviceName, resources) {
|
|
231
|
+
const totalResources = resources.length; // Calculate the total resources
|
|
232
|
+
|
|
233
|
+
// Sort resources alphabetically by name
|
|
234
|
+
resources.sort((a, b) => a.name.localeCompare(b.name));
|
|
235
|
+
|
|
236
|
+
const halfLength = Math.ceil(totalResources / 2);
|
|
237
|
+
const firstColumnResources = resources.slice(0, halfLength);
|
|
238
|
+
const secondColumnResources = resources.slice(halfLength);
|
|
239
|
+
|
|
240
|
+
// Generate the HTML for resource links in the first column
|
|
241
|
+
const firstColumnLinks = generateResourceLinks(providerName, serviceName, firstColumnResources);
|
|
242
|
+
|
|
243
|
+
// Generate the HTML for resource links in the second column
|
|
244
|
+
const secondColumnLinks = generateResourceLinks(providerName, serviceName, secondColumnResources);
|
|
245
|
+
|
|
246
|
+
// Create the markdown content for the service index
|
|
247
|
+
// You can customize this content as needed
|
|
248
|
+
return `---
|
|
249
|
+
title: ${serviceName}
|
|
250
|
+
hide_title: false
|
|
251
|
+
hide_table_of_contents: false
|
|
252
|
+
keywords:
|
|
253
|
+
- ${serviceName}
|
|
254
|
+
- ${providerName}
|
|
255
|
+
- stackql
|
|
256
|
+
- infrastructure-as-code
|
|
257
|
+
- configuration-as-data
|
|
258
|
+
- cloud inventory
|
|
259
|
+
description: Query, deploy and manage ${providerName} resources using SQL
|
|
260
|
+
custom_edit_url: null
|
|
261
|
+
image: /img/providers/${providerName}/stackql-${providerName}-provider-featured-image.png
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
${serviceName} service documentation.
|
|
265
|
+
|
|
266
|
+
:::info Service Summary
|
|
267
|
+
|
|
268
|
+
<div class="row">
|
|
269
|
+
<div class="providerDocColumn">
|
|
270
|
+
<span>total resources: <b>${totalResources}</b></span><br />
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
:::
|
|
275
|
+
|
|
276
|
+
## Resources
|
|
277
|
+
<div class="row">
|
|
278
|
+
<div class="providerDocColumn">
|
|
279
|
+
${firstColumnLinks}
|
|
280
|
+
</div>
|
|
281
|
+
<div class="providerDocColumn">
|
|
282
|
+
${secondColumnLinks}
|
|
283
|
+
</div>
|
|
284
|
+
</div>`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function generateResourceLinks(providerName, serviceName, resources) {
|
|
288
|
+
// Generate resource links for the service index
|
|
289
|
+
const resourceLinks = resources.map((resource) => {
|
|
290
|
+
return `<a href="/providers/${providerName}/${serviceName}/${resource.name}/">${resource.name}</a>`;
|
|
291
|
+
});
|
|
292
|
+
return resourceLinks.join('<br />\n');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Function to convert services to markdown links
|
|
296
|
+
function servicesToMarkdown(providerName, servicesList) {
|
|
297
|
+
return servicesList.map(service => `<a href="/providers/${providerName}/${service}/">${service}</a><br />`).join('\n');
|
|
298
|
+
}
|