@stackql/provider-utils 0.2.3 → 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.
- package/README.md +8 -1
- package/package.json +2 -1
- package/src/docgen/generator.js +27 -13
- package/src/docgen/helpers.js +26 -32
- package/src/docgen/resource/fields.js +53 -41
- package/src/docgen/resource/overview.js +7 -3
- package/src/docgen/resource/view.js +130 -0
- package/src/docgen/resource-content.js +6 -8
- package/src/index.js +3 -2
- package/src/logger.js +52 -0
- package/src/providerdev/analyze.js +169 -0
- package/src/providerdev/generate.js +307 -0
- package/src/providerdev/index.js +10 -0
- package/src/providerdev/split.js +492 -0
- package/src/utils.js +42 -0
package/README.md
CHANGED
|
@@ -154,9 +154,16 @@ components:
|
|
|
154
154
|
### 3. Run the Test
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
|
-
node tests/docgen/test-docgen.js
|
|
157
|
+
node tests/docgen/test-docgen.js snowflake
|
|
158
|
+
node tests/docgen/test-docgen.js google
|
|
159
|
+
node tests/docgen/test-docgen.js homebrew
|
|
158
160
|
```
|
|
159
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
|
+
|
|
160
167
|
## Using the Documentation Generator
|
|
161
168
|
|
|
162
169
|
### Basic Example
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackql/provider-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Utilities for building StackQL providers from OpenAPI specifications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@apidevtools/swagger-parser": "^10.1.1",
|
|
38
38
|
"@stackql/deno-openapi-dereferencer": "npm:@jsr/stackql__deno-openapi-dereferencer@^0.3.1",
|
|
39
|
+
"csv-parser": "^3.2.0",
|
|
39
40
|
"js-yaml": "^4.1.0",
|
|
40
41
|
"pluralize": "^8.0.0"
|
|
41
42
|
},
|
package/src/docgen/generator.js
CHANGED
|
@@ -13,6 +13,7 @@ export async function generateDocs(options) {
|
|
|
13
13
|
providerDir, // e.g., 'output/src/heroku/v00.00.00000'
|
|
14
14
|
outputDir, // e.g., 'website'
|
|
15
15
|
providerDataDir, // e.g., 'config/provider-data'
|
|
16
|
+
dereferenced = false,
|
|
16
17
|
} = options;
|
|
17
18
|
|
|
18
19
|
console.log(`documenting ${providerName}...`);
|
|
@@ -60,7 +61,7 @@ export async function generateDocs(options) {
|
|
|
60
61
|
const filePath = path.join(serviceDir, file);
|
|
61
62
|
totalServicesCount++;
|
|
62
63
|
const serviceFolder = `${servicesDir}/${serviceName}`;
|
|
63
|
-
await createDocsForService(filePath, providerName, serviceName, serviceFolder);
|
|
64
|
+
await createDocsForService(filePath, providerName, serviceName, serviceFolder, dereferenced);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
console.log(`Processed ${totalServicesCount} services`);
|
|
@@ -116,7 +117,7 @@ ${servicesToMarkdown(providerName, secondColumnServices)}
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
// Process each service sequentially
|
|
119
|
-
async function createDocsForService(yamlFilePath, providerName, serviceName, serviceFolder) {
|
|
120
|
+
async function createDocsForService(yamlFilePath, providerName, serviceName, serviceFolder, dereferenced = false) {
|
|
120
121
|
|
|
121
122
|
const data = yaml.load(fs.readFileSync(yamlFilePath, 'utf8'));
|
|
122
123
|
|
|
@@ -126,12 +127,17 @@ async function createDocsForService(yamlFilePath, providerName, serviceName, ser
|
|
|
126
127
|
const ignorePaths = ["$.components.x-stackQL-resources"];
|
|
127
128
|
let dereferencedAPI;
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
dereferencedAPI =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
if (dereferenced) {
|
|
131
|
+
// If API is already dereferenced, just use it as is
|
|
132
|
+
dereferencedAPI = api;
|
|
133
|
+
} else {
|
|
134
|
+
try {
|
|
135
|
+
// Only dereference and flatten if needed
|
|
136
|
+
dereferencedAPI = await SwaggerParser.dereference(api);
|
|
137
|
+
dereferencedAPI = await deno_openapi_dereferencer.flattenAllOf(dereferencedAPI);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("error in dereferencing or flattening:", error);
|
|
140
|
+
}
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
// Create service directory
|
|
@@ -154,11 +160,21 @@ async function createDocsForService(yamlFilePath, providerName, serviceName, ser
|
|
|
154
160
|
console.warn(`No 'id' defined for resource: ${resourceName} in service: ${serviceName}`);
|
|
155
161
|
continue;
|
|
156
162
|
}
|
|
157
|
-
|
|
163
|
+
|
|
164
|
+
const resourceDescription = resourceData.description || '';
|
|
165
|
+
|
|
166
|
+
// Determine if it's a View or a Resource
|
|
167
|
+
let resourceType = "Resource"; // Default type
|
|
168
|
+
if (resourceData.config?.views?.select) {
|
|
169
|
+
resourceType = "View";
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
resources.push({
|
|
159
173
|
name: resourceName,
|
|
174
|
+
description: resourceDescription,
|
|
175
|
+
type: resourceType,
|
|
160
176
|
resourceData,
|
|
161
|
-
dereferencedAPI
|
|
177
|
+
dereferencedAPI,
|
|
162
178
|
});
|
|
163
179
|
}
|
|
164
180
|
|
|
@@ -197,9 +213,7 @@ async function processResource(providerName, serviceFolder, serviceName, resourc
|
|
|
197
213
|
const resourceIndexContent = await createResourceIndexContent(
|
|
198
214
|
providerName,
|
|
199
215
|
serviceName,
|
|
200
|
-
resource
|
|
201
|
-
resource.resourceData,
|
|
202
|
-
resource.dereferencedAPI,
|
|
216
|
+
resource,
|
|
203
217
|
);
|
|
204
218
|
fs.writeFileSync(resourceIndexPath, resourceIndexContent);
|
|
205
219
|
|
package/src/docgen/helpers.js
CHANGED
|
@@ -397,28 +397,6 @@ function getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAP
|
|
|
397
397
|
};
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
// function getHttpRespBody(schema, objectKey) {
|
|
401
|
-
|
|
402
|
-
// if (schema.type === 'array') {
|
|
403
|
-
// return {
|
|
404
|
-
// respProps: schema.items.properties || {},
|
|
405
|
-
// respDescription: schema.items.description || '',
|
|
406
|
-
// }
|
|
407
|
-
// } else if (schema.type === 'object') {
|
|
408
|
-
// return {
|
|
409
|
-
// respProps: schema.properties || {},
|
|
410
|
-
// respDescription: schema.description || '',
|
|
411
|
-
// };
|
|
412
|
-
// } else {
|
|
413
|
-
// return {
|
|
414
|
-
// respProps: {},
|
|
415
|
-
// respDescription: '',
|
|
416
|
-
// };
|
|
417
|
-
// }
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// }
|
|
421
|
-
|
|
422
400
|
function getHttpRespBody(schema, objectKey) {
|
|
423
401
|
|
|
424
402
|
if (schema.type === 'array') {
|
|
@@ -435,12 +413,20 @@ function getHttpRespBody(schema, objectKey) {
|
|
|
435
413
|
const parts = objectKey.split('[*]');
|
|
436
414
|
const complexObjectKey = parts[1].replace('.', '');
|
|
437
415
|
console.log(`Item of Interest : ${complexObjectKey}`);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
416
|
+
|
|
417
|
+
// Safe access to respProps
|
|
418
|
+
const respProps = schema?.properties?.items?.additionalProperties?.properties?.[complexObjectKey]?.items?.properties ?? {};
|
|
419
|
+
|
|
420
|
+
// Safe access to respDescription with fallbacks
|
|
421
|
+
const respDescription =
|
|
422
|
+
schema?.properties?.items?.additionalProperties?.properties?.[complexObjectKey]?.items?.description ??
|
|
423
|
+
schema?.properties?.items?.description ??
|
|
424
|
+
'';
|
|
425
|
+
|
|
426
|
+
// console.info(respProps);
|
|
427
|
+
// console.log(respDescription);
|
|
442
428
|
return {
|
|
443
|
-
respProps: respProps
|
|
429
|
+
respProps: respProps,
|
|
444
430
|
respDescription: respDescription,
|
|
445
431
|
};
|
|
446
432
|
|
|
@@ -448,12 +434,20 @@ function getHttpRespBody(schema, objectKey) {
|
|
|
448
434
|
// simple object key
|
|
449
435
|
console.log(`Simple Object Key : ${objectKey}`);
|
|
450
436
|
const simpleObjectKey = objectKey.replace('$.', '');
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
const respProps = (schema?.properties?.[simpleObjectKey]?.items?.properties) ??
|
|
440
|
+
(schema?.properties?.[simpleObjectKey]?.properties) ??
|
|
441
|
+
{};
|
|
442
|
+
|
|
443
|
+
const respDescription = (schema?.properties?.[simpleObjectKey]?.items?.description) ??
|
|
444
|
+
(schema?.description) ??
|
|
445
|
+
'';
|
|
446
|
+
|
|
447
|
+
// console.info(respProps);
|
|
448
|
+
// console.log(respDescription);
|
|
455
449
|
return {
|
|
456
|
-
respProps: respProps
|
|
450
|
+
respProps: respProps,
|
|
457
451
|
respDescription: respDescription,
|
|
458
452
|
};
|
|
459
453
|
}
|
|
@@ -3,48 +3,54 @@ import {
|
|
|
3
3
|
getSqlMethodsWithOrderedFields,
|
|
4
4
|
sanitizeHtml,
|
|
5
5
|
} from '../helpers.js';
|
|
6
|
+
import { docView } from './view.js';
|
|
6
7
|
|
|
7
8
|
const mdCodeAnchor = "`";
|
|
8
9
|
|
|
9
|
-
export function createFieldsSection(resourceData, dereferencedAPI) {
|
|
10
|
+
export function createFieldsSection(resourceType, resourceData, dereferencedAPI) {
|
|
10
11
|
let content = '## Fields\n\n';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// Use the reusable function to get methods with ordered fields
|
|
15
|
-
const methods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'select');
|
|
13
|
+
if(resourceType === 'Resource'){
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Create the tab values array for the Tabs component
|
|
22
|
-
const tabValues = methodNames.map(methodName => {
|
|
23
|
-
return `{ label: '${methodName}', value: '${methodName}' }`;
|
|
24
|
-
}).join(',\n ');
|
|
15
|
+
content += 'The following fields are returned by `SELECT` queries:\n\n';
|
|
16
|
+
|
|
17
|
+
// Use the reusable function to get methods with ordered fields
|
|
18
|
+
const methods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'select');
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
if (Object.keys(methods).length > 0) {
|
|
21
|
+
// Create the tabs and markdown content
|
|
22
|
+
const methodNames = Object.keys(methods);
|
|
23
|
+
|
|
24
|
+
// Create the tab values array for the Tabs component
|
|
25
|
+
const tabValues = methodNames.map(methodName => {
|
|
26
|
+
return `{ label: '${methodName}', value: '${methodName}' }`;
|
|
27
|
+
}).join(',\n ');
|
|
28
|
+
|
|
29
|
+
// Start building the Tabs component
|
|
30
|
+
content += `<Tabs
|
|
28
31
|
defaultValue="${methodNames[0]}"
|
|
29
32
|
values={[
|
|
30
33
|
${tabValues}
|
|
31
34
|
]}
|
|
32
35
|
>\n`;
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// Create the TabItems with table content
|
|
38
|
+
for (const methodName of methodNames) {
|
|
39
|
+
const methodData = methods[methodName];
|
|
40
|
+
|
|
41
|
+
// Start the TabItem
|
|
42
|
+
content += `<TabItem value="${methodName}">\n\n`;
|
|
43
|
+
|
|
44
|
+
// Add the method description if available
|
|
45
|
+
if (methodData.respDescription
|
|
46
|
+
&& methodData.respDescription.trim().toUpperCase() !== 'OK'
|
|
47
|
+
&& methodData.respDescription.trim() !== 'Successful response'
|
|
48
|
+
) {
|
|
49
|
+
content += `${sanitizeHtml(methodData.respDescription)}\n\n`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Add the table header
|
|
53
|
+
content += `<table>
|
|
48
54
|
<thead>
|
|
49
55
|
<tr>
|
|
50
56
|
<th>Name</th>
|
|
@@ -54,28 +60,34 @@ export function createFieldsSection(resourceData, dereferencedAPI) {
|
|
|
54
60
|
</thead>
|
|
55
61
|
<tbody>`;
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
// Add each property as a row in the table
|
|
64
|
+
for (const [propName, propData] of Object.entries(methodData.properties)) {
|
|
65
|
+
content += `\n<tr>
|
|
60
66
|
<td><CopyableCode code="${propName}" /></td>
|
|
61
67
|
<td><code>${propData.type}</code></td>
|
|
62
68
|
<td>${sanitizeHtml(propData.description)}</td>
|
|
63
69
|
</tr>`;
|
|
64
|
-
|
|
70
|
+
}
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
content += `\n</tbody>
|
|
67
73
|
</table>
|
|
68
74
|
`;
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
// Close the TabItem
|
|
77
|
+
content += `</TabItem>\n`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Close the Tabs component
|
|
81
|
+
content += `</Tabs>\n`;
|
|
82
|
+
} else {
|
|
83
|
+
// no fields
|
|
84
|
+
content += `${mdCodeAnchor}SELECT${mdCodeAnchor} not supported for this resource, use ${mdCodeAnchor}SHOW METHODS${mdCodeAnchor} to view available operations for the resource.\n\n`;
|
|
72
85
|
}
|
|
73
|
-
|
|
74
|
-
// Close the Tabs component
|
|
75
|
-
content += `</Tabs>\n`;
|
|
86
|
+
|
|
76
87
|
} else {
|
|
77
|
-
//
|
|
78
|
-
|
|
88
|
+
// its a view
|
|
89
|
+
console.log(`processing view : ${resourceData.name}...`)
|
|
90
|
+
content += docView(resourceData);
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
return content;
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
getIndefiniteArticle,
|
|
4
4
|
} from '../helpers.js';
|
|
5
5
|
|
|
6
|
-
export function createOverviewSection(resourceName, providerName, serviceName) {
|
|
6
|
+
export function createOverviewSection(resourceName, resourceType, resourceDescription, providerName, serviceName) {
|
|
7
7
|
|
|
8
8
|
let content = `---
|
|
9
9
|
title: ${resourceName}
|
|
@@ -25,12 +25,16 @@ import CopyableCode from '@site/src/components/CopyableCode/CopyableCode';
|
|
|
25
25
|
import Tabs from '@theme/Tabs';
|
|
26
26
|
import TabItem from '@theme/TabItem';
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
content += resourceDescription ? resourceDescription : `Creates, updates, deletes, gets or lists ${getIndefiniteArticle(resourceName)} <code>${resourceName}</code> resource.`;
|
|
31
|
+
|
|
32
|
+
content += `
|
|
29
33
|
|
|
30
34
|
## Overview
|
|
31
35
|
<table><tbody>
|
|
32
36
|
<tr><td><b>Name</b></td><td><code>${resourceName}</code></td></tr>
|
|
33
|
-
<tr><td><b>Type</b></td><td
|
|
37
|
+
<tr><td><b>Type</b></td><td>${resourceType}</td></tr>
|
|
34
38
|
<tr><td><b>Id</b></td><td><CopyableCode code="${providerName}.${serviceName}.${resourceName}" /></td></tr>
|
|
35
39
|
</tbody></table>
|
|
36
40
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/docgen/resource/view.js
|
|
2
|
+
export function docView(resourceData) {
|
|
3
|
+
|
|
4
|
+
let content = '';
|
|
5
|
+
|
|
6
|
+
const fields = resourceData.config?.views?.fields ?? [];
|
|
7
|
+
|
|
8
|
+
if (fields.length === 0) {
|
|
9
|
+
content += `See the SQL Definition (view DDL) for fields returned by this view.\n\n`;
|
|
10
|
+
} else {
|
|
11
|
+
// Add the table
|
|
12
|
+
content += `The following fields are returned by this view:\n\n`;
|
|
13
|
+
content += `<table>
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th>Name</th>
|
|
17
|
+
<th>Datatype</th>
|
|
18
|
+
<th>Description</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>`;
|
|
22
|
+
|
|
23
|
+
for (const field of fields) {
|
|
24
|
+
content += `
|
|
25
|
+
<tr>
|
|
26
|
+
<td>${field.name}</td>
|
|
27
|
+
<td>${field.type}</td>
|
|
28
|
+
<td>${field.description}</td>
|
|
29
|
+
</tr>`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Close the table
|
|
33
|
+
content += `
|
|
34
|
+
</tbody>
|
|
35
|
+
</table>\n\n`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Add required params section if exists
|
|
39
|
+
const requiredParams = resourceData.config?.views?.requiredParams ?? [];
|
|
40
|
+
if (requiredParams.length > 0) {
|
|
41
|
+
// add the table
|
|
42
|
+
content += `## Required Parameters\n\n`;
|
|
43
|
+
content += `The following parameters are required by this view:\n\n`;
|
|
44
|
+
content += `<table>
|
|
45
|
+
<thead>
|
|
46
|
+
<tr>
|
|
47
|
+
<th>Name</th>
|
|
48
|
+
<th>Datatype</th>
|
|
49
|
+
<th>Description</th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody>`;
|
|
53
|
+
|
|
54
|
+
for (const param of requiredParams) {
|
|
55
|
+
content += `
|
|
56
|
+
<tr>
|
|
57
|
+
<td>${param.name}</td>
|
|
58
|
+
<td>${param.type}</td>
|
|
59
|
+
<td>${param.description}</td>
|
|
60
|
+
</tr>`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Close the table
|
|
64
|
+
content += `
|
|
65
|
+
</tbody>
|
|
66
|
+
</table>\n\n`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// SQL Definition section
|
|
70
|
+
content += `## SQL Definition\n\n`;
|
|
71
|
+
|
|
72
|
+
// Build array of dialect objects
|
|
73
|
+
const dialects = [];
|
|
74
|
+
let currentSelect = resourceData.config.views.select;
|
|
75
|
+
|
|
76
|
+
// Add primary dialect
|
|
77
|
+
dialects.push({
|
|
78
|
+
name: extractDialectName(currentSelect.predicate),
|
|
79
|
+
ddl: currentSelect.ddl
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Add fallback dialects
|
|
83
|
+
while (currentSelect.fallback) {
|
|
84
|
+
currentSelect = currentSelect.fallback;
|
|
85
|
+
dialects.push({
|
|
86
|
+
name: extractDialectName(currentSelect.predicate),
|
|
87
|
+
ddl: currentSelect.ddl
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Create the tabbed interface
|
|
92
|
+
const tabValues = dialects.map(dialect => (
|
|
93
|
+
`{ label: '${dialect.name}', value: '${dialect.name}' }`
|
|
94
|
+
)).join(',\n');
|
|
95
|
+
|
|
96
|
+
content += `<Tabs
|
|
97
|
+
defaultValue="${dialects[0].name}"
|
|
98
|
+
values={[
|
|
99
|
+
${tabValues}
|
|
100
|
+
]}
|
|
101
|
+
>\n`;
|
|
102
|
+
|
|
103
|
+
// Create tab content
|
|
104
|
+
for (const dialect of dialects) {
|
|
105
|
+
content += `<TabItem value="${dialect.name}">\n\n`;
|
|
106
|
+
content += `\`\`\`sql
|
|
107
|
+
${dialect.ddl}
|
|
108
|
+
\`\`\`\n\n`;
|
|
109
|
+
content += `</TabItem>\n`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
content += `</Tabs>\n`;
|
|
113
|
+
|
|
114
|
+
return content;
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Extract and format dialect name from predicate
|
|
119
|
+
function extractDialectName(predicate) {
|
|
120
|
+
if (!predicate) {
|
|
121
|
+
return 'Default';
|
|
122
|
+
}
|
|
123
|
+
const dialectMatch = predicate.match(/sqlDialect\s*==\s*['"](.*?)['"]/);
|
|
124
|
+
if (!dialectMatch || !dialectMatch[1]) {
|
|
125
|
+
throw new Error(`Invalid dialect predicate: ${predicate}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Capitalize first letter of dialect name
|
|
129
|
+
return dialectMatch[1].charAt(0).toUpperCase() + dialectMatch[1].slice(1);
|
|
130
|
+
}
|
|
@@ -9,16 +9,14 @@ import { createExamplesSection } from './resource/examples.js';
|
|
|
9
9
|
export async function createResourceIndexContent(
|
|
10
10
|
providerName,
|
|
11
11
|
serviceName,
|
|
12
|
-
|
|
13
|
-
resourceData,
|
|
14
|
-
dereferencedAPI,
|
|
12
|
+
resource,
|
|
15
13
|
) {
|
|
16
14
|
// Generate each section of the documentation
|
|
17
|
-
const overviewContent = createOverviewSection(
|
|
18
|
-
const fieldsContent = createFieldsSection(resourceData, dereferencedAPI);
|
|
19
|
-
const methodsContent = createMethodsSection(resourceData, dereferencedAPI);
|
|
20
|
-
const paramsContent = createParamsSection(resourceData, dereferencedAPI);
|
|
21
|
-
const examplesContent = createExamplesSection(providerName, serviceName,
|
|
15
|
+
const overviewContent = createOverviewSection(resource.name, resource.type, resource.description, providerName, serviceName);
|
|
16
|
+
const fieldsContent = createFieldsSection(resource.type, resource.resourceData, resource.dereferencedAPI);
|
|
17
|
+
const methodsContent = resource.type === 'Resource' ? createMethodsSection(resource.resourceData, resource.dereferencedAPI) : '';
|
|
18
|
+
const paramsContent = resource.type === 'Resource' ? createParamsSection(resource.resourceData, resource.dereferencedAPI) : '';
|
|
19
|
+
const examplesContent = resource.type === 'Resource' ? createExamplesSection(providerName, serviceName, resource.name, resource.resourceData, resource.dereferencedAPI) : '';
|
|
22
20
|
|
|
23
21
|
// Combine all sections into the final content
|
|
24
22
|
return `${overviewContent}${fieldsContent}${methodsContent}${paramsContent}${examplesContent}`;
|
package/src/index.js
CHANGED
package/src/logger.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @stackql/provider-utils/src/logger.js
|
|
2
|
+
|
|
3
|
+
// Simple logger implementation
|
|
4
|
+
const logLevels = {
|
|
5
|
+
error: 0,
|
|
6
|
+
warn: 1,
|
|
7
|
+
info: 2,
|
|
8
|
+
debug: 3
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let currentLevel = 'info';
|
|
12
|
+
|
|
13
|
+
const logger = {
|
|
14
|
+
get level() {
|
|
15
|
+
return currentLevel;
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
set level(newLevel) {
|
|
19
|
+
if (logLevels[newLevel] !== undefined) {
|
|
20
|
+
currentLevel = newLevel;
|
|
21
|
+
} else {
|
|
22
|
+
console.warn(`Invalid log level: ${newLevel}. Using 'info' instead.`);
|
|
23
|
+
currentLevel = 'info';
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
error: (message) => {
|
|
28
|
+
if (logLevels[currentLevel] >= logLevels.error) {
|
|
29
|
+
console.error(`ERROR: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
warn: (message) => {
|
|
34
|
+
if (logLevels[currentLevel] >= logLevels.warn) {
|
|
35
|
+
console.warn(`WARNING: ${message}`);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
info: (message) => {
|
|
40
|
+
if (logLevels[currentLevel] >= logLevels.info) {
|
|
41
|
+
console.info(`INFO: ${message}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
debug: (message) => {
|
|
46
|
+
if (logLevels[currentLevel] >= logLevels.debug) {
|
|
47
|
+
console.debug(`DEBUG: ${message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default logger;
|