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

GrottoCenter / grottocenter-api / 11210769896

07 Oct 2024 07:32AM UTC coverage: 46.184% (+0.08%) from 46.105%
11210769896

push

github

vmarseguerra
fix(entities): small issues
- document description body is not saved when importing a csv
- cannot get statistics for country and massif
- broken sql dev setup
- missing 'names' key in massif converter
- stop validating the mimeType when uploading a file

740 of 2199 branches covered (33.65%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 3 files covered. (0.0%)

251 existing lines in 21 files now uncovered.

2485 of 4784 relevant lines covered (51.94%)

4.46 hits per line

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

7.37
/api/services/RecentChangeService.js
1
const CommonService = require('./CommonService');
6✔
2

3
const GROUP_MAX_TIME_DIFF_S = 60 * 60 * 6; // 6 Hours
6✔
4

5
async function removeOlderChanges() {
6
  const query = `DELETE FROM t_last_change WHERE date_change < current_timestamp - interval '1 month';`;
×
7
  await CommonService.query(query);
×
8
}
9

10
// When creating a cave, entrance, massif, document or grotto the name is created after the entity
11
// So the name is updated afterward
12
async function setNameCreate(entityType, entityId, authorId, name) {
13
  const query = `
6✔
14
  UPDATE t_last_change
15
  SET name = $1
16
  WHERE type_entity = $2 AND type_change = 'create' AND id_entity = $3 AND id_author = $4 AND date_change > current_timestamp - interval '1 minute';
17
  `;
18
  await CommonService.query(query, [name, entityType, entityId, authorId]);
6✔
19
}
20

21
// When deleting / restoring an entity, the author is not historized, so it is updated afterward
22
async function setDeleteRestoreAuthor(
23
  changeType,
24
  entityType,
25
  entityId,
26
  authorId
27
) {
28
  const query = `
2✔
29
  UPDATE t_last_change
30
  SET id_author = $4
31
  WHERE type_entity = $2 AND type_change = $1 AND id_entity = $3 AND date_change > current_timestamp - interval '1 minute';
32
  `;
33
  await CommonService.query(query, [
2✔
34
    changeType,
35
    entityType,
36
    entityId,
37
    authorId,
38
  ]);
39
}
40

41
// To make the change list more relevant we groups change event when they are from the same author and about the same entity
42
function groupChanges(changes) {
43
  const authorChanges = {};
×
44
  // Grouping order:
45
  // - author
46
  // - related entity
47
  // - time
48
  // Change type 'create' or 'restore' have priority over 'update'
49
  // Change type 'delete' is never grouped
50

51
  const createNewGroupFromChange = (c) => ({
×
52
    date: c.date_change,
53
    authorId: c.id_author,
54
    author: c.nickname,
55
    mainEntityType: c.type_related_entity ?? c.type_entity,
×
56
    mainEntityId: c.id_related_entity ?? c.id_entity,
×
57
    mainAction: c.id_related_entity ? null : c.type_change, // Null in case it is a change on a sub entity
×
58
    subEntityTypes: c.id_related_entity ? [c.type_entity] : [],
×
59
    subAction: c.id_related_entity ? c.type_change : null, // Null when no sub entities
×
60
    name: c.name, // Can be the real entity name or the related entity name
61
  });
62

63
  const addChangeToExistingGroup = (g, c) => {
×
UNCOV
64
    if (
×
65
      !c.id_related_entity &&
×
66
      g.mainAction === 'update' &&
67
      ['create', 'restore'].includes(c.type_change)
68
    )
69
      g.mainAction = c.type_change; // eslint-disable-line no-param-reassign
×
UNCOV
70
    if (c.id_related_entity && !g.subEntityTypes.includes(c.type_entity))
×
UNCOV
71
      g.subEntityTypes.push(c.type_entity);
×
UNCOV
72
    if (c.id_related_entity && g.subAction !== c.type_change)
×
UNCOV
73
      g.subAction = 'change'; // eslint-disable-line no-param-reassign
×
74
  };
75

76
  for (const change of changes) {
×
77
    if (!authorChanges[change.id_author]) {
×
UNCOV
78
      authorChanges[change.id_author] = [createNewGroupFromChange(change)];
×
79
      continue; // eslint-disable-line no-continue
×
80
    }
81

82
    const authorsChanges = authorChanges[change.id_author];
×
83
    const previousChangeForThisEntity = authorsChanges.find(
×
84
      (e) =>
UNCOV
85
        e.mainEntityId === (change.id_related_entity ?? change.id_entity) &&
×
86
        e.mainEntityType === (change.type_related_entity ?? change.type_entity)
×
87
    );
88

UNCOV
89
    if (
×
90
      !previousChangeForThisEntity ||
×
91
      Math.abs(previousChangeForThisEntity.date - change.date_change) >
92
        GROUP_MAX_TIME_DIFF_S * 1000 ||
93
      change.type_change === 'delete' ||
94
      previousChangeForThisEntity.mainAction === 'delete'
95
    ) {
UNCOV
96
      authorsChanges.unshift(createNewGroupFromChange(change));
×
97
      continue; // eslint-disable-line no-continue
×
98
    }
UNCOV
99
    addChangeToExistingGroup(previousChangeForThisEntity, change);
×
100
  }
101

UNCOV
102
  const allGroups = Object.values(authorChanges).flat();
×
UNCOV
103
  return allGroups.sort((a, b) => b.date - a.date);
×
104
}
105

106
async function getRecent() {
107
  // The t_last_change table is populated by trigger on the other main tables
UNCOV
108
  const query = `
×
109
  SELECT tbl.*, author.nickname FROM t_last_change tbl
110
  LEFT JOIN t_caver author ON tbl.id_author = author.id
111
  ORDER BY date_change DESC
112
  `;
UNCOV
113
  const rep = await CommonService.query(query);
×
114

115
  // When creating/updating a entrance the associated cave is also created/updated (when not part of a network)
116
  // As cave changes are duplicate we filter out them
UNCOV
117
  const changes = rep.rows.filter((e, i, a) => {
×
118
    if (e.type_entity !== 'cave') return true;
×
119

120
    // Is the cave change is after the entrance change ?
UNCOV
121
    if (
×
122
      i < a.length - 2 &&
×
123
      a[i + 1].date_change - e.date_change < 5000 &&
124
      a[i + 1].id_author === e.id_author &&
125
      a[i + 1].type_entity === 'entrance'
126
    )
UNCOV
127
      return false;
×
128

129
    // Is the cave change is before the entrance change ?
UNCOV
130
    if (
×
131
      i > 0 &&
×
132
      e.date_change - a[i - 1].date_change < 5000 &&
133
      a[i - 1].id_author === e.id_author &&
134
      a[i - 1].type_entity === 'entrance'
135
    )
UNCOV
136
      return false;
×
137

UNCOV
138
    return true;
×
139
  });
140

141
  // 5% chance to also remove older changes
UNCOV
142
  if (Math.random() < 0.05) removeOlderChanges();
×
143

UNCOV
144
  return groupChanges(changes);
×
145
}
146

147
module.exports = {
6✔
148
  getRecent,
149
  setNameCreate,
150
  setDeleteRestoreAuthor,
151
};
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