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

statuscompliance / status-backend / 14621848543

23 Apr 2025 03:18PM UTC coverage: 51.212% (+7.1%) from 44.064%
14621848543

Pull #139

github

web-flow
Merge 4d453dad8 into a5c98d299
Pull Request #139: test(utils): added sqlQueryBuilder

395 of 844 branches covered (46.8%)

Branch coverage included in aggregate %.

159 of 172 new or added lines in 1 file covered. (92.44%)

66 existing lines in 1 file now uncovered.

936 of 1755 relevant lines covered (53.33%)

6.06 hits per line

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

4.04
/src/controllers/control.controller.js
1
import { models } from '../models/models.js';
2
import { methods } from '../config/grafana.js';
3
import { checkRequiredProperties } from '../utils/checkRequiredProperties.js';
4

5
export const getControls = async (req, res) => {
19✔
6
  try {
×
7
    const { status } = req.query;
×
8
    
9
    const whereClause = {};
×
10
    if (status === 'finalized' || status === 'draft') {
×
11
      whereClause.status = status;
×
12
    }
13
    
14
    const rows = await models.Control.findAll({ 
×
15
      where: whereClause 
16
    });
17
    
18
    res.status(200).json(rows);
×
19
  } catch (error) {
20
    res.status(500).json({
×
21
      message: 'Error retrieving controls',
22
      error: error.message,
23
    });
24
  }
25
};
26

27
export const getControl = async (req, res) => {
19✔
28
  try {
×
29
    const row = await models.Control.findByPk(req.params.id);
×
30

31
    if (!row) {
×
32
      return res.status(404).json({
×
33
        message: 'Control not found',
34
      });
35
    }
36

37
    res.status(200).json(row);
×
38
  } catch (error) {
39
    res.status(500).json({
×
40
      message: 'Error retrieving control',
41
      error: error.message,
42
    });
43
  }
44
};
45

46
export const getCatalogControls = async (req, res) => {
19✔
47
  try {
×
48
    const { catalogId } = req.params;
×
49
    const { status } = req.query;
×
50
    
51
    const whereClause = {
×
52
      catalogId: catalogId,
53
    };
54
    
55
    if (status === 'finalized' || status === 'draft') {
×
56
      whereClause.status = status;
×
57
    }
58
    
59
    const rows = await models.Control.findAll({
×
60
      where: whereClause,
61
    });
62

63
    res.status(200).json(rows);
×
64
  } catch (error) {
65
    res.status(500).json({
×
66
      message: 'Error retrieving catalog controls',
67
      error: error.message,
68
    });
69
  }
70
};
71

72
export const createControl = async (req, res) => {
19✔
73
  try {
×
74
    const {
75
      name,
76
      description,
77
      period,
78
      startDate,
79
      endDate,
80
      mashupId,
81
      catalogId,
82
      params, // Should include endpoint and threshold at least
83
    } = req.body;
×
84
    
85
    const {validation, textError} = checkRequiredProperties(params, ['endpoint', 'threshold']);
×
86

87
    if(!validation) {
×
88
      return res.status(400).json({error: textError});
×
89
    }
90
    
91
    let formattedStartDate = null;
×
92
    if (startDate) {
×
93
      formattedStartDate = new Date(startDate);
×
94
      if (isNaN(formattedStartDate.getTime())) {
×
95
        return res.status(400).json({ error: 'Invalid startDate' });
×
96
      }
97
    }
98
    const formattedEndDate = endDate ? new Date(endDate) : null;
×
99

100
    const rows = await models.Control.create({
×
101
      name,
102
      description,
103
      period,
104
      startDate: formattedStartDate,
105
      endDate: formattedEndDate,
106
      mashupId,
107
      catalogId,
108
      params,
109
      status: 'finalized',
110
    });
111
    
112
    res.status(201).json({
×
113
      id: rows.id,
114
      name,
115
      description,
116
      period,
117
      formattedStartDate,
118
      formattedEndDate,
119
      mashupId,
120
      catalogId,
121
    });
122
  } catch (error) {
123
    console.error('Error creating control:', error);
×
124
    res.status(500).json({
×
125
      message: 'Error creating control',
126
      error: error.message,
127
    });
128
  }
129
};
130

