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

statuscompliance / status-backend / 16189292467

10 Jul 2025 07:49AM UTC coverage: 86.822% (+0.1%) from 86.693%
16189292467

push

github

web-flow
feat(controls): added pending controls(#219)

879 of 1037 branches covered (84.76%)

Branch coverage included in aggregate %.

22 of 29 new or added lines in 5 files covered. (75.86%)

2 existing lines in 1 file now uncovered.

1763 of 2006 relevant lines covered (87.89%)

20.5 hits per line

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

82.19
/src/controllers/catalog.controller.js
1
import { models } from '../models/models.js';
2
import { storeGuaranteePoints } from '../utils/storeGuaranteePoints.js';
3
import registry from '../config/registry.js';
4
import { updateOrCreateAgreement } from '../utils/updateOrCreateAgreement.js';
5
import { v4 as uuidv4 } from 'uuid';
6
import { finalizeControlsByCatalogId } from './control.controller.js';
7

8
export const getCatalogs = async (req, res) => {
55✔
9
  try {
3✔
10
    const { status } = req.query;
3✔
11

12
    let where = {};
3✔
13
    if (status === 'finalized' || status === 'draft') {
3✔
14
      where = { status };
1✔
15
    }
16

17
    const catalogs = await models.Catalog.findAll({ where });
3✔
18
    res.status(200).json(catalogs);
2✔
19
  } catch (error) {
20
    res.status(500).json({ message: `Failed to retrieve catalogs, error: ${error.message}` });
1✔
21
  }
22
};
23

24
export const getCatalog = async (req, res) => {
55✔
25
  try {
3✔
26
    const row = await models.Catalog.findByPk(req.params.id);
3✔
27

28
    if (!row) {
2✔
29
      return res.status(404).json({ message: 'Catalog not found' });
1✔
30
    }
31

32
    res.status(200).json(row);
1✔
33
  } catch (error) {
34
    res.status(500).json({ message: `Failed to retrieve catalog, error: ${error.message}` });
1✔
35
  }
36
};
37

38
export const createCatalog = async (req, res) => {
55✔
39
  try {
4✔
40
    const { name, description, startDate, endDate, dashboard_id, status } = req.body;
4✔
41
    if (!name || !startDate || !endDate) {
4✔
42
      return res.status(400).json({ message: 'Missing required fields: name, startDate, and/or endDate' });
1✔
43
    }
44
    const tpaId = status === 'draft' ? null : `tpa-${uuidv4()}`;
3✔
45
    const rows = await models.Catalog.create({
3✔
46
      name,
47
      description,
48
      startDate,
49
      endDate,
50
      dashboard_id,
51
      tpaId,
52
      status: status || 'finalized',
4✔
53
    });
54
    res.status(201).json(rows);
2✔
55
  } catch (error) {
56
    res.status(500).json({ message: `Failed to create catalog, error: ${error.message}` });
1✔
57
  }
58
};
59

60
export const updateCatalog = async (req, res) => {
55✔
61
  try {
4✔
62
    const { id } = req.params;
4✔
63
    const { name, description, startDate, endDate, dashboard_id, tpaId, status } = req.body;
4✔
64

65
    const currentCatalog = await models.Catalog.findByPk(id);
4✔
66
    if (!currentCatalog) {
4✔
67
      return res.status(404).json({ message: 'Catalog not found' });
1✔
68
    }
69

70
    // Prevent changing status from finalized to draft
71
    if (currentCatalog.status === 'finalized' && status === 'draft') {
3✔
72
      return res.status(400).json({ message: 'Cannot change status from finalized to draft' });
1✔
73
    }
74

75
    const updatedCatalog = await models.Catalog.update(
2✔
76
      {
77
        name,
78
        description,
79
        startDate,
80
        endDate,
81
        dashboard_id,
82
        tpaId,
83
        status,
84
      },
85
      {
86
        where: {
87
          id,
88
        },
89
        returning: true,
90
        plain: true,
91
      }
92
    );
93
    res.status(200).json(updatedCatalog[1]); // The first element is the number of affectedRows
1✔
94
  } catch (error) {
95
    res.status(500).json({ message: `Failed to update catalog, error: ${error.message}` });
1✔
96
  }
97
};
98

99
export const deleteCatalog = async (req, res) => {
55✔
100
  const result = await models.Catalog.destroy({
3✔
101
    where: {
102
      id: req.params.id,
103
    },
104
  });
105

106
  if (result <= 0)
2✔
107
    return res.status(404).json({
1✔
108
      message: 'Catalog not found',
109
    });
110

111
  res.sendStatus(204);
1✔
112
};
113

114
export async function calculatePoints(req, res) {
115

116
  try {
4✔
117
    const agreementId = req.params.tpaId;
4✔
118
    const { from, to } = req.query;
4✔
119
    const { controlIds } = req.body || {};
4✔
120

121
    // Validate agreementId format
122
    if (!/^tpa-[a-f0-9-]{36}$/.test(agreementId)) {
4✔
123
      return res.status(400).json({ message: 'Invalid agreementId format' });
1✔
124
    }
125

126
    const catalog = await models.Catalog.findOne({ where: { tpaId: agreementId } });
3✔
127

128
    let controls = [];
2✔
129

130
    if (Array.isArray(controlIds) && controlIds.length > 0) {
2!
NEW
131
      controls = await models.Control.findAll({
×
132
        where: {
133
          catalogId: catalog.id,
134
          id: controlIds
135
        }
136
      });
137
    } else {
138
      controls = await models.Control.findAll({ where: { catalogId: catalog.id } });
2✔
139
    }
140
    await updateOrCreateAgreement(catalog, controls, agreementId);
2✔
141

142
    // Construct the URL for fetching guarantees
143
    const basePath = 'api/v6/states/';
×
144
    const safeAgreementId = encodeURIComponent(agreementId);
×
145

UNCOV
146
    const url = `${basePath}${safeAgreementId}/guarantees`;
×
147

148

UNCOV
149
    const guaranteesStates = await registry.get(url, {
×
150
      params: { from, to, newPeriodsFromGuarantees: false },
151
      headers: { 'x-access-token': req.cookies.accessToken }
152
    });
153

154
    // Update lastComputed
NEW
155
    const now = new Date();
×
NEW
156
    if (controls.length > 0) {
×
NEW
157
      const controlIdsToUpdate = controls.map(c => c.id);
×
158

NEW
159
      const [updatedCount] = await models.Control.update(
×
160
        { lastComputed: now },
161
        { where: { id: controlIdsToUpdate } }
162
      );
NEW
163
      res.status(200).json(updatedCount);
×
164
    }
165

166
    const { storedPoints, error } = await storeGuaranteePoints(guaranteesStates.data, agreementId);
×
167

168
    if (error.length > 0) {
×
NEW
169
      const points = await models.Point.findAll({ where: { agreementId } });
×
170
      if (points.length > 0) {
×
171
        res.status(200).json(points);
×
172
      } else {
173
        res.status(400).json(error);
×
174
      }
175
    } else {
176
      res.status(200).json(storedPoints);
×
177
    }
178
  } catch (error) {
179
    res.status(500).json({
3✔
180
      message: `Failed to get points, error: ${error.message}`,
181
    });
182
  }
183
}
184

185
// Draft Catalogs
186
export const createDraftCatalog = async (req, res) => {
55✔
187
  try {
3✔
188
    const { name, description, startDate, endDate, dashboard_id } = req.body;
3✔
189
    if (!name || !startDate) {
3✔
190
      return res.status(400).json({ message: 'Missing required fields: name and/or startDate' });
1✔
191
    }
192

193
    const rows = await models.Catalog.create({
2✔
194
      name,
195
      description,
196
      startDate: startDate,
197
      endDate,
198
      dashboard_id,
199
      tpaId: null,
200
      status: 'draft',
201
    });
202
    res.status(201).json(rows);
1✔
203
  } catch (error) {
204
    res.status(500).json({ message: `Failed to create draft catalog, error: ${error.message}` });
1✔
205
  }
206
};
207

208
export const finalizeCatalog = async (req, res) => {
55✔
209
  try {
5✔
210
    const { id } = req.params;
5✔
211

212
    const currentCatalog = await models.Catalog.findByPk(id);
5✔
213
    if (!currentCatalog) {
5✔
214
      return res.status(404).json({ message: 'Catalog not found' });
1✔
215
    }
216

217
    if (currentCatalog.status !== 'draft') {
4✔
218
      return res.status(400).json({ message: 'Only draft catalogs can be finalized' });
1✔
219
    }
220

221
    if (!currentCatalog.startDate || !currentCatalog.endDate) {
3✔
222
      return res.status(400).json({ message: 'Catalog must have startDate and endDate to be finalized' });
1✔
223
    }
224

225
    const tpaId = `tpa-${uuidv4()}`;
2✔
226

227
    // First we update the catalog status and TPA ID
228
    const updatedCatalog = await models.Catalog.update(
2✔
229
      {
230
        status: 'finalized',
231
        tpaId,
232
      },
233
      {
234
        where: {
235
          id,
236
        },
237
        returning: true,
238
        plain: true,
239
      }
240
    );
241

242
    // Then we finalize the controls
243
    const controlsResult = await finalizeControlsByCatalogId(id);
1✔
244

245
    // Return the updated catalog and the number of finalized controls
246
    const finalizedCount = Array.isArray(controlsResult?.updated) ? controlsResult.updated.length : 0;
1!
247
    res.status(200).json({
1✔
248
      catalog: updatedCatalog[1],
249
      controls: {
250
        finalized: finalizedCount,
251
      }
252
    });
253
  } catch (error) {
254
    res.status(500).json({ message: `Failed to finalize catalog, error: ${error.message}` });
1✔
255
  }
256
};
257

258
export { finalizeControlsByCatalogId } from './control.controller.js';
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