@onlineapps/conn-orch-api-mapper 1.0.6 → 1.0.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 +2 -2
- package/src/ApiMapper.js +149 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-api-mapper",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "API mapping connector for OA Drive - maps cookbook operations to HTTP endpoints",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"axios": "^1.4.0",
|
|
24
|
-
"@onlineapps/content-resolver": "^1.1.
|
|
24
|
+
"@onlineapps/content-resolver": "^1.1.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"jest": "^29.5.0",
|
package/src/ApiMapper.js
CHANGED
|
@@ -126,6 +126,11 @@ class ApiMapper {
|
|
|
126
126
|
/**
|
|
127
127
|
* Transform HTTP response to cookbook format
|
|
128
128
|
* Normalizes output fields to Content Descriptor based on operations.json output schema
|
|
129
|
+
*
|
|
130
|
+
* Supports two operations.json output formats:
|
|
131
|
+
* 1. Named fields: { "message": { "type": "string" }, "pdf": { "type": "file" } }
|
|
132
|
+
* 2. Direct type: { "type": "file", "description": "..." } (entire output is one value)
|
|
133
|
+
*
|
|
129
134
|
* @method transformResponse
|
|
130
135
|
* @param {Object} httpResponse - HTTP response
|
|
131
136
|
* @param {Object} [operation] - Operation schema from operations.json (for output normalization)
|
|
@@ -136,15 +141,24 @@ class ApiMapper {
|
|
|
136
141
|
// Extract relevant data from HTTP response
|
|
137
142
|
let output = httpResponse.data || httpResponse;
|
|
138
143
|
|
|
144
|
+
// === LOGGING: Input ===
|
|
145
|
+
const logPrefix = '[ApiMapper:transformResponse]';
|
|
146
|
+
console.log(`${logPrefix} INPUT:`, JSON.stringify({
|
|
147
|
+
hasOperation: !!operation,
|
|
148
|
+
operationOutput: operation?.output,
|
|
149
|
+
outputType: typeof output,
|
|
150
|
+
outputKeys: output && typeof output === 'object' ? Object.keys(output) : null,
|
|
151
|
+
output_descriptor: output?._descriptor,
|
|
152
|
+
output_type: output?.type
|
|
153
|
+
}));
|
|
154
|
+
|
|
139
155
|
// If no operation schema, return as-is
|
|
140
156
|
if (!operation || !operation.output) {
|
|
157
|
+
console.log(`${logPrefix} OUTPUT: No operation schema, returning as-is`);
|
|
141
158
|
return output;
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
// Normalize output fields according to operations.json output schema
|
|
145
|
-
// Fields with type: "file" or type: "content" should be normalized to Descriptor
|
|
146
161
|
const outputSchema = operation.output;
|
|
147
|
-
const normalizedOutput = { ...output };
|
|
148
162
|
|
|
149
163
|
// Initialize ContentAccessor if needed (lazy initialization)
|
|
150
164
|
let contentAccessor = null;
|
|
@@ -160,11 +174,115 @@ class ApiMapper {
|
|
|
160
174
|
return contentAccessor;
|
|
161
175
|
};
|
|
162
176
|
|
|
177
|
+
// === DETECT OUTPUT SCHEMA FORMAT ===
|
|
178
|
+
// Format A: Direct type - { type: "file", description: "..." }
|
|
179
|
+
// Format B: Named fields - { "fieldName": { type: "string" }, ... }
|
|
180
|
+
const isDirectType = outputSchema.type &&
|
|
181
|
+
(outputSchema.type === 'file' ||
|
|
182
|
+
outputSchema.type === 'content' ||
|
|
183
|
+
outputSchema.type === 'string' ||
|
|
184
|
+
outputSchema.type === 'object' ||
|
|
185
|
+
outputSchema.type === 'array');
|
|
186
|
+
|
|
187
|
+
console.log(`${logPrefix} Schema format: ${isDirectType ? 'DIRECT_TYPE' : 'NAMED_FIELDS'}`);
|
|
188
|
+
|
|
189
|
+
// === FORMAT A: Direct type (entire output is one value) ===
|
|
190
|
+
if (isDirectType) {
|
|
191
|
+
const schemaType = outputSchema.type;
|
|
192
|
+
|
|
193
|
+
// If schema expects file/content, ensure output is Descriptor
|
|
194
|
+
if (schemaType === 'file' || schemaType === 'content') {
|
|
195
|
+
// Check if output is already a valid Descriptor
|
|
196
|
+
if (output && typeof output === 'object' && output._descriptor === true) {
|
|
197
|
+
console.log(`${logPrefix} OUTPUT: Already Descriptor with _descriptor=true`);
|
|
198
|
+
return output;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check if output looks like Descriptor but missing _descriptor flag
|
|
202
|
+
if (output && typeof output === 'object' && (output.type === 'file' || output.type === 'inline')) {
|
|
203
|
+
console.log(`${logPrefix} Normalizing Descriptor (missing _descriptor flag)`);
|
|
204
|
+
try {
|
|
205
|
+
const accessor = getContentAccessor();
|
|
206
|
+
const normalized = await accessor.normalizeToDescriptor(output, {
|
|
207
|
+
filename: output.filename,
|
|
208
|
+
content_type: output.content_type
|
|
209
|
+
});
|
|
210
|
+
console.log(`${logPrefix} OUTPUT: Normalized Descriptor:`, JSON.stringify({
|
|
211
|
+
_descriptor: normalized._descriptor,
|
|
212
|
+
type: normalized.type,
|
|
213
|
+
storage_ref: normalized.storage_ref,
|
|
214
|
+
filename: normalized.filename
|
|
215
|
+
}));
|
|
216
|
+
return normalized;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error(`${logPrefix} FAIL: Failed to normalize Descriptor: ${error.message}`);
|
|
219
|
+
// FAIL-FAST: Don't silently continue with broken data
|
|
220
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Output is ${schemaType} but failed to normalize: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Output is string (storage reference) - convert to Descriptor
|
|
225
|
+
if (typeof output === 'string') {
|
|
226
|
+
console.log(`${logPrefix} Converting string to Descriptor: ${output.substring(0, 50)}...`);
|
|
227
|
+
try {
|
|
228
|
+
const accessor = getContentAccessor();
|
|
229
|
+
const normalized = await accessor.normalizeToDescriptor(output, {
|
|
230
|
+
filename: outputSchema.filename,
|
|
231
|
+
content_type: outputSchema.content_type
|
|
232
|
+
});
|
|
233
|
+
console.log(`${logPrefix} OUTPUT: String converted to Descriptor`);
|
|
234
|
+
return normalized;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`${logPrefix} FAIL: Failed to convert string to Descriptor: ${error.message}`);
|
|
237
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Output is ${schemaType} but string conversion failed: ${error.message}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Output is object with storage_ref - extract and convert
|
|
242
|
+
if (output && typeof output === 'object' && output.storage_ref) {
|
|
243
|
+
console.log(`${logPrefix} Converting object with storage_ref to Descriptor`);
|
|
244
|
+
try {
|
|
245
|
+
const accessor = getContentAccessor();
|
|
246
|
+
const normalized = await accessor.normalizeToDescriptor(output.storage_ref, {
|
|
247
|
+
filename: output.filename || output.output_name,
|
|
248
|
+
content_type: output.content_type
|
|
249
|
+
});
|
|
250
|
+
console.log(`${logPrefix} OUTPUT: Object converted to Descriptor`);
|
|
251
|
+
return normalized;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error(`${logPrefix} FAIL: Failed to convert object to Descriptor: ${error.message}`);
|
|
254
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Output is ${schemaType} but object conversion failed: ${error.message}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// FAIL-FAST: Schema expects file/content but got unexpected format
|
|
259
|
+
console.error(`${logPrefix} FAIL-FAST: Schema expects ${schemaType} but got unexpected output format:`, {
|
|
260
|
+
outputType: typeof output,
|
|
261
|
+
outputKeys: output && typeof output === 'object' ? Object.keys(output) : null
|
|
262
|
+
});
|
|
263
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Schema expects '${schemaType}' but received unexpected format. Output type: ${typeof output}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// For other direct types (string, object, array), return as-is
|
|
267
|
+
console.log(`${logPrefix} OUTPUT: Direct type '${schemaType}', returning as-is`);
|
|
268
|
+
return output;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// === FORMAT B: Named fields ===
|
|
272
|
+
const normalizedOutput = { ...output };
|
|
273
|
+
|
|
163
274
|
// Process each output field
|
|
164
275
|
for (const [fieldName, fieldSchema] of Object.entries(outputSchema)) {
|
|
276
|
+
// Skip if fieldSchema is not an object (e.g., "type" key from Format A detected wrongly)
|
|
277
|
+
if (!fieldSchema || typeof fieldSchema !== 'object') {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
|
|
165
281
|
const fieldType = fieldSchema.type;
|
|
166
282
|
const fieldValue = output[fieldName];
|
|
167
283
|
|
|
284
|
+
console.log(`${logPrefix} Processing field '${fieldName}': type=${fieldType}, hasValue=${fieldValue !== undefined}`);
|
|
285
|
+
|
|
168
286
|
// Skip if field not present or null
|
|
169
287
|
if (fieldValue === undefined || fieldValue === null) {
|
|
170
288
|
continue;
|
|
@@ -175,42 +293,50 @@ class ApiMapper {
|
|
|
175
293
|
try {
|
|
176
294
|
const accessor = getContentAccessor();
|
|
177
295
|
|
|
178
|
-
// If field is already a Descriptor (has
|
|
179
|
-
if (typeof fieldValue === 'object' && fieldValue !== null &&
|
|
180
|
-
|
|
296
|
+
// If field is already a Descriptor (has _descriptor flag), pass through
|
|
297
|
+
if (typeof fieldValue === 'object' && fieldValue !== null && fieldValue._descriptor === true) {
|
|
298
|
+
console.log(`${logPrefix} Field '${fieldName}' already Descriptor with _descriptor=true`);
|
|
299
|
+
normalizedOutput[fieldName] = fieldValue;
|
|
300
|
+
} else if (typeof fieldValue === 'object' && fieldValue !== null && (fieldValue.type === 'file' || fieldValue.type === 'inline')) {
|
|
301
|
+
// Looks like Descriptor but missing _descriptor flag - normalize
|
|
302
|
+
console.log(`${logPrefix} Field '${fieldName}' normalizing Descriptor (missing _descriptor flag)`);
|
|
181
303
|
normalizedOutput[fieldName] = await accessor.normalizeToDescriptor(fieldValue, {
|
|
182
304
|
filename: fieldValue.filename,
|
|
183
305
|
content_type: fieldValue.content_type
|
|
184
306
|
});
|
|
185
307
|
} else if (typeof fieldValue === 'string') {
|
|
186
308
|
// String value - normalize to Descriptor
|
|
309
|
+
console.log(`${logPrefix} Field '${fieldName}' converting string to Descriptor`);
|
|
187
310
|
normalizedOutput[fieldName] = await accessor.normalizeToDescriptor(fieldValue, {
|
|
188
311
|
filename: fieldSchema.filename,
|
|
189
312
|
content_type: fieldSchema.content_type
|
|
190
313
|
});
|
|
191
|
-
} else if (typeof fieldValue === 'object' && fieldValue !== null) {
|
|
192
|
-
// Object
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
314
|
+
} else if (typeof fieldValue === 'object' && fieldValue !== null && fieldValue.storage_ref) {
|
|
315
|
+
// Object with storage_ref - extract and normalize
|
|
316
|
+
console.log(`${logPrefix} Field '${fieldName}' converting object with storage_ref to Descriptor`);
|
|
317
|
+
normalizedOutput[fieldName] = await accessor.normalizeToDescriptor(fieldValue.storage_ref, {
|
|
318
|
+
filename: fieldValue.filename || fieldValue.output_name,
|
|
319
|
+
content_type: fieldValue.content_type
|
|
320
|
+
});
|
|
321
|
+
} else {
|
|
322
|
+
// FAIL-FAST: Unexpected format for file/content field
|
|
323
|
+
console.error(`${logPrefix} FAIL-FAST: Field '${fieldName}' expects ${fieldType} but got unexpected format:`, {
|
|
324
|
+
valueType: typeof fieldValue,
|
|
325
|
+
valueKeys: fieldValue && typeof fieldValue === 'object' ? Object.keys(fieldValue) : null
|
|
326
|
+
});
|
|
327
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Field '${fieldName}' expects '${fieldType}' but received unexpected format`);
|
|
206
328
|
}
|
|
207
329
|
} catch (error) {
|
|
208
|
-
|
|
209
|
-
|
|
330
|
+
if (error.message.includes('FAIL-FAST')) {
|
|
331
|
+
throw error; // Re-throw fail-fast errors
|
|
332
|
+
}
|
|
333
|
+
console.error(`${logPrefix} FAIL: Failed to normalize field '${fieldName}': ${error.message}`);
|
|
334
|
+
throw new Error(`[ApiMapper] FAIL-FAST: Failed to normalize field '${fieldName}' to Descriptor: ${error.message}`);
|
|
210
335
|
}
|
|
211
336
|
}
|
|
212
337
|
}
|
|
213
338
|
|
|
339
|
+
console.log(`${logPrefix} OUTPUT: Normalized fields:`, JSON.stringify(Object.keys(normalizedOutput)));
|
|
214
340
|
return normalizedOutput;
|
|
215
341
|
}
|
|
216
342
|
|