131
export const updateControl = async (req, res) => {
19✔
132
  const { id } = req.params;
×
133
  const {
134
    name,
135
    description,
136
    period,
137
    startDate,
138
    endDate,
139
    mashupId,
140
    catalogId,
141
    params,
142
    status,
143
  } = req.body;
×
144
  
145
  try {
×
146
    const currentControl = await models.Control.findByPk(id);
×
147
    if (!currentControl) {
×
148
      return res.status(404).json({ message: 'Control not found' });
×
149
    }
150
    
UNCOV
151
    if (currentControl.status === 'finalized' && status === 'draft') {
×
152
      return res.status(400).json({ 
×
153
        message: 'Cannot change status from finalized to draft' 
154
      });
155
    }
156
    
UNCOV
157
    if (status === 'finalized' || (!status && currentControl.status === 'finalized')) {
×
UNCOV
158
      const {validation, textError} = checkRequiredProperties(params || currentControl.params, ['endpoint', 'threshold']);
×
159
      if (!validation) {
×
160
        return res.status(400).json({error: textError});
×
161
      }
162
    }
163
    
UNCOV
164
    const formattedStartDate = startDate ? new Date(startDate) : currentControl.startDate;
×
UNCOV
165
    const formattedEndDate = endDate ? new Date(endDate) : currentControl.endDate;
×
166

167
    await models.Control.update(
×
168
      {
169
        name,
170
        description,
171
        period,
172
        startDate: formattedStartDate,
173
        endDate: formattedEndDate,
174
        mashupId,
175
        catalogId,
176
        params,
177
        status,
178
      },
179
      {
180
        where: {
181
          id,
182
        },
183
      }
184
    );
185

UNCOV
186
    const row = await models.Control.findByPk(id);
×
UNCOV
187
    res.status(200).json(row);
×
188
  } catch (error) {
189
    res.status(500).json({ 
×
190
      message: `Failed to update control, error: ${error.message}` 
191
    });
192
  }
193
};
194

195
export const deleteControl = async (req, res) => {
19✔
UNCOV
196
  try {
×
UNCOV
197
    const { id } = req.params;
×
198
    const deletedCount = await models.Control.destroy({
×
199
      where: { id },
200
    });
201

UNCOV
202
    if (deletedCount === 0) {
×
UNCOV
203
      return res.status(404).json({ message: 'Control not found' });
×
204
    }
205

UNCOV
206
    res.status(204).json();
×
207
  } catch (error) {
208
    console.error('Error deleting control:', error);
×
UNCOV
209
    res.status(500).json({
×
210
      message: 'Error deleting control',
211
      error: error.message,
212
    });
213
  }
214
};
215

216
export async function addPanelToControl(req, res) {
UNCOV
217
  const { id, panelId } = req.params;
×
UNCOV
218
  const { dashboardUid } = req.body;
×
219

220
  try {
×
UNCOV
221
    const panel = await models.Panel.create({
×
222
      id: panelId,
223
      controlId: id,
224
      dashboardUid: dashboardUid,
225
    });
UNCOV
226
    res.status(201).json({
×
227
      message: 'Panel added to control',
228
      data: panel,
229
    });
230
  } catch (error) {
UNCOV
231
    res.status(500).json({
×
232
      message: 'Error adding panel to control',
233
      error: error.message,
234
    });
235
  }
236
}
237

238
export async function getPanelsByControlId(req, res) {
UNCOV
239
  const { id } = req.params;
×
240

241
  try {
×
UNCOV
242
    const panels = await models.Panel.findAll({
×
243
      where: {
244
        controlId: id,
245
      },
246
    });
UNCOV
247
    let panelsDTO = [];
×
248

249
    // THIS MUST BE CACHED AND REFACTORED
UNCOV
250
    for (let panel of panels) {
×
UNCOV
251
      panel = panel.dataValues;
×
252
      let panelDTO = {};
×
253
      if (Object.prototype.hasOwnProperty.call(panel, 'dashboardUid')) {
×
254
        const dashboardUid = panel.dashboardUid;
×
255
        const dashboardResponse =
256
                    await methods.dashboard.getDashboardByUID(dashboardUid);
×
UNCOV
257
        const actualDashboard = dashboardResponse.data.dashboard;
×
258
        const panelElement = actualDashboard.panels.find(
×
259
          (e) => e.id == panel.id
×
260
        );
261
        panelDTO = {
×
262
          ...panel,
263
          title: panelElement.title,
264
          type: panelElement.type,
265
          sqlQuery: panelElement.targets[0].rawSql,
266
          table: panelElement.targets[0].table,
267
          displayName: panelElement.targets[0].alias,
268
          gridPos: panelElement.gridPos,
269
        };
UNCOV
270
        panelsDTO.push(panelDTO);
×
271
      }
272
    }
UNCOV
273
    res.status(200).json(panelsDTO);
×
274
  } catch (error) {
275
    if (error.response) {
×
UNCOV
276
      const { status, statusText } = error.response;
×
277
      return res.status(status).json({
×
278
        message: statusText,
279
        error: error,
280
      });
281
    } else {
UNCOV
282
      res.status(500).json({
×
283
        message:
284
                    'Failed to get panels from control, error in Grafana API',
285
        error: error.message,
286
      });
287
    }
288
  }
289
}
290

291
export async function deletePanelFromControl(req, res) {
UNCOV
292
  const { id, panelId } = req.params;
×
293

294
  try {
×
UNCOV
295
    await models.Panel.destroy({
×
296
      where: {
297
        controlId: id,
298
        id: panelId,
299
      },
300
    });
UNCOV
301
    res.status(204).json({
×
302
      message: 'Panel deleted from control',
303
    });
304
  } catch (error) {
UNCOV
305
    res.status(500).json({
×
306
      message: 'Error deleting panel from control',
307
      error: error.message,
308
    });
309
  }
310
}
311

