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

statuscompliance / status-backend / 14548565353

19 Apr 2025 11:11AM UTC coverage: 35.962% (-2.2%) from 38.115%
14548565353

push

github

web-flow
feat(catalog): added draft objects (#144)

* feat: added draft catalogs

* feat: added draft controls

217 of 710 branches covered (30.56%)

Branch coverage included in aggregate %.

15 of 124 new or added lines in 4 files covered. (12.1%)

3 existing lines in 2 files now uncovered.

613 of 1598 relevant lines covered (38.36%)

3.86 hits per line

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

6.11
/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 { agreementBuilder } from '../utils/agreementBuilder.js';
5
import { v4 as uuidv4 } from 'uuid';
6
import _ from 'lodash';
7
import { finalizeControlsByCatalogId } from './control.controller.js';
8

9
export const getCatalogs = async (req, res) => {
13✔
10
  try {
×
11
    const catalogs = await models.Catalog.findAll();
×
12
    res.status(200).json(catalogs);
×
13
  } catch (error) {
14
    res.status(500).json({ message: `Failed to retrieve catalogs, error: ${error.message}` });
×
15
  }
16
};
17

18
export const getCatalog = async (req, res) => {
13✔
19
  try {
×
20
    const row = await models.Catalog.findByPk(req.params.id);
×
21

22
    if (!row) {
×
23
      return res.status(404).json({ message: 'Catalog not found' });
×
24
    }
25

26
    res.status(200).json(row);
×
27
  } catch (error) {
28
    res.status(500).json({ message: `Failed to retrieve catalog, error: ${error.message}` });
×
29
  }
30
};
31

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

54
export const updateCatalog = async (req, res) => {
13✔
55
  try {
×
56
    const { id } = req.params;
×
NEW
57
    const { name, description, startDate, endDate, dashboard_id, tpaId, status } = req.body;
×
58

59
    const currentCatalog = await models.Catalog.findByPk(id);
×
60
    if (!currentCatalog) {
×
61
      return res.status(404).json({ message: 'Catalog not found' });
×
62
    }
63

64
    // Prevent changing status from finalized to draft
NEW
65
    if (currentCatalog.status === 'finalized' && status === 'draft') {
×
NEW
66
      return res.status(400).json({ message: 'Cannot change status from finalized to draft' });
×
67
    }
68

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

93
export const deleteCatalog = async (req, res) => {
13✔
94
  const result = await models.Catalog.destroy({
×
95
    where: {
96
      id: req.params.id,
97
    },
98
  });
99

100
  if (result <= 0)
×
101
    return res.status(404).json({
×
102
      message: 'Catalog not found',
103
    });
104

105
  res.sendStatus(204);
×
106
};
107

108
export async function calculatePoints(req, res) {
109
  try {
×
110
    const agreementId = req.params.tpaId;
×
111
    const { from, to } = req.query;
×
112

113
    const catalog = await models.Catalog.findOne({ where: {tpaId: agreementId}});
×
114
    const controls = await models.Control.findAll({where: {catalogId: catalog.id}});
×
115

116
    await updateOrCreateAgreement(catalog, controls, agreementId);
×
117
    
118
    const guaranteesStates = await registry.get(`api/v6/states/${agreementId}/guarantees`, {
×
119
      params: { from, to, newPeriodsFromGuarantees: false },
120
      headers: { 'x-access-token': req.cookies.accessToken }
121
    });
122
    const { storedPoints, error } = await storeGuaranteePoints(guaranteesStates.data, agreementId);
×
123
    if (error.length > 0) {
×
124
      const points = await models.Point.findAll({where: {agreementId}});
×
125
      if (points.length > 0) {
×
126
        res.status(200).json(points);
×
127
      } else {
128
        res.status(400).json(error);
×
129
      }
130
    } else {
131
      res.status(200).json(storedPoints);
×
132
    }
133
  } catch (error) {
134
    res.status(500).json({
×
135
      message: `Failed to get points, error: ${error.message}`,
136
    });
137
  }
138
}
139

140
async function updateOrCreateAgreement(catalog, controls, agreementId) {
141
  const agreement = await agreementBuilder(catalog, controls, { id: agreementId });
×
142
  try {
×
143
    const response = await registry.get(`api/v6/agreements/${agreementId}`);
×
144
    const oldAgreement = response.data;
×
145

146
    if (!_.isEqual(agreement, oldAgreement)) {
×
147
      console.log(`Updating agreement ${agreementId}`);
×
148
      await registry.put(`api/v6/agreements/${agreementId}`, agreement);
×
149
    }
150
  } catch (error) {
151
    if (error.response?.status === 404) {
×
152
      console.log(`Creating agreement ${agreementId}`);
×
153
      await registry.post('api/v6/agreements', agreement);
×
154
    } else {
155
      throw error; // Rethrow other errors
×
156
    }
157
  }
158
}
159

160
// Draft Catalogs
161

162
export const getDraftCatalogs = async (req, res) => {
13✔
NEW
163
  try {
×
NEW
164
    const catalogs = await models.Catalog.findAll({
×
165
      where: {
166
        status: 'draft'
167
      }
168
    });
NEW
169
    res.status(200).json(catalogs);
×
170
  } catch (error) {
NEW
171
    res.status(500).json({ message: `Failed to retrieve draft catalogs, error: ${error.message}` });
×
172
  }
173
};
174

175
export const createDraftCatalog = async (req, res) => {
13✔
NEW
176
  try {
×
NEW
177
    const { name, description, startDate, endDate, dashboard_id } = req.body;
×
NEW
178
    if (!name || !startDate) {
×
NEW
179
      return res.status(400).json({ message: 'Missing required fields: name and/or startDate' });
×
180
    }
181
    
NEW
182
    const rows = await models.Catalog.create({
×
183
      name,
184
      description,
185
      startDate: startDate,
186
      endDate,
187
      dashboard_id,
188
      tpaId: null,
189
      status: 'draft',
190
    });
NEW
191
    res.status(201).json(rows);
×
192
  } catch (error) {
NEW
193
    res.status(500).json({ message: `Failed to create draft catalog, error: ${error.message}` });
×
194
  }
195
};
196

197
export const finalizeCatalog = async (req, res) => {
13✔
NEW
198
  try {
×
NEW
199
    const { id } = req.params;
×
200
    
NEW
201
    const currentCatalog = await models.Catalog.findByPk(id);
×
NEW
202
    if (!currentCatalog) {
×
NEW
203
      return res.status(404).json({ message: 'Catalog not found' });
×
204
    }
205
    
NEW
206
    if (currentCatalog.status !== 'draft') {
×
NEW
207
      return res.status(400).json({ message: 'Only draft catalogs can be finalized' });
×
208
    }
209
    
NEW
210
    if (!currentCatalog.startDate || !currentCatalog.endDate) {
×
NEW
211
      return res.status(400).json({ message: 'Catalog must have startDate and endDate to be finalized' });
×
212
    }
213
    
NEW
214
    const tpaId = `tpa-${uuidv4()}`;
×
215
    
216
    // First we update the catalog status and TPA ID
NEW
217
    const updatedCatalog = await models.Catalog.update(
×
218
      {
219
        status: 'finalized',
220
        tpaId,
221
      },
222
      {
223
        where: {
224
          id,
225
        },
226
        returning: true,
227
        plain: true,
228
      }
229
    );
230
    
231
    // Then we finalize the controls
NEW
232
    const controlsResult = await finalizeControlsByCatalogId(id);
×
233
    
234
    // Return the updated catalog and the number of finalized controls
NEW
235
    res.status(200).json({
×
236
      catalog: updatedCatalog[1],
237
      controls: {
238
        finalized: controlsResult.updated.length,
239
      }
240
    });
241
  } catch (error) {
NEW
242
    res.status(500).json({ message: `Failed to finalize catalog, error: ${error.message}` });
×
243
  }
244
};
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