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

statuscompliance / status-backend / 20269538425

16 Dec 2025 01:26PM UTC coverage: 90.985% (-0.4%) from 91.375%
20269538425

push

github

alvarobernal2412
chore(linker): make datasourceConfigs required field

1470 of 1698 branches covered (86.57%)

Branch coverage included in aggregate %.

40 of 51 new or added lines in 4 files covered. (78.43%)

1 existing line in 1 file now uncovered.

2769 of 2961 relevant lines covered (93.52%)

28.31 hits per line

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

92.94
/src/utils/databinder/linker/linkerValidation.js
1
/**
2
 * Linker validation utilities
3
 */
4

5
/**
6
 * Validate linker input for creation
7
 * @param {Object} input - The input to validate
8
 * @returns {Object} Validation result with isValid and errors
9
 */
10
export const validateLinkerInput = (input) => {
78✔
11
  const errors = [];
13✔
12

13
  if (!input.datasourceIds || !Array.isArray(input.datasourceIds)) {
13✔
14
    errors.push('datasourceIds must be an array');
2✔
15
  } else if (input.datasourceIds.length === 0) {
11✔
16
    errors.push('datasourceIds must contain at least one datasource ID');
2✔
17
  }
18

19
  if (input.defaultMethodName !== undefined && typeof input.defaultMethodName !== 'string') {
13✔
20
    errors.push('defaultMethodName must be a string');
1✔
21
  }
22

23
  // datasourceConfigs is now REQUIRED and must configure all datasources
24
  if (!input.datasourceConfigs || typeof input.datasourceConfigs !== 'object' || Array.isArray(input.datasourceConfigs)) {
13✔
25
    errors.push('datasourceConfigs is required and must be an object');
10✔
26
  } else if (input.datasourceIds && Array.isArray(input.datasourceIds)) {
3!
27
    // Validate that all datasources have configs
28
    for (const dsId of input.datasourceIds) {
3✔
29
      if (!input.datasourceConfigs[dsId]) {
7✔
30
        errors.push(`Missing configuration for datasource '${dsId}' in datasourceConfigs`);
1✔
31
      }
32
    }
33
  }
34

35
  return {
13✔
36
    isValid: errors.length === 0,
37
    errors
38
  };
39
};
40

41
/**
42
 * Validate linker input for updates
43
 * @param {Object} input - The input to validate
44
 * @returns {Object} Validation result with isValid and errors
45
 */
46
export const validateLinkerUpdateInput = (input) => {
78✔
47
  const errors = [];
12✔
48

49
  if (input.datasourceIds !== undefined) {
12✔
50
    if (!Array.isArray(input.datasourceIds)) {
6✔
51
      errors.push('datasourceIds must be an array');
1✔
52
    } else if (input.datasourceIds.length === 0) {
5✔
53
      errors.push('datasourceIds must contain at least one datasource ID');
1✔
54
    }
55
  }
56

57
  if (input.defaultMethodName !== undefined && typeof input.defaultMethodName !== 'string') {
12✔
58
    errors.push('defaultMethodName must be a string');
1✔
59
  }
60

61
  if (input.datasourceConfigs !== undefined && input.datasourceConfigs !== null) {
12✔
62
    if (typeof input.datasourceConfigs !== 'object' || Array.isArray(input.datasourceConfigs)) {
1!
63
      errors.push('datasourceConfigs must be an object');
1✔
64
    }
65
  }
66

67
  return {
12✔
68
    isValid: errors.length === 0,
69
    errors
70
  };
71
};
72

73
/**
74
 * Validate that all datasource IDs exist in the database
75
 * @param {Array<string>} datasourceIds - Array of datasource IDs to validate
76
 * @param {Object} Datasource - Datasource model
77
 * @param {number} userId - User ID to check ownership
78
 * @returns {Promise<Object>} Validation result with isValid, errors, and found datasources
79
 */
80
export const validateDatasourcesExist = async (datasourceIds, Datasource, userId) => {
78✔
81
  try {
7✔
82
    const datasources = await Datasource.findAll({
7✔
83
      where: {
84
        id: datasourceIds,
85
        ownerId: userId
86
      }
87
    });
88

89
    const foundIds = new Set(datasources.map(ds => ds.id));
13✔
90
    const missingIds = datasourceIds.filter(id => !foundIds.has(id));
14✔
91

92
    if (missingIds.length > 0) {
6✔
93
      return {
2✔
94
        isValid: false,
95
        errors: [`The following datasource IDs were not found or you don't have access: ${missingIds.join(', ')}`],
96
        datasources: []
97
      };
98
    }
99

100
    return {
4✔
101
      isValid: true,
102
      errors: [],
103
      datasources
104
    };
105
  } catch (error) {
106
    return {
1✔
107
      isValid: false,
108
      errors: [`Error validating datasources: ${error.message}`],
109
      datasources: []
110
    };
111
  }
112
};
113

114
/**
115
 * Validate a single datasource config
116
 * @param {string} dsId - Datasource ID
117
 * @param {Object} config - Config to validate
118
 * @param {Array<string>} datasourceIds - Array of valid datasource IDs
119
 * @returns {Array<string>} Array of error messages
120
 */
121
const validateSingleDatasourceConfig = (dsId, config, datasourceIds) => {
78✔
122
  const errors = [];
10✔
123
  const datasourceIdSet = new Set(datasourceIds);
10✔
124

125
  if (!datasourceIdSet.has(dsId)) {
10✔
126
    errors.push(`Config provided for datasource '${dsId}' but this ID is not in datasourceIds array`);
1✔
127
  }
128

129
  if (config.id && config.id !== dsId) {
10✔
130
    errors.push(`Config for datasource '${dsId}' has mismatched id field: '${config.id}'`);
1✔
131
  }
132

133
  // methodConfig is now MANDATORY for each datasource
134
  if (!config.methodConfig) {
10!
NEW
135
    errors.push(`methodConfig is required for datasource '${dsId}'`);
×
136
  } else {
137
    const methodConfigErrors = validateMethodConfig(dsId, config.methodConfig);
10✔
138
    errors.push(...methodConfigErrors);
10✔
139
  }
140

141
  if (config.propertyMapping) {
10✔
142
    const propertyMappingErrors = validatePropertyMapping(dsId, config.propertyMapping);
2✔
143
    errors.push(...propertyMappingErrors);
2✔
144
  }
145

146
  return errors;
10✔
147
};
148

149
/**
150
 * Validate method config structure
151
 * @param {string} dsId - Datasource ID
152
 * @param {Object} methodConfig - Method config to validate
153
 * @returns {Array<string>} Array of error messages
154
 */
155
const validateMethodConfig = (dsId, methodConfig) => {
78✔
156
  const errors = [];
10✔
157

158
  if (typeof methodConfig !== 'object') {
10✔
159
    errors.push(`methodConfig for datasource '${dsId}' must be an object`);
1✔
160
    return errors;
1✔
161
  }
162

163
  // methodName is now MANDATORY
164
  if (!methodConfig.methodName) {
9!
NEW
165
    errors.push(`methodConfig.methodName is required for datasource '${dsId}'`);
×
166
  } else if (typeof methodConfig.methodName !== 'string') {
9✔
167
    errors.push(`methodConfig.methodName for datasource '${dsId}' must be a string`);
1✔
168
  }
169

170
  // Validate options if provided
171
  if (methodConfig.options !== undefined && (typeof methodConfig.options !== 'object' || Array.isArray(methodConfig.options))) {
9!
NEW
172
    errors.push(`methodConfig.options for datasource '${dsId}' must be an object`);
×
173
  }
174

175
  return errors;
9✔
176
};
177

178
/**
179
 * Validate property mapping structure
180
 * @param {string} dsId - Datasource ID
181
 * @param {Object} propertyMapping - Property mapping to validate
182
 * @returns {Array<string>} Array of error messages
183
 */
184
const validatePropertyMapping = (dsId, propertyMapping) => {
78✔
185
  const errors = [];
2✔
186

187
  if (typeof propertyMapping !== 'object' || Array.isArray(propertyMapping)) {
2✔
188
    errors.push(`propertyMapping for datasource '${dsId}' must be an object`);
1✔
189
  }
190

191
  return errors;
2✔
192
};
193

194
/**
195
 * Validate datasource configs structure
196
 * @param {Object} datasourceConfigs - The configs to validate
197
 * @param {Array<string>} datasourceIds - Array of valid datasource IDs
198
 * @returns {Object} Validation result
199
 */
200
export const validateDatasourceConfigs = (datasourceConfigs, datasourceIds) => {
78✔
201
  if (!datasourceConfigs) {
8✔
202
    return { isValid: false, errors: ['datasourceConfigs is required and cannot be null'] };
1✔
203
  }
204

205
  const allErrors = [];
7✔
206

207
  // Check that ALL datasources have configurations
208
  const configuredDatasourceIds = new Set(Object.keys(datasourceConfigs));
7✔
209
  for (const dsId of datasourceIds) {
7✔
210
    if (!configuredDatasourceIds.has(dsId)) {
11✔
211
      allErrors.push(`Missing configuration for datasource '${dsId}'`);
2✔
212
    }
213
  }
214

215
  // Validate each config
216
  for (const [dsId, config] of Object.entries(datasourceConfigs)) {
7✔
217
    const configErrors = validateSingleDatasourceConfig(dsId, config, datasourceIds);
10✔
218
    allErrors.push(...configErrors);
10✔
219
  }
220

221
  return {
7✔
222
    isValid: allErrors.length === 0,
223
    errors: allErrors
224
  };
225
};
226

227
/**
228
 * Normalize datasource configs to ensure all have the correct structure
229
 * @param {Object} datasourceConfigs - The configs to normalize
230
 * @param {Array<string>} datasourceIds - Array of datasource IDs
231
 * @returns {Object} Normalized configs
232
 */
233
export const normalizeDatasourceConfigs = (datasourceConfigs, datasourceIds) => {
78✔
234
  if (!datasourceConfigs) {
6✔
235
    throw new Error('datasourceConfigs is required and cannot be null');
2✔
236
  }
237

238
  const normalized = {};
4✔
239

240
  for (const dsId of datasourceIds) {
4✔
241
    if (!datasourceConfigs[dsId]) {
7✔
242
      throw new Error(`Missing configuration for datasource '${dsId}'`);
1✔
243
    }
244
    
245
    if (!datasourceConfigs[dsId].methodConfig) {
6!
NEW
246
      throw new Error(`methodConfig is required for datasource '${dsId}'`);
×
247
    }
248

249
    normalized[dsId] = {
6✔
250
      id: dsId,
251
      methodConfig: datasourceConfigs[dsId].methodConfig,
252
      propertyMapping: datasourceConfigs[dsId].propertyMapping || undefined
11✔
253
    };
254
  }
255

256
  return normalized;
3✔
257
};
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