312
// Draft controls
313

314
export const createDraftControl = async (req, res) => {
19✔
315
  const {
316
    name,
317
    description,
318
    startDate,
319
    endDate,
320
    period,
321
    mashupId,
322
    catalogId,
323
    params,
UNCOV
324
  } = req.body;
×
325
  
326
  if (!name || !catalogId) {
×
UNCOV
327
    return res.status(400).json({
×
328
      error: 'Missing required fields for draft control: name and catalogId'
329
    });
330
  }
331
  
UNCOV
332
  const {validation, textError} = checkRequiredProperties(params, ['endpoint', 'threshold']);
×
UNCOV
333
  if (!validation) {
×
334
    return res.status(400).json({error: textError});
×
335
  }
336
  
337
  try {
×
338
    // Check if catalog exists
UNCOV
339
    const catalog = await models.Catalog.findByPk(catalogId);
×
UNCOV
340
    if (!catalog) {
×
UNCOV
341
      return res.status(404).json({ error: 'Catalog not found' });
×
342
    }
343
    
344
    // Check if catalog is a draft
UNCOV
345
    if (catalog.status !== 'draft') {
×
UNCOV
346
      return res.status(400).json({
×
347
        error: 'Draft controls can only be added to draft catalogs'
348
      });
349
    }
350
    
UNCOV
351
    const rows = await models.Control.create({
×
352
      name,
353
      description: description || '',
×
354
      period: period || 'MONTHLY',
×
355
      startDate: startDate ? new Date(startDate) : new Date(),
×
356
      endDate: endDate ? new Date(endDate) : null,
×
357
      mashupId: mashupId || '',
×
358
      catalogId,
359
      params: params || {},
×
360
      status: 'draft',
361
    });
362
    
UNCOV
363
    res.status(201).json(rows);
×
364
  } catch (error) {
UNCOV
365
    res.status(500).json({
×
366
      message: `Failed to create draft control, error: ${error.message}`
367
    });
368
  }
369
};
370

371
export const finalizeControl = async (req, res) => {
19✔
372
  try {
×
373
    const { id } = req.params;
×
374
    
UNCOV
375
    const currentControl = await models.Control.findByPk(id);
×
UNCOV
376
    if (!currentControl) {
×
377
      return res.status(404).json({ message: 'Control not found' });
×
378
    }
379
    
UNCOV
380
    if (currentControl.status !== 'draft') {
×
UNCOV
381
      return res.status(400).json({ message: 'Only draft controls can be finalized' });
×
382
    }
383
    
384
    // Check if associated catalog is finalized
UNCOV
385
    const catalog = await models.Catalog.findByPk(currentControl.catalogId);
×
UNCOV
386
    if (!catalog) {
×
387
      return res.status(404).json({ message: 'Associated catalog not found' });
×
388
    }
389
    
UNCOV
390
    if (catalog.status !== 'finalized') {
×
UNCOV
391
      return res.status(400).json({ 
×
392
        message: 'Cannot finalize a control that belongs to a draft catalog' 
393
      });
394
    }
395
    
396
    // Check required properties for finalized controls
UNCOV
397
    const {validation, textError} = checkRequiredProperties(
×
398
      currentControl.params, 
399
      ['endpoint', 'threshold']
400
    );
401
    
UNCOV
402
    if (!validation) {
×
UNCOV
403
      return res.status(400).json({
×
404
        error: `Cannot finalize control: ${textError}`
405
      });
406
    }
407
    
UNCOV
408
    const updatedControl = await models.Control.update(
×
409
      {
410
        status: 'finalized',
411
      },
412
      {
413
        where: {
414
          id,
415
        },
416
        returning: true,
417
      }
418
    );
419
    
UNCOV
420
    res.status(200).json(updatedControl[1][0]);
×
421
  } catch (error) {
UNCOV
422
    res.status(500).json({ 
×
423
      message: `Failed to finalize control, error: ${error.message}` 
424
    });
425
  }
426
};
427

428
// Method to finalize all draft controls in a catalog
429
export const finalizeControlsByCatalogId = async (catalogId) => {
19✔
UNCOV
430
  try {
×
431
    // Get draft controls
UNCOV
432
    const draftControls = await models.Control.findAll({
×
433
      where: {
434
        catalogId,
435
        status: 'draft'
436
      }
437
    });
438
    
439
    // Update valid controls to finalized
UNCOV
440
    let updatedControls = {};
×
UNCOV
441
    if (draftControls.length > 0) {
×
UNCOV
442
      updatedControls = await models.Control.update(
×
443
        { status: 'finalized' },
444
        {
445
          where: {
UNCOV
446
            id: draftControls.map(control => control.id)
×
447
          }
448
        }
449
      );
450
    }
451
    
452
    return updatedControls;
×
453
  } catch (error) {
UNCOV
454
    console.error('Error finalizing controls:', error);
×
UNCOV
455
    throw error;
×
456
  }
457
};
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

© 2025 Coveralls, Inc