@stackql/provider-utils 0.5.6 → 0.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackql/provider-utils",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Utilities for building StackQL providers from OpenAPI specifications.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -291,6 +291,7 @@ export async function generateDocsv2(options) {
291
291
  outputDir, // e.g., 'website'
292
292
  providerDataDir, // e.g., 'config/provider-data'
293
293
  dereferenced = false,
294
+ succinct = false, // use summary instead of description for method/example descriptions
294
295
  } = options;
295
296
 
296
297
  console.log(`documenting ${providerName} (v2)...`);
@@ -338,7 +339,7 @@ export async function generateDocsv2(options) {
338
339
  const filePath = path.join(serviceDir, file);
339
340
  totalServicesCount++;
340
341
  const serviceFolder = `${servicesDir}/${serviceName}`;
341
- await createDocsForServicev2(filePath, providerName, serviceName, serviceFolder, dereferenced);
342
+ await createDocsForServicev2(filePath, providerName, serviceName, serviceFolder, dereferenced, succinct);
342
343
  }
343
344
 
344
345
  console.log(`Processed ${totalServicesCount} services`);
@@ -394,7 +395,7 @@ ${servicesToMarkdown(providerName, secondColumnServices)}
394
395
  }
395
396
 
396
397
  // v2 service processing - uses SchemaTable for collapsible nested fields
397
- async function createDocsForServicev2(yamlFilePath, providerName, serviceName, serviceFolder, dereferenced = false) {
398
+ async function createDocsForServicev2(yamlFilePath, providerName, serviceName, serviceFolder, dereferenced = false, succinct = false) {
398
399
 
399
400
  const data = yaml.load(fs.readFileSync(yamlFilePath, 'utf8'));
400
401
 
@@ -467,18 +468,18 @@ async function createDocsForServicev2(yamlFilePath, providerName, serviceName, s
467
468
 
468
469
  // Process each resource in first column
469
470
  for (const resource of firstColumn) {
470
- await processResourcev2(providerName, serviceFolder, serviceName, resource);
471
+ await processResourcev2(providerName, serviceFolder, serviceName, resource, succinct);
471
472
  }
472
473
 
473
474
  // Process each resource in second column
474
475
  for (const resource of secondColumn) {
475
- await processResourcev2(providerName, serviceFolder, serviceName, resource);
476
+ await processResourcev2(providerName, serviceFolder, serviceName, resource, succinct);
476
477
  }
477
478
 
478
479
  console.log(`Generated documentation (v2) for ${serviceName}`);
479
480
  }
480
481
 
481
- async function processResourcev2(providerName, serviceFolder, serviceName, resource) {
482
+ async function processResourcev2(providerName, serviceFolder, serviceName, resource, succinct = false) {
482
483
  console.log(`Processing resource (v2): ${resource.name}`);
483
484
 
484
485
  const resourceFolder = path.join(serviceFolder, resource.name);
@@ -491,6 +492,7 @@ async function processResourcev2(providerName, serviceFolder, serviceName, resou
491
492
  providerName,
492
493
  serviceName,
493
494
  resource,
495
+ succinct,
494
496
  );
495
497
  fs.writeFileSync(resourceIndexPath, resourceIndexContent);
496
498
 
@@ -134,7 +134,7 @@ export function getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, sq
134
134
  }
135
135
 
136
136
  // Get response and params using the same function as for SQL verbs
137
- const { respProps, respDescription, opDescription, requestBody } = getHttpOperationInfo(
137
+ const { respProps, respDescription, opDescription, opSummary, requestBody } = getHttpOperationInfo(
138
138
  dereferencedAPI,
139
139
  resolvedPath,
140
140
  resolvedVerb,
@@ -152,12 +152,14 @@ export function getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, sq
152
152
  // Initialize the method with the same structure as SQL methods
153
153
  methods[methodName] = {
154
154
  opDescription,
155
+ opSummary,
155
156
  respDescription,
156
157
  properties: {},
157
158
  requiredParams: requiredParams || {},
158
159
  optionalParams: optionalParams || {},
159
160
  requestBody: requestBody || {},
160
161
  rawRespProps: respProps,
162
+ methodConfig: methodData.config || null,
161
163
  };
162
164
 
163
165
  // Format and sort the properties using our helper functions
@@ -176,19 +178,21 @@ export function getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, sq
176
178
  }
177
179
 
178
180
  for (const thisMethod of resourceData.sqlVerbs[sqlVerb]) {
179
- const {path, httpVerb, mediaType, openAPIDocKey, objectKey, methodName} = getHttpOperationForSqlVerb(thisMethod.$ref, resourceData);
180
- const {respProps, respDescription, opDescription, requestBody} = getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAPIDocKey, objectKey);
181
+ const {path, httpVerb, mediaType, openAPIDocKey, objectKey, methodName, methodConfig} = getHttpOperationForSqlVerb(thisMethod.$ref, resourceData);
182
+ const {respProps, respDescription, opDescription, opSummary, requestBody} = getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAPIDocKey, objectKey);
181
183
  const {requiredParams, optionalParams} = getHttpOperationParams(dereferencedAPI, path, httpVerb);
182
184
 
183
185
  // Initialize the method object with description and params
184
186
  methods[methodName] = {
185
187
  opDescription,
188
+ opSummary,
186
189
  respDescription,
187
190
  properties: {},
188
191
  requiredParams: requiredParams || {},
189
192
  optionalParams: optionalParams || {},
190
193
  requestBody: requestBody || {},
191
194
  rawRespProps: respProps,
195
+ methodConfig: methodConfig || null,
192
196
  };
193
197
 
194
198
  // Format and sort the properties using our helper functions
@@ -253,12 +257,19 @@ function formatProperties(respProps) {
253
257
  // Get the base description
254
258
  let fullDescription = propDetails.description || '';
255
259
  fullDescription = fullDescription.replace(/\n/g, ' ');
260
+
261
+ // Collect enum values from enum or x-enum keys
262
+ const enumValues = propDetails['enum'] || propDetails['x-enum'];
263
+ if (Array.isArray(enumValues) && enumValues.length > 0) {
264
+ fullDescription += ` (${enumValues.join(', ')})`;
265
+ }
266
+
256
267
  let additionalDescriptionPaths = [];
257
268
 
258
269
  // Add all other fields to description parts
259
270
  for (const [fieldName, fieldValue] of Object.entries(propDetails)) {
260
271
  // Skip the fields we're handling separately
261
- if (fieldName === 'type' || fieldName === 'format' || fieldName === 'description') {
272
+ if (fieldName === 'type' || fieldName === 'format' || fieldName === 'description' || fieldName === 'enum' || fieldName === 'x-enum') {
262
273
  continue;
263
274
  }
264
275
 
@@ -360,13 +371,14 @@ function getHttpOperationForSqlVerb(sqlVerbRef, resourceData){
360
371
  const httpVerb = operationRef.split('/').pop()
361
372
  const path = operationRef.split('/')[0].replaceAll('~1','/');
362
373
 
363
- return {
364
- path,
365
- httpVerb,
366
- mediaType: methodObj.response.mediaType,
374
+ return {
375
+ path,
376
+ httpVerb,
377
+ mediaType: methodObj.response.mediaType,
367
378
  openAPIDocKey: methodObj.response.openAPIDocKey,
368
379
  objectKey: methodObj.response.objectKey || false,
369
- methodName
380
+ methodName,
381
+ methodConfig: methodObj.config || null
370
382
  }
371
383
  }
372
384
 
@@ -383,8 +395,9 @@ function getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAP
383
395
  throw new Error(`HTTP verb '${httpVerb}' not found for path '${path}'`);
384
396
  }
385
397
 
386
- // Get operation description
398
+ // Get operation description and summary
387
399
  const opDescription = (dereferencedAPI.paths[path][httpVerb].description || '');
400
+ const opSummary = (dereferencedAPI.paths[path][httpVerb].summary || '');
388
401
 
389
402
  // Extract request body if it exists
390
403
  let requestBody = {};
@@ -474,19 +487,21 @@ function getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAP
474
487
  respProps: {},
475
488
  respDescription: '',
476
489
  opDescription,
490
+ opSummary,
477
491
  requestBody
478
492
  };
479
493
  }
480
-
494
+
481
495
  // Check if there's a content section with the mediaType
482
496
  const responseObj = dereferencedAPI.paths[path][httpVerb].responses[openAPIDocKey];
483
-
497
+
484
498
  // If no content or no mediaType in the response, return empty properties
485
499
  if (!responseObj.content || !mediaType || !responseObj.content[mediaType] || !responseObj.content[mediaType].schema) {
486
500
  return {
487
501
  respProps: {},
488
502
  respDescription: responseObj.description || '',
489
503
  opDescription,
504
+ opSummary,
490
505
  requestBody
491
506
  };
492
507
  }
@@ -499,6 +514,7 @@ function getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAP
499
514
  respProps,
500
515
  respDescription: responseObj.description ? responseObj.description : respDescription,
501
516
  opDescription,
517
+ opSummary,
502
518
  requestBody
503
519
  };
504
520
  }
@@ -589,6 +605,12 @@ export function generateSchemaJsonFromProps(respProps, depth = 0, maxDepth = 4)
589
605
  let propType = prop.type || 'object';
590
606
  let propDesc = sanitizeHtml(prop.description || '');
591
607
 
608
+ // Collect enum values from enum or x-enum keys
609
+ const enumValues = prop['enum'] || prop['x-enum'];
610
+ if (Array.isArray(enumValues) && enumValues.length > 0) {
611
+ propDesc += ` (${enumValues.join(', ')})`;
612
+ }
613
+
592
614
  // Add format info to type string if available
593
615
  if (prop.format) {
594
616
  propType += ` (${prop.format})`;
@@ -4,7 +4,7 @@ import {
4
4
  sanitizeHtml,
5
5
  } from '../../helpers.js';
6
6
 
7
- export function createDeleteExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
7
+ export function createDeleteExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct = false) {
8
8
  const deleteMethods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'delete');
9
9
 
10
10
  // if there are no delete methods, return empty content
@@ -30,7 +30,7 @@ export function createDeleteExamples(providerName, serviceName, resourceName, re
30
30
  content += '<TabItem value="' + methodName + '">\n\n';
31
31
 
32
32
  // Add method description
33
- const opDescription = methodDetails.opDescription || 'No description available.';
33
+ const opDescription = (succinct && methodDetails.opSummary) ? methodDetails.opSummary : (methodDetails.opDescription || 'No description available.');
34
34
  content += sanitizeHtml(opDescription);
35
35
 
36
36
  // Create SQL example
@@ -4,7 +4,7 @@ import {
4
4
  sanitizeHtml
5
5
  } from '../../helpers.js';
6
6
 
7
- export function createExecExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
7
+ export function createExecExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct = false) {
8
8
  const execMethods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'exec');
9
9
 
10
10
  // if there are no exec methods, return empty content
@@ -30,7 +30,7 @@ export function createExecExamples(providerName, serviceName, resourceName, reso
30
30
  content += '<TabItem value="' + methodName + '">\n\n';
31
31
 
32
32
  // Add method description
33
- const opDescription = methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
33
+ const opDescription = (succinct && methodDetails.opSummary) ? methodDetails.opSummary : (methodDetails.opDescription || methodDetails.respDescription || 'No description available.');
34
34
  content += sanitizeHtml(opDescription);
35
35
 
36
36
  // Create SQL example
@@ -4,7 +4,7 @@ import {
4
4
  sanitizeHtml
5
5
  } from '../../helpers.js';
6
6
 
7
- export function createInsertExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
7
+ export function createInsertExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct = false) {
8
8
  const insertMethods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'insert');
9
9
 
10
10
  // if there are no insert methods, return empty content
@@ -34,23 +34,27 @@ export function createInsertExamples(providerName, serviceName, resourceName, re
34
34
  content += '<TabItem value="' + methodName + '">\n\n';
35
35
 
36
36
  // Add method description
37
- const opDescription = methodDetails.opDescription || 'No description available.';
37
+ const opDescription = (succinct && methodDetails.opSummary) ? methodDetails.opSummary : (methodDetails.opDescription || 'No description available.');
38
38
  content += sanitizeHtml(opDescription);
39
- // content += methodDetails.opDescription || 'No description available.';
40
39
 
41
40
  // Create SQL example
42
41
  content += '\n\n```sql\nINSERT INTO ' + providerName + '.' + serviceName + '.' + resourceName + ' (\n';
43
42
 
44
- // Add requestBody fields prefixed with data__ (excluding read-only props)
45
- const reqBodyProps = methodDetails.requestBody?.properties
43
+ // Add requestBody fields (excluding read-only props)
44
+ // If requestBodyTranslate.algorithm is 'naive', do not prefix with data__
45
+ const hasNaiveTranslate = methodDetails.methodConfig?.requestBodyTranslate?.algorithm === 'naive';
46
+
47
+ const reqBodyProps = methodDetails.requestBody?.properties
46
48
  ? Object.entries(methodDetails.requestBody.properties)
47
49
  .filter(([_, propDetails]) => propDetails.readOnly !== true)
48
50
  .map(([propName]) => propName)
49
51
  : [];
50
52
 
51
53
  const requiredBodyProps = methodDetails.requestBody?.required ? methodDetails.requestBody.required : [];
52
-
53
- const dataProps = reqBodyProps.map(prop => 'data__' + prop);
54
+
55
+ const dataProps = hasNaiveTranslate
56
+ ? reqBodyProps
57
+ : reqBodyProps.map(prop => 'data__' + prop);
54
58
 
55
59
  // Combine data props with params
56
60
  const requiredParams = Object.keys(methodDetails.requiredParams || {});
@@ -63,11 +67,18 @@ export function createInsertExamples(providerName, serviceName, resourceName, re
63
67
  // Start SELECT statement
64
68
  content += '\n)\nSELECT \n';
65
69
 
70
+ // Build a set of body prop names for quick lookup
71
+ const bodyPropSet = new Set(reqBodyProps);
72
+
66
73
  // Add values placeholders
67
74
  const valueLines = allFields.map(field => {
68
- const isDataField = field.startsWith('data__');
69
- const paramName = isDataField ? field.substring(6) : field;
70
-
75
+ const isDataField = hasNaiveTranslate
76
+ ? bodyPropSet.has(field)
77
+ : field.startsWith('data__');
78
+ const paramName = isDataField
79
+ ? (hasNaiveTranslate ? field : field.substring(6))
80
+ : field;
81
+
71
82
  // Check for required body props
72
83
  let isRequiredBodyParam = false;
73
84
  if(isDataField){
@@ -79,13 +90,13 @@ export function createInsertExamples(providerName, serviceName, resourceName, re
79
90
  // Check if it's a number or boolean type
80
91
  let isNumber = false;
81
92
  let isBoolean = false;
82
-
93
+
83
94
  if (isDataField && methodDetails.requestBody?.properties?.[paramName]) {
84
95
  const propType = methodDetails.requestBody.properties[paramName].type;
85
96
  isNumber = propType === 'number' || propType === 'integer';
86
97
  isBoolean = propType === 'boolean';
87
98
  }
88
-
99
+
89
100
  if (isNumber || isBoolean) {
90
101
  return '{{ ' + paramName + ' }}' + (isRequiredBodyParam ? ' /* required */' : '');
91
102
  } else {
@@ -4,7 +4,7 @@ import {
4
4
  sanitizeHtml
5
5
  } from '../../helpers.js';
6
6
 
7
- export function createSelectExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
7
+ export function createSelectExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct = false) {
8
8
  const selectMethods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, 'select');
9
9
 
10
10
  // if there are no select methods, return empty content
@@ -30,7 +30,7 @@ export function createSelectExamples(providerName, serviceName, resourceName, re
30
30
  content += '<TabItem value="' + methodName + '">\n\n';
31
31
  // content += methodDetails.opDescription || 'No description available.';
32
32
  // Add method description
33
- const opDescription = methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
33
+ const opDescription = (succinct && methodDetails.opSummary) ? methodDetails.opSummary : (methodDetails.opDescription || methodDetails.respDescription || 'No description available.');
34
34
  content += sanitizeHtml(opDescription);
35
35
 
36
36
  // Create SQL example
@@ -4,7 +4,7 @@ import {
4
4
  sanitizeHtml
5
5
  } from '../../helpers.js';
6
6
 
7
- export function createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, isReplace = false) {
7
+ export function createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, isReplace = false, succinct = false) {
8
8
  const updateMethods = getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, isReplace ? 'replace' : 'update');
9
9
 
10
10
  // if there are no update methods, return empty content
@@ -34,105 +34,108 @@ export function createUpdateExamples(providerName, serviceName, resourceName, re
34
34
  Object.entries(updateMethods).forEach(([methodName, methodDetails]) => {
35
35
  content += '<TabItem value="' + methodName + '">\n\n';
36
36
 
37
- // // Add method description
38
- // content += methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
39
37
  // Add method description
40
- const opDescription = methodDetails.opDescription || 'No description available.';
38
+ const opDescription = (succinct && methodDetails.opSummary) ? methodDetails.opSummary : (methodDetails.opDescription || 'No description available.');
41
39
  content += sanitizeHtml(opDescription);
42
40
 
43
41
  // Create SQL example
44
42
  content += '\n\n```sql\n';
45
43
  content += (isReplace ? 'REPLACE ' : 'UPDATE ') + providerName + '.' + serviceName + '.' + resourceName;
46
44
 
45
+ // If requestBodyTranslate.algorithm is 'naive', do not prefix with data__
46
+ const hasNaiveTranslate = methodDetails.methodConfig?.requestBodyTranslate?.algorithm === 'naive';
47
+
47
48
  // Add SET clause with requestBody fields (excluding read-only props)
48
49
  content += '\nSET \n';
49
-
50
+
50
51
  // Get request body fields (excluding read-only props)
51
- const reqBodyProps = methodDetails.requestBody?.properties
52
+ const reqBodyProps = methodDetails.requestBody?.properties
52
53
  ? Object.entries(methodDetails.requestBody.properties)
53
54
  .filter(([_, propDetails]) => propDetails.readOnly !== true)
54
55
  .map(([propName]) => propName)
55
56
  : [];
56
-
57
- // Add data__ prefixed fields to SET clause
57
+
58
+ // Add fields to SET clause (with or without data__ prefix)
58
59
  if (reqBodyProps.length > 0) {
59
60
  const setLines = reqBodyProps.map(prop => {
60
61
  const propDetails = methodDetails.requestBody.properties[prop];
61
62
  const isNumber = propDetails.type === 'number' || propDetails.type === 'integer';
62
63
  const isBoolean = propDetails.type === 'boolean';
63
-
64
+ const fieldName = hasNaiveTranslate ? prop : 'data__' + prop;
65
+
64
66
  if (isNumber || isBoolean) {
65
- return 'data__' + prop + ' = {{ ' + prop + ' }}';
67
+ return fieldName + ' = {{ ' + prop + ' }}';
66
68
  } else {
67
- return 'data__' + prop + ' = \'{{ ' + prop + ' }}\'';
69
+ return fieldName + ' = \'{{ ' + prop + ' }}\'';
68
70
  }
69
71
  });
70
-
72
+
71
73
  content += setLines.join(',\n');
72
74
  } else {
73
75
  content += '-- No updatable properties';
74
76
  }
75
-
77
+
76
78
  // Add WHERE clause with parameters
77
79
  const requiredParams = Object.keys(methodDetails.requiredParams || {});
78
80
  const optionalParams = Object.keys(methodDetails.optionalParams || {});
79
-
81
+
80
82
  // Get required body props (excluding read-only props)
81
- const requiredBodyProps = methodDetails.requestBody?.required
82
- ? methodDetails.requestBody.required.filter(prop =>
83
- methodDetails.requestBody.properties[prop] &&
83
+ const requiredBodyProps = methodDetails.requestBody?.required
84
+ ? methodDetails.requestBody.required.filter(prop =>
85
+ methodDetails.requestBody.properties[prop] &&
84
86
  methodDetails.requestBody.properties[prop].readOnly !== true)
85
87
  : [];
86
-
88
+
87
89
  if (requiredParams.length > 0 || requiredBodyProps.length > 0 || optionalParams.length > 0) {
88
90
 
89
91
  content += '\nWHERE \n';
90
-
92
+
91
93
  // Add required parameters
92
94
  let clauseCount = 0;
93
-
95
+
94
96
  // Add required query/path/header params
95
97
  requiredParams.forEach(param => {
96
98
  if (clauseCount > 0) content += '\nAND ';
97
99
  content += param + ' = \'{{ ' + param + ' }}\' --required';
98
100
  clauseCount++;
99
101
  });
100
-
102
+
101
103
  // Add required body params (only non-readonly ones)
102
104
  requiredBodyProps.forEach(prop => {
103
105
  if (clauseCount > 0) content += '\nAND ';
104
-
106
+
105
107
  const propDetails = methodDetails.requestBody.properties[prop];
106
108
  const isBoolean = propDetails.type === 'boolean';
107
-
109
+ const fieldName = hasNaiveTranslate ? prop : 'data__' + prop;
110
+
108
111
  if (isBoolean) {
109
- content += 'data__' + prop + ' = {{ ' + prop + ' }} --required';
112
+ content += fieldName + ' = {{ ' + prop + ' }} --required';
110
113
  } else {
111
- content += 'data__' + prop + ' = \'{{ ' + prop + ' }}\' --required';
114
+ content += fieldName + ' = \'{{ ' + prop + ' }}\' --required';
112
115
  }
113
-
116
+
114
117
  clauseCount++;
115
118
  });
116
-
119
+
117
120
  // Add optional parameters
118
121
  optionalParams.forEach(param => {
119
122
  if (clauseCount > 0) content += '\nAND ';
120
-
123
+
121
124
  // For boolean parameters, we can add a comment about their default value
122
125
  const paramDetails = methodDetails.optionalParams[param];
123
126
  const isBoolean = paramDetails.type === 'boolean';
124
127
  const defaultValue = paramDetails.default;
125
-
128
+
126
129
  if (isBoolean) {
127
130
  content += param + ' = {{ ' + param + '}}';
128
131
  } else {
129
132
  content += param + ' = \'{{ ' + param + '}}\'';
130
133
  }
131
-
134
+
132
135
  if (isBoolean && defaultValue !== undefined) {
133
136
  content += ' -- default: ' + defaultValue;
134
137
  }
135
-
138
+
136
139
  clauseCount++;
137
140
  });
138
141
  }
@@ -6,26 +6,26 @@ import { createUpdateExamples } from './examples/update-example.js';
6
6
  import { createDeleteExamples } from './examples/delete-example.js';
7
7
  import { createExecExamples } from './examples/exec-example.js';
8
8
 
9
- export function createExamplesSection(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
9
+ export function createExamplesSection(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct = false) {
10
10
  let content = '';
11
-
11
+
12
12
  // Add SELECT examples
13
- content += createSelectExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI);
13
+ content += createSelectExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct);
14
14
 
15
15
  // Add INSERT examples
16
- content += createInsertExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI);
17
-
16
+ content += createInsertExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct);
17
+
18
18
  // Add UPDATE examples
19
- content += createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, false);
20
-
19
+ content += createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, false, succinct);
20
+
21
21
  // Add REPLACE examples
22
- content += createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, true);
22
+ content += createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, true, succinct);
23
23
 
24
24
  // Add DELETE examples
25
- content += createDeleteExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI);
25
+ content += createDeleteExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct);
26
26
 
27
27
  // Add EXEC examples
28
- content += createExecExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI);
28
+ content += createExecExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, succinct);
29
29
 
30
30
  return content;
31
31
  }
@@ -9,20 +9,23 @@ const getRequiredBodyParams = (methodDetails, accessType) => {
9
9
  if (!['insert', 'update', 'replace', 'exec'].includes(accessType)) {
10
10
  return [];
11
11
  }
12
-
12
+
13
13
  // Get required body params if they exist
14
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)) {
15
+
16
+ // Check if requestBodyTranslate.algorithm is 'naive' - if so, don't prefix with data__
17
+ const hasNaiveTranslate = methodDetails.methodConfig?.requestBodyTranslate?.algorithm === 'naive';
18
+
19
+ // For insert, update, and replace, prefix with data__ (unless naive translate is set)
20
+ if (['insert', 'update', 'replace'].includes(accessType) && !hasNaiveTranslate) {
18
21
  return requiredBodyProps.map(prop => `data__${prop}`);
19
22
  } else {
20
- // For exec, don't prefix
23
+ // For exec or naive translate, don't prefix
21
24
  return requiredBodyProps;
22
25
  }
23
26
  };
24
27
 
25
- export function createMethodsSection(resourceData, dereferencedAPI) {
28
+ export function createMethodsSection(resourceData, dereferencedAPI, succinct = false) {
26
29
 
27
30
  let content = `\n## Methods\n\n`;
28
31
 
@@ -80,7 +83,7 @@ export function createMethodsSection(resourceData, dereferencedAPI) {
80
83
  <td><CopyableCode code="${accessType}" /></td>
81
84
  <td>${requiredParamsStr}</td>
82
85
  <td>${optionalParamsStr}</td>
83
- <td>${sanitizeHtml(methodDetails.opDescription)}</td>
86
+ <td>${sanitizeHtml(succinct && methodDetails.opSummary ? methodDetails.opSummary : methodDetails.opDescription)}</td>
84
87
  </tr>`;
85
88
  }
86
89
  };
@@ -26,13 +26,14 @@ export async function createResourceIndexContentv2(
26
26
  providerName,
27
27
  serviceName,
28
28
  resource,
29
+ succinct = false,
29
30
  ) {
30
31
  // Generate each section of the documentation (v2 uses SchemaTable for fields)
31
32
  const overviewContent = createOverviewSectionv2(resource.name, resource.type, resource.description, providerName, serviceName);
32
33
  const fieldsContent = createFieldsSectionv2(resource.type, resource.resourceData, resource.dereferencedAPI);
33
- const methodsContent = resource.type === 'Resource' ? createMethodsSection(resource.resourceData, resource.dereferencedAPI) : '';
34
+ const methodsContent = resource.type === 'Resource' ? createMethodsSection(resource.resourceData, resource.dereferencedAPI, succinct) : '';
34
35
  const paramsContent = resource.type === 'Resource' ? createParamsSection(resource.resourceData, resource.dereferencedAPI) : '';
35
- const examplesContent = resource.type === 'Resource' ? createExamplesSection(providerName, serviceName, resource.name, resource.resourceData, resource.dereferencedAPI) : '';
36
+ const examplesContent = resource.type === 'Resource' ? createExamplesSection(providerName, serviceName, resource.name, resource.resourceData, resource.dereferencedAPI, succinct) : '';
36
37
 
37
38
  // Combine all sections into the final content
38
39
  return `${overviewContent}${fieldsContent}${methodsContent}${paramsContent}${examplesContent}`;
@@ -177,6 +177,8 @@ export async function generate(options) {
177
177
  providerId,
178
178
  servers = null,
179
179
  providerConfig = null,
180
+ serviceConfig = null,
181
+ naiveReqBodyTranslate = false,
180
182
  skipFiles = []
181
183
  } = options;
182
184
 
@@ -217,8 +219,19 @@ export async function generate(options) {
217
219
  return false;
218
220
  }
219
221
 
222
+ // Parse serviceConfig if provided
223
+ let serviceConfigJson = null;
224
+ if (serviceConfig) {
225
+ try {
226
+ serviceConfigJson = JSON.parse(serviceConfig);
227
+ } catch (error) {
228
+ logger.error(`❌ Failed to parse service config JSON: ${error.message}`);
229
+ return false;
230
+ }
231
+ }
232
+
220
233
  const providerServices = {};
221
-
234
+
222
235
  try {
223
236
  const files = fs.readdirSync(inputDir);
224
237
 
@@ -302,10 +315,22 @@ export async function generate(options) {
302
315
  const pathRef = encodeRefPath(pathKey, verb);
303
316
  const responseInfo = getSuccessResponseInfo(operation);
304
317
 
305
- const methodEntry = {
306
- operation: { $ref: pathRef },
307
- response: responseInfo
308
- };
318
+ const methodEntry = {};
319
+
320
+ // Add requestBodyTranslate config for methods with request bodies
321
+ if (naiveReqBodyTranslate && ['post', 'put', 'patch'].includes(verb)) {
322
+ // Check if the operation actually has a request body
323
+ if (operation.requestBody) {
324
+ methodEntry.config = {
325
+ requestBodyTranslate: {
326
+ algorithm: 'naive'
327
+ }
328
+ };
329
+ }
330
+ }
331
+
332
+ methodEntry.operation = { $ref: pathRef };
333
+ methodEntry.response = responseInfo;
309
334
 
310
335
  // Add objectKey to the response info if it exists in the manifest and is for a GET operation
311
336
  if (entry.stackql_object_key && verb === 'get') {
@@ -344,7 +369,12 @@ export async function generate(options) {
344
369
  return false;
345
370
  }
346
371
  }
347
-
372
+
373
+ // Inject x-stackQL-config if serviceConfig is provided
374
+ if (serviceConfigJson) {
375
+ spec['x-stackQL-config'] = serviceConfigJson;
376
+ }
377
+
348
378
  // Write enriched spec (always as YAML, ensuring .yaml extension)
349
379
  const outputFilename = filename.endsWith('.json')
350
380
  ? filename.replace(/\.json$/, '.yaml')