@stackql/provider-utils 0.3.1 → 0.3.3

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.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Utilities for building StackQL providers from OpenAPI specifications.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import yaml from 'js-yaml';
5
5
  import logger from '../logger.js';
6
6
  import { camelToSnake } from '../utils.js';
7
+ import { createReadStream } from 'fs';
7
8
 
8
9
  /**
9
10
  * Load specification from YAML or JSON file
@@ -105,13 +106,60 @@ export async function analyze(options) {
105
106
  } = options;
106
107
 
107
108
  try {
108
- fs.mkdirSync(outputDir, { recursive: true });
109
+ // In the analyze function
109
110
  const outputPath = path.join(outputDir, 'all_services.csv');
110
-
111
- const writer = fs.createWriteStream(outputPath, { encoding: 'utf8' });
112
-
113
- // Write header
114
- writer.write('filename,path,operationId,formatted_op_id,verb,response_object,tags,formatted_tags,stackql_resource_name,stackql_method_name,stackql_verb\n');
111
+
112
+ // Check if output file already exists
113
+ let fileExists = false;
114
+ if (fs.existsSync(outputPath)) {
115
+ logger.info(`Output file already exists: ${outputPath}`);
116
+ fileExists = true;
117
+ } else if (!fs.existsSync(outputDir)) {
118
+ logger.info(`Output directory does not exist. Creating: ${outputDir}`);
119
+ fs.mkdirSync(outputDir, { recursive: true });
120
+ }
121
+
122
+ // get existing mappings
123
+ const existingMappings = {};
124
+ if (fileExists) {
125
+ try {
126
+ await new Promise((resolve, reject) => {
127
+ createReadStream(outputPath)
128
+ .pipe(csv())
129
+ .on('data', (row) => {
130
+ if (row.operationId) {
131
+ const key = `${row.filename}::${row.operationId}`;
132
+ existingMappings[key] = {
133
+ resourceName: row.stackql_resource_name || '',
134
+ methodName: row.stackql_method_name || '',
135
+ sqlVerb: row.stackql_verb || ''
136
+ };
137
+ }
138
+ })
139
+ .on('end', () => {
140
+ logger.info(`Loaded ${Object.keys(existingMappings).length} mappings from existing CSV`);
141
+ resolve();
142
+ })
143
+ .on('error', (error) => {
144
+ logger.error(`Failed to load existing CSV: ${error.message}`);
145
+ reject(error);
146
+ });
147
+ });
148
+ } catch (error) {
149
+ logger.error(`Error processing CSV: ${error.message}`);
150
+ }
151
+ }
152
+
153
+ // Create write stream - append if file exists
154
+ const writer = fs.createWriteStream(outputPath, {
155
+ encoding: 'utf8',
156
+ flags: fileExists ? 'a' : 'w' // Use 'a' for append if file exists, 'w' for write if new
157
+ });
158
+
159
+ // Only write header if creating a new file
160
+ if (!fileExists) {
161
+ writer.write('filename,path,operationId,formatted_op_id,verb,response_object,tags,formatted_tags,stackql_resource_name,stackql_method_name,stackql_verb\n');
162
+ }
115
163
 
116
164
  const files = fs.readdirSync(inputDir);
117
165
 
@@ -129,7 +177,20 @@ export async function analyze(options) {
129
177
  continue;
130
178
  }
131
179
 
180
+ // Then in the operation processing loop:
132
181
  const operationId = operation.operationId || '';
182
+ // Check if operation is already mapped in CSV
183
+ const mappingKey = `${filename}::${operationId}`;
184
+ if (operationId && existingMappings[mappingKey]) {
185
+ const mapping = existingMappings[mappingKey];
186
+ if (mapping.resourceName && mapping.methodName && mapping.sqlVerb) {
187
+ logger.info(`Skipping already mapped operation: ${mappingKey} (${mapping.resourceName}.${mapping.methodName} - ${mapping.sqlVerb})`);
188
+ continue; // Skip to next operation
189
+ } else {
190
+ logger.warn(`Operation ${mappingKey} found in CSV but has incomplete mapping`);
191
+ }
192
+ }
193
+
133
194
  // Format operationId as snake_case
134
195
  const formattedOpId = operationId ? camelToSnake(operationId) : '';
135
196