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

GrottoCenter / grottocenter-api / 25562024572

08 May 2026 02:45PM UTC coverage: 86.295% (-0.2%) from 86.497%
25562024572

Pull #1566

github

dawoldo
fix(1451): added traling newline and removed spurious entry
Pull Request #1566: 1451 private messaging

3050 of 3681 branches covered (82.86%)

Branch coverage included in aggregate %.

216 of 255 new or added lines in 16 files covered. (84.71%)

8 existing lines in 1 file now uncovered.

6370 of 7235 relevant lines covered (88.04%)

50.56 hits per line

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

79.27
/api/controllers/v1/message/create.js
1
const MessageService = require('../../../services/MessageService');
1✔
2
const ControllerService = require('../../../services/ControllerService');
1✔
3
const CommonService = require('../../../services/CommonService');
1✔
4
const NotificationService = require('../../../services/NotificationService');
1✔
5

6
/**
7
 * MessageController.create
8
 *
9
 * @description :: Send a private message.
10
 * @help        :: See https://sailsjs.com/documentation/concepts/controllers
11
 */
12

13
module.exports = async (req, res) => {
1✔
14
  const senderId = req.token.id;
17✔
15
  const recipientId = req.param('recipientId');
17✔
16
  const conversationId = req.param('conversationId');
17✔
17
  const body = req.param('body');
17✔
18

19
  // Validation
20
  if (!body || body.trim().length === 0) {
17✔
21
    return res.badRequest(
2✔
22
      sails.helpers.formatStructuredError(
23
        req,
24
        'Message body cannot be empty.',
25
        'E_VALIDATION'
26
      )
27
    );
28
  }
29
  if (body.length > 5000) {
15✔
30
    return res.badRequest(
1✔
31
      sails.helpers.formatStructuredError(
32
        req,
33
        'Message body cannot exceed 5000 characters.',
34
        'E_VALIDATION'
35
      )
36
    );
37
  }
38

39
  let finalConversationId;
40

41
  // 1. Resolve Conversation or Recipient Eligibility
42
  if (conversationId) {
14✔
43
    try {
3✔
44
      const queryResult = await CommonService.query(
3✔
45
        'SELECT 1 FROM j_participant WHERE id_conversation = $1 AND id_caver = $2',
46
        [conversationId, senderId]
47
      );
48

49
      if (queryResult.rows.length === 0) {
3✔
50
        return res.forbidden(
1✔
51
          sails.helpers.formatStructuredError(
52
            req,
53
            'You are not a participant in this conversation.',
54
            'E_AUTHORIZATION'
55
          )
56
        );
57
      }
58

59
      // Check if the other participant is still eligible
60
      const otherParticipantId = await MessageService.getOtherParticipantId(
2✔
61
        conversationId,
62
        senderId
63
      );
64
      if (otherParticipantId) {
2!
65
        try {
2✔
66
          await MessageService.getEligibleRecipient(otherParticipantId);
2✔
67
        } catch (err) {
68
          if (err.code === 'E_NOT_FOUND') {
1!
NEW
69
            return res.notFound(
×
70
              sails.helpers.formatStructuredError(
71
                req,
72
                err.message,
73
                'E_NOT_FOUND'
74
              )
75
            );
76
          }
77
          if (err.code === 'E_FORBIDDEN') {
1!
78
            return res.forbidden(
1✔
79
              sails.helpers.formatStructuredError(
80
                req,
81
                err.message,
82
                'E_AUTHORIZATION'
83
              )
84
            );
85
          }
NEW
86
          throw err;
×
87
        }
88
      }
89

90
      finalConversationId = conversationId;
1✔
91
    } catch (err) {
NEW
92
      sails.log.error(err);
×
NEW
93
      return res.serverError(
×
94
        sails.helpers.formatStructuredError(
95
          req,
96
          'An error occurred while verifying conversation membership.',
97
          'E_SERVER_ERROR'
98
        )
99
      );
100
    }
101
  } else if (recipientId) {
11!
102
    if (Number(recipientId) === Number(senderId)) {
11✔
103
      return res.badRequest(
1✔
104
        sails.helpers.formatStructuredError(
105
          req,
106
          'You cannot send a message to yourself.',
107
          'E_VALIDATION'
108
        )
109
      );
110
    }
111

112
    try {
10✔
113
      await MessageService.getEligibleRecipient(recipientId);
10✔
114
    } catch (err) {
115
      if (err.code === 'E_NOT_FOUND') {
4✔
116
        return res.notFound(
1✔
117
          sails.helpers.formatStructuredError(req, err.message, 'E_NOT_FOUND')
118
        );
119
      }
120
      if (err.code === 'E_FORBIDDEN') {
3!
121
        return res.forbidden(
3✔
122
          sails.helpers.formatStructuredError(
123
            req,
124
            err.message,
125
            'E_AUTHORIZATION'
126
          )
127
        );
128
      }
NEW
129
      sails.log.error(err);
×
NEW
130
      return res.serverError(
×
131
        sails.helpers.formatStructuredError(
132
          req,
133
          'An error occurred while verifying the recipient.',
134
          'E_SERVER_ERROR'
135
        )
136
      );
137
    }
138

139
    // 2. Resolve/Create Conversation for the Recipient
140
    try {
6✔
141
      finalConversationId = await MessageService.findExistingConversation(
6✔
142
        senderId,
143
        recipientId
144
      );
145
      if (!finalConversationId) {
6✔
146
        const newConvo = await MessageService.createConversation(
4✔
147
          senderId,
148
          recipientId
149
        );
150
        finalConversationId = newConvo.id;
4✔
151
      }
152
    } catch (err) {
NEW
153
      sails.log.error(err);
×
NEW
154
      return res.serverError(
×
155
        sails.helpers.formatStructuredError(
156
          req,
157
          'An error occurred while establishing the conversation.',
158
          'E_SERVER_ERROR'
159
        )
160
      );
161
    }
162
  } else {
NEW
163
    return res.badRequest(
×
164
      sails.helpers.formatStructuredError(
165
        req,
166
        'You must provide either a recipientId or a conversationId.',
167
        'E_VALIDATION'
168
      )
169
    );
170
  }
171

172
  // 3. Create the message
173
  try {
7✔
174
    const newMessage = await TMessage.create({
7✔
175
      conversation: finalConversationId,
176
      caverSender: senderId,
177
      body: body.trim(),
178
      dateSent: new Date(),
179
    }).fetch();
180

181
    // Notify recipient (non-blocking)
182
    // Errors are handled inside the service to avoid affecting message creation
183
    NotificationService.notifyMessageRecipient(
7✔
184
      req,
185
      senderId,
186
      finalConversationId
187
    ).catch((err) => {
NEW
188
      sails.log.error('Background notification failed:', err);
×
189
    });
190

191
    return ControllerService.treat(
7✔
192
      req,
193
      null,
194
      MessageService.formatMessage(newMessage),
195
      { controllerMethod: 'MessageController.create' },
196
      res
197
    );
198
  } catch (err) {
NEW
199
    sails.log.error(err);
×
NEW
200
    return res.serverError(
×
201
      sails.helpers.formatStructuredError(
202
        req,
203
        'An error occurred while sending the message.',
204
        'E_SERVER_ERROR'
205
      )
206
    );
207
  }
208
};
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