• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

statuscompliance / status-backend / 20368835235

19 Dec 2025 11:35AM UTC coverage: 91.547% (+0.6%) from 90.985%
20368835235

push

github

alvarobernal2412
test(controllers): increase linker and databinder coverage

1505 of 1729 branches covered (87.04%)

Branch coverage included in aggregate %.

2827 of 3003 relevant lines covered (94.14%)

28.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.35
/src/controllers/databinder.controller.js
1
import { models } from '../models/models.js';
2
import { getDatabinderCatalog } from '../config/databinder.js';
3
import logger from '../config/logger.js';
4
import { 
5
  sanitizeDatasource, 
6
  checkOwnership, 
7
  normalizeName, 
8
  generateInstanceId, 
9
  generateExecutionId,
10
  extractResultData,
11
  validateDatasourceInput, 
12
  validateDatasourceUpdateInput, 
13
  validateDefinitionExists, 
14
  validateDatasourceConfig, 
15
  validateMethodExists,
16
  performPrimaryTest, 
17
  performAdditionalTests, 
18
  determineOverallTestStatus, 
19
  createTestSummary, 
20
  createTestDetails, 
21
  createTestResults,
22
  applyPropertyMapping, 
23
  createMappingMetadata,
24
  generateCorrelationIds, 
25
  createHttpCallDetails, 
26
  createSpanAttributes, 
27
  createCallInfo, 
28
  createResponseMetadata, 
29
  createLogMetadata, 
30
  createTelemetryContext,
31
  getMethodDescription, 
32
  createMethodsInfo 
33
} from '../utils/databinder/index.js';
34

35
// Get the initialized DatasourceCatalog
36
const datasourceCatalog = getDatabinderCatalog();
78✔
37

38
export const listDatasources = async (req, res) => {
78✔
39
  try {
3✔
40
    const userId = req.user?.user_id;
3✔
41
    if (!userId) return res.status(401).json({ message: 'Unauthorized' });
3✔
42

43
    const datasources = await models.Datasource.findAll({
2✔
44
      where: { ownerId: userId },
45
    });
46

47
    const sanitized = datasources.map((ds) => sanitizeDatasource(ds));
1✔
48
    res.json(sanitized);
1✔
49
  } catch (error) {
50
    logger.error('Error getting datasource methods:', error);
1✔
51
    res.status(500).json({ message: 'Error getting datasource methods', error: error.message });
1✔
52
  }
53
};
54

55
export const getDatasource = async (req, res) => {
78✔
56
  try {
3✔
57
    const userId = req.user?.user_id;
3✔
58
    const { id } = req.params;
3✔
59

60
    const datasource = await models.Datasource.findByPk(id);
3✔
61

62
    if (!checkOwnership(datasource, userId)) {
2✔
63
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
64
    }
65

66
    res.json(sanitizeDatasource(datasource, true));
1✔
67
  } catch (error) {
68
    logger.error('Error fetching datasource:', error);
1✔
69
    res.status(500).json({ message: 'Error fetching datasource', error: error.message });
1✔
70
  }
71
};
72

73
export const createDatasource = async (req, res) => {
78✔
74
  try {
7✔
75
    const userId = req.user?.user_id;
7✔
76
    if (!userId) return res.status(401).json({ message: 'Unauthorized' });
7✔
77

78
    const { name, definitionId, config, description, environment } = req.body;
6✔
79

80
    // Validate input
81
    const validation = validateDatasourceInput({ name, definitionId, config });
6✔
82
    if (!validation.isValid) {
6✔
83
      return res.status(400).json({ error: validation.errors.join(', ') });
1✔
84
    }
85

86
    // Validate that the definitionId exists
87
    const availableDefinitions = datasourceCatalog.listDatasourceDefinitions();
5✔
88
    const definitionValidation = validateDefinitionExists(definitionId, availableDefinitions);
5✔
89
    if (!definitionValidation.isValid) {
5✔
90
      return res.status(400).json({ error: definitionValidation.error });
1✔
91
    }
92

93
    const normalizedName = normalizeName(name);
4✔
94

95
    // Check if datasource with same name already exists for this user
96
    const existing = await models.Datasource.findOne({
4✔
97
      where: { name: normalizedName, ownerId: userId }
98
    });
99
    if (existing) {
3✔
100
      return res.status(409).json({ message: 'A datasource with this name already exists.' });
1✔
101
    }
102

103
    // Generate a unique instanceId based on user and name
104
    const instanceId = generateInstanceId(userId, normalizedName);
2✔
105

106
    // Try to create a datasource instance using the catalog to validate config
107
    const configValidation = validateDatasourceConfig(
2✔
108
      datasourceCatalog.createDatasourceInstance.bind(datasourceCatalog),
109
      definitionId,
110
      config,
111
      instanceId
112
    );
113

114
    if (!configValidation.isValid) {
2✔
115
      return res.status(400).json({ 
1✔
116
        message: 'Invalid datasource configuration', 
117
        error: configValidation.error 
118
      });
119
    }
120

121
    // Save to database
122
    const datasourceData = {
1✔
123
      name: normalizedName,
124
      definitionId,
125
      config,
126
      description: description || null,
2✔
127
      environment: environment || 'production',
2✔
128
      isActive: true,
129
      createdBy: req.user.username,
130
      version: 1,
131
      ownerId: userId,
132
    };
133

134
    const newDatasource = await models.Datasource.create(datasourceData);
1✔
135

136
    const response = sanitizeDatasource(newDatasource, true);
1✔
137
    res.status(201).json({ 
1✔
138
      message: 'Datasource created successfully', 
139
      instanceId: configValidation.instance.id,
140
      availableMethods: definitionValidation.definition.availableMethods || Object.keys(configValidation.instance.methods),
2✔
141
      ...response 
142
    });
143
  } catch (error) {
144
    logger.error('Error creating datasource:', error);
1✔
145
    res.status(500).json({ message: 'Error creating datasource', error: error.message });
1✔
146
  }
147
};
148

149
export const updateDatasource = async (req, res) => {
78✔
150
  try {
11✔
151
    const userId = req.user?.user_id;
11✔
152
    const { id } = req.params;
11✔
153
    const { name, definitionId, config, description, environment, isActive } = req.body;
11✔
154

155
    const datasource = await models.Datasource.findByPk(id);
11✔
156
    if (!checkOwnership(datasource, userId)) {
10✔
157
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
158
    }
159

160
    // Validate input
161
    const validation = validateDatasourceUpdateInput({ name, definitionId, config });
9✔
162
    if (!validation.isValid) {
9✔
163
      return res.status(400).json({ error: validation.errors.join(', ') });
1✔
164
    }
165

166
    const updateData = {};
8✔
167

168
    if (name !== undefined) {
8✔
169
      updateData.name = normalizeName(name);
1✔
170
    }
171

172
    if (definitionId !== undefined) {
8✔
173
      updateData.definitionId = definitionId;
1✔
174
    }
175

176
    if (config !== undefined) {
8✔
177
      // Validate new config using the catalog
178
      const testDefinitionId = definitionId || datasource.definitionId;
2✔
179
      const configValidation = validateDatasourceConfig(
2✔
180
        datasourceCatalog.createDatasourceInstance.bind(datasourceCatalog),
181
        testDefinitionId,
182
        config,
183
        `test_${Date.now()}`
184
      );
185

186
      if (!configValidation.isValid) {
2✔
187
        return res.status(400).json({ 
1✔
188
          message: 'Invalid datasource configuration', 
189
          error: configValidation.error 
190
        });
191
      }
192
      
193
      updateData.config = config;
1✔
194
      updateData.version = datasource.version + 1;
1✔
195
      updateData.testStatus = 'not_tested';
1✔
196
    }
197

198
    if (description !== undefined) {
7✔
199
      updateData.description = description;
1✔
200
    }
201

202
    if (environment !== undefined) {
7✔
203
      updateData.environment = environment;
1✔
204
    }
205

206
    if (isActive !== undefined) {
7✔
207
      updateData.isActive = Boolean(isActive);
1✔
208
    }
209

210
    if (Object.keys(updateData).length === 0) {
7✔
211
      return res.status(400).json({ message: 'No valid fields provided for update.' });
1✔
212
    }
213

214
    updateData.updatedAt = new Date();
6✔
215

216
    await datasource.update(updateData);
6✔
217

218
    res.json({ 
6✔
219
      message: 'Datasource updated successfully', 
220
      ...sanitizeDatasource(datasource, true) 
221
    });
222
  } catch (error) {
223
    logger.error('Error updating datasource:', error);
1✔
224
    res.status(500).json({ message: 'Error updating datasource', error: error.message });
1✔
225
  }
226
};
227

228
export const deleteDatasource = async (req, res) => {
78✔
229
  try {
3✔
230
    const userId = req.user?.user_id;
3✔
231
    const { id } = req.params;
3✔
232

233
    const datasource = await models.Datasource.findByPk(id);
3✔
234
    if (!checkOwnership(datasource, userId)) {
2✔
235
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
236
    }
237

238
    await datasource.destroy();
1✔
239
    res.status(204).send();
1✔
240
  } catch (error) {
241
    logger.error('Error deleting datasource:', error);
1✔
242
    res.status(500).json({ message: 'Error deleting datasource', error: error.message });
1✔
243
  }
244
};
245

246
export const testDatasource = async (req, res) => {
78✔
247
  try {
5✔
248
    const userId = req.user?.user_id;
5✔
249
    const { id } = req.params;
5✔
250

251
    const datasource = await models.Datasource.findByPk(id);
5✔
252
    if (!checkOwnership(datasource, userId)) {
4✔
253
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
254
    }
255

256
    // Update test status to pending
257
    await datasource.update({ 
3✔
258
      testStatus: 'pending',
259
      lastTestedAt: new Date()
260
    });
261

262
    try {
3✔
263
      // Create datasource instance and test it
264
      const instance = datasourceCatalog.createDatasourceInstance(
3✔
265
        datasource.definitionId,
266
        datasource.config,
267
        `test_${datasource.id}_${Date.now()}`
268
      );
269

270
      // Get available methods for this datasource type
271
      const availableMethods = Object.keys(instance.methods);
3✔
272

273
      // Perform primary test (pass datasource config for context)
274
      const { primaryTestMethod, testResult: primaryResult } = await performPrimaryTest(instance, datasource.config);
3✔
275

276
      // Perform additional tests (pass datasource config for context)
277
      const additionalResults = await performAdditionalTests(instance, datasource.definitionId, availableMethods, datasource.config);
3✔
278

279
      // Combine all results
280
      const allResults = [primaryResult, ...additionalResults].filter(Boolean);
3✔
281
      const testResults = createTestResults(primaryResult, additionalResults);
3✔
282
      const testDetails = createTestDetails(datasource.definitionId, availableMethods, allResults);
3✔
283

284
      // Determine overall test status
285
      const overallStatus = determineOverallTestStatus(allResults);
3✔
286

287
      await datasource.update({ 
3✔
288
        testStatus: overallStatus,
289
        lastTestedAt: new Date()
290
      });
291

292
      res.json({ 
3✔
293
        message: `Datasource test completed with status: ${overallStatus}`,
294
        testStatus: overallStatus,
295
        testDetails,
296
        testResults,
297
        summary: createTestSummary(allResults, primaryTestMethod)
298
      });
299
    } catch (testError) {
300
      await datasource.update({ 
×
301
        testStatus: 'failure',
302
        lastTestedAt: new Date()
303
      });
304

305
      res.status(400).json({ 
×
306
        message: 'Datasource test failed',
307
        testStatus: 'failure',
308
        error: testError.message,
309
        testDetails: {
310
          datasourceType: datasource.definitionId,
311
          errorDuringSetup: true,
312
          setupError: testError.message
313
        }
314
      });
315
    }
316
  } catch (error) {
317
    logger.error('Error testing datasource:', error);
1✔
318
    res.status(500).json({ message: 'Error testing datasource', error: error.message });
1✔
319
  }
320
};
321

322
export const listAvailableDefinitions = async (req, res) => {
78✔
323
  try {
2✔
324
    const definitions = datasourceCatalog.listDatasourceDefinitions();
2✔
325
    
326
    const formattedDefinitions = definitions.map(def => ({
6✔
327
      id: def.id,
328
      name: def.name,
329
      description: def.description,
330
      configSchema: def.configSchema,
331
      availableMethods: def.availableMethods
332
    }));
333

334
    res.json(formattedDefinitions);
2✔
335
  } catch (error) {
336
    logger.error('Error listing datasource definitions:', error);
×
337
    res.status(500).json({ message: 'Error listing datasource definitions', error: error.message });
×
338
  }
339
};
340

341
export const fetchFromDatasource = async (req, res) => {
78✔
342
  // Generate trace-like IDs for correlation
343
  const { traceId, spanId } = generateCorrelationIds();
7✔
344
  
345
  try {
7✔
346
    const userId = req.user?.user_id;
7✔
347
    const { id } = req.params;
7✔
348
    const { methodName = 'default', options = {}, propertyMapping = null } = req.body;
7✔
349

350
    const datasource = await models.Datasource.findByPk(id);
7✔
351
    if (!checkOwnership(datasource, userId)) {
6✔
352
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
353
    }
354

355
    // Capture call start time and generate execution ID
356
    const callStartTime = new Date();
5✔
357
    const executionId = generateExecutionId(datasource.id, callStartTime.getTime());
5✔
358

359
    // Create datasource instance
360
    const instanceId = generateInstanceId(datasource.id, Date.now(), 'fetch');
5✔
361
    const instance = datasourceCatalog.createDatasourceInstance(
5✔
362
      datasource.definitionId,
363
      datasource.config,
364
      instanceId
365
    );
366

367
    // Validate method exists
368
    const methodValidation = validateMethodExists(instance, methodName);
5✔
369
    if (!methodValidation.isValid) {
5✔
370
      return res.status(400).json({ 
1✔
371
        message: methodValidation.error,
372
        availableMethods: methodValidation.availableMethods
373
      });
374
    }
375

376
    // Determine the actual HTTP method and endpoint based on datasource type and config
377
    const httpCallDetails = createHttpCallDetails(datasource.definitionId, datasource.config, options);
4✔
378

379
    logger.debug(`[${executionId}] Starting datasource fetch`, {
4✔
380
      datasourceId: datasource.id,
381
      methodName,
382
      datasourceType: datasource.definitionId,
383
      httpCallDetails,
384
      userId,
385
      requestId: req.requestId || 'unknown',
8✔
386
      traceId,
387
      spanId,
388
      propertyMappingProvided: !!propertyMapping,
389
      mappingRules: propertyMapping || null,
7✔
390
      // Telemetry-style metadata
391
      'databinder.datasource': datasource.definitionId,
392
      'databinder.datasource.id': datasource.id,
393
      'databinder.method': methodName
394
    });
395

396
    // Execute the method and capture timing
397
    const result = await instance.methods[methodName](options);
4✔
398
    const callEndTime = new Date();
×
399
    const executionDuration = callEndTime.getTime() - callStartTime.getTime();
×
400

401
    // Extract data from nested structure if it exists
402
    const extractedResult = extractResultData(result);
×
403

404
    // Apply property mapping if provided
405
    const finalResult = propertyMapping ? applyPropertyMapping(extractedResult, propertyMapping) : extractedResult;
×
406

407
    logger.debug(`[${executionId}] Datasource fetch completed successfully`, {
×
408
      executionDuration: `${executionDuration}ms`,
409
      resultSize: JSON.stringify(finalResult).length,
410
      datasourceId: datasource.id,
411
      traceId,
412
      spanId,
413
      propertyMappingApplied: !!propertyMapping,
414
      dataExtracted: result !== extractedResult,
415
      originalStructureHadData: result && typeof result === 'object' && result.data !== undefined
×
416
    });
417

418
    // Create span attributes
419
    const spanAttributes = createSpanAttributes({
×
420
      httpCallDetails,
421
      datasource,
422
      methodName
423
    });
424

425
    // Create call info
426
    const callInfo = createCallInfo({
×
427
      executionId,
428
      executionDuration: `${executionDuration}ms`,
429
      callStartTime,
430
      callEndTime,
431
      httpCallDetails,
432
      requestId: req.requestId || 'unknown',
×
433
      traceId,
434
      spanId,
435
      spanAttributes
436
    });
437

438
    // Create telemetry context
439
    const telemetryContext = createTelemetryContext({
×
440
      traceId,
441
      spanId,
442
      operationName: `databinder.fetch.${datasource.definitionId}.${methodName}`,
443
      correlationId: executionId
444
    });
445

446
    // Create log metadata
447
    const logMetadata = createLogMetadata({
×
448
      datasourceId: datasource.id,
449
      methodName,
450
      userId,
451
      operationType: 'fetch',
452
      success: true
453
    });
454

455
    // Create mapping metadata
456
    const mappingMetadata = createMappingMetadata(result, finalResult, propertyMapping);
×
457

458
    // Create response metadata
459
    const metadata = createResponseMetadata({
×
460
      datasource,
461
      instanceId,
462
      finalResult,
463
      originalResult: result,
464
      extractedResult,
465
      propertyMapping: mappingMetadata,
466
      logMetadata,
467
      telemetryContext
468
    });
469

470
    res.json({
×
471
      message: 'Data fetched successfully',
472
      datasourceId: datasource.id,
473
      datasourceName: datasource.name,
474
      methodUsed: methodName,
475
      result: finalResult,
476
      callInfo,
477
      metadata
478
    });
479

480
  } catch (error) {
481
    const callEndTime = new Date();
5✔
482
    logger.error('Error fetching from datasource:', error);
5✔
483
    
484
    // Log the error with call context if available
485
    const errorContext = {
5✔
486
      datasourceId: req.params.id,
487
      methodName: req.body?.methodName || 'default',
10✔
488
      error: error.message,
489
      requestId: req.requestId || 'unknown',
10✔
490
      traceId,
491
      spanId
492
    };
493

494
    // Create error span attributes
495
    const spanAttributes = createSpanAttributes({
5✔
496
      httpCallDetails: null,
497
      datasource: { definitionId: req.body?.datasourceType || 'unknown' },
10✔
498
      methodName: req.body?.methodName || 'default',
10✔
499
      error: error.message
500
    });
501

502
    // Create error call info
503
    const callInfo = createCallInfo({
5✔
504
      executionId: `error_${Date.now()}`,
505
      executionDuration: '0ms',
506
      callStartTime: callEndTime,
507
      callEndTime,
508
      httpCallDetails: null,
509
      requestId: req.requestId || 'unknown',
10✔
510
      traceId,
511
      spanId,
512
      spanAttributes,
513
      failed: true,
514
      errorContext
515
    });
516

517
    // Create error telemetry context
518
    const telemetryContext = createTelemetryContext({
5✔
519
      traceId,
520
      spanId,
521
      operationName: 'databinder.fetch.error',
522
      correlationId: `error_${Date.now()}`
523
    });
524

525
    // Create error log metadata
526
    const logMetadata = createLogMetadata({
5✔
527
      datasourceId: req.params.id,
528
      methodName: req.body?.methodName || 'default',
10✔
529
      userId: req.user?.user_id || 'unknown',
5!
530
      operationType: 'fetch',
531
      success: false,
532
      errorMessage: error.message
533
    });
534

535
    res.status(500).json({ 
5✔
536
      message: 'Error fetching from datasource', 
537
      error: error.message,
538
      datasourceId: req.params.id,
539
      callInfo,
540
      metadata: {
541
        timestamp: callEndTime.toISOString(),
542
        logMetadata,
543
        telemetryContext
544
      }
545
    });
546
  }
547
};
548

549
export const getDatasourceMethods = async (req, res) => {
78✔
550
  try {
3✔
551
    const userId = req.user?.user_id;
3✔
552
    const { id } = req.params;
3✔
553

554
    const datasource = await models.Datasource.findByPk(id);
3✔
555
    if (!checkOwnership(datasource, userId)) {
2✔
556
      return res.status(404).json({ message: 'Datasource not found or access denied' });
1✔
557
    }
558

559
    // Create a temporary instance to get available methods
560
    const instance = datasourceCatalog.createDatasourceInstance(
1✔
561
      datasource.definitionId,
562
      datasource.config,
563
      `methods_${datasource.id}_${Date.now()}`
564
    );
565

566
    // Use built-in introspection method
567
    const methods = instance.listMethods ? instance.listMethods() : Object.keys(instance.methods);
1!
568

569
    res.json({
1✔
570
      datasourceId: datasource.id,
571
      datasourceName: datasource.name,
572
      definitionId: datasource.definitionId,
573
      methods
574
    });
575

576
  } catch (error) {
577
    logger.error('Error getting datasource methods:', error);
1✔
578
    res.status(500).json({ 
1✔
579
      message: 'Error getting datasource methods', 
580
      error: error.message 
581
    });
582
  }
583
};
584

585
export const getMethodInfo = async (req, res) => {
78✔
586
  try {
×
587
    const userId = req.user?.user_id;
×
588
    const { id, methodName } = req.params;
×
589

590
    const datasource = await models.Datasource.findByPk(id);
×
591
    if (!checkOwnership(datasource, userId)) {
×
592
      return res.status(404).json({ message: 'Datasource not found or access denied' });
×
593
    }
594

595
    // Create a temporary instance
596
    const instance = datasourceCatalog.createDatasourceInstance(
×
597
      datasource.definitionId,
598
      datasource.config,
599
      `method_info_${datasource.id}_${Date.now()}`
600
    );
601

602
    // Use built-in introspection method if available
603
    let methodInfo;
604
    if (instance.getMethodInfo) {
×
605
      methodInfo = instance.getMethodInfo(methodName);
×
606
    } else {
607
      // Fallback to checking if method exists
608
      if (!instance.methods[methodName]) {
×
609
        return res.status(404).json({ 
×
610
          message: `Method '${methodName}' not found on datasource` 
611
        });
612
      }
613
      methodInfo = {
×
614
        name: methodName,
615
        description: getMethodDescription(
616
          datasourceCatalog.listDatasourceDefinitions.bind(datasourceCatalog),
617
          datasource.definitionId,
618
          methodName
619
        )
620
      };
621
    }
622

623
    if (!methodInfo) {
×
624
      return res.status(404).json({ 
×
625
        message: `Method '${methodName}' not found on datasource` 
626
      });
627
    }
628

629
    // Filter out technical Zod schema details, keep only useful info
630
    const filteredMethodInfo = {
×
631
      name: methodInfo.name,
632
      description: methodInfo.description,
633
      requiredOptions: methodInfo.requiredOptions || [],
×
634
      optionalOptions: methodInfo.optionalOptions || [],
×
635
      examples: methodInfo.examples || []
×
636
    };
637

638
    res.json({
×
639
      datasourceId: datasource.id,
640
      datasourceName: datasource.name,
641
      definitionId: datasource.definitionId,
642
      methodInfo: filteredMethodInfo
643
    });
644

645
  } catch (error) {
646
    logger.error('Error getting method info:', error);
×
647
    res.status(500).json({ 
×
648
      message: 'Error getting method info', 
649
      error: error.message 
650
    });
651
  }
652
};
653

654
export const getAllMethodsInfo = async (req, res) => {
78✔
655
  try {
×
656
    const userId = req.user?.user_id;
×
657
    const { id } = req.params;
×
658

659
    const datasource = await models.Datasource.findByPk(id);
×
660
    if (!checkOwnership(datasource, userId)) {
×
661
      return res.status(404).json({ message: 'Datasource not found or access denied' });
×
662
    }
663

664
    // Create a temporary instance
665
    const instance = datasourceCatalog.createDatasourceInstance(
×
666
      datasource.definitionId,
667
      datasource.config,
668
      `all_methods_info_${datasource.id}_${Date.now()}`
669
    );
670

671
    // Use built-in introspection method if available
672
    let allMethodsInfo;
673
    if (instance.getAllMethodsInfo) {
×
674
      allMethodsInfo = instance.getAllMethodsInfo();
×
675
    } else {
676
      // Fallback to creating info from available methods
677
      allMethodsInfo = createMethodsInfo(instance, (methodName) => 
×
678
        getMethodDescription(
×
679
          datasourceCatalog.listDatasourceDefinitions.bind(datasourceCatalog),
680
          datasource.definitionId,
681
          methodName
682
        )
683
      );
684
    }
685

686
    // Filter out technical Zod schema details from all methods
687
    const filteredMethodsInfo = {};
×
688
    Object.keys(allMethodsInfo).forEach(methodName => {
×
689
      const info = allMethodsInfo[methodName];
×
690
      filteredMethodsInfo[methodName] = {
×
691
        name: info.name,
692
        description: info.description,
693
        requiredOptions: info.requiredOptions || [],
×
694
        optionalOptions: info.optionalOptions || [],
×
695
        examples: info.examples || []
×
696
      };
697
    });
698

699
    res.json({
×
700
      datasourceId: datasource.id,
701
      datasourceName: datasource.name,
702
      definitionId: datasource.definitionId,
703
      methods: filteredMethodsInfo
704
    });
705

706
  } catch (error) {
707
    logger.error('Error getting all methods info:', error);
×
708
    res.status(500).json({ 
×
709
      message: 'Error getting all methods info', 
710
      error: error.message 
711
    });
712
  }
713
};
714

715
export { datasourceCatalog };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc