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

mendersoftware / gui / 1201121080

05 Mar 2024 08:10AM UTC coverage: 83.549% (-16.4%) from 99.964%
1201121080

Pull #4348

gitlab-ci

mineralsfree
fix: Download log button failing to download log file

Ticket: MEN-7029
Changelog: None

Signed-off-by: Mikita Pilinka <mikita.pilinka@northern.tech>
Pull Request #4348: MEN-7029: Download log button failing to download file

4431 of 6324 branches covered (70.07%)

10 of 11 new or added lines in 1 file covered. (90.91%)

1639 existing lines in 162 files now uncovered.

8405 of 10060 relevant lines covered (83.55%)

140.67 hits per line

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

24.14
/src/js/utils/sockethook.js
1
// Copyright 2022 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import { useCallback, useRef, useState } from 'react';
15

16
import msgpack5 from 'msgpack5';
17
import Cookies from 'universal-cookie';
18

19
import GeneralApi, { apiUrl } from '../api/general-api';
20
import { TIMEOUTS } from '../constants/appConstants';
21
import { DEVICE_MESSAGE_PROTOCOLS as MessageProtocols, DEVICE_MESSAGE_TYPES as MessageTypes } from '../constants/deviceConstants';
22
import { extractErrorMessage } from '../helpers.js';
23

24
const cookies = new Cookies();
15✔
25

26
const MessagePack = msgpack5();
15✔
27

28
export const byteArrayToString = body => String.fromCharCode(...body);
15✔
29

30
export const blobToString = blob =>
15✔
UNCOV
31
  new Promise(resolve => {
×
UNCOV
32
    let fr = new FileReader();
×
UNCOV
33
    fr.onload = () => {
×
UNCOV
34
      resolve(fr.result);
×
35
    };
UNCOV
36
    fr.readAsArrayBuffer(blob);
×
37
  });
38

39
export const useSession = ({ onClose, onHealthCheckFailed, onMessageReceived, onNotify, onOpen, token }) => {
15✔
40
  const [sessionId, setSessionId] = useState();
6✔
41
  const healthcheckTimeout = useRef();
6✔
42
  const socketRef = useRef();
6✔
43
  const sendMessage = useCallback(({ typ, body, props }) => {
6✔
44
    if (!socketRef.current) {
4!
45
      return;
4✔
46
    }
UNCOV
47
    const proto_header = { proto: MessageProtocols.Shell, typ, sid: socketRef.current.sessionId, props };
×
UNCOV
48
    const encodedData = MessagePack.encode({ hdr: proto_header, body });
×
UNCOV
49
    socketRef.current.send(encodedData);
×
50
  }, []);
51

52
  const close = useCallback(() => {
6✔
53
    if (!socketRef.current || socketRef.current?.readyState !== WebSocket.OPEN) {
5!
54
      return;
5✔
55
    }
UNCOV
56
    sendMessage({ typ: MessageTypes.Stop, body: {}, props: {} });
×
UNCOV
57
    socketRef.current.close();
×
UNCOV
58
    setSessionId();
×
59
  }, [sendMessage]);
60

61
  const healthcheckFailed = useCallback(() => {
6✔
UNCOV
62
    onHealthCheckFailed();
×
UNCOV
63
    close();
×
64
  }, [close, onHealthCheckFailed]);
65

66
  const onSocketMessage = useCallback(
6✔
67
    event =>
UNCOV
68
      blobToString(event.data).then(data => {
×
69
        const {
70
          hdr: { props = {}, proto, sid, typ },
×
71
          body
UNCOV
72
        } = MessagePack.decode(data);
×
UNCOV
73
        if (proto !== MessageProtocols.Shell) {
×
UNCOV
74
          return;
×
75
        }
UNCOV
76
        switch (typ) {
×
77
          case MessageTypes.New: {
UNCOV
78
            if (props.status == WebSocket.CLOSING) {
×
UNCOV
79
              onNotify(`Error: ${byteArrayToString(body)}`);
×
UNCOV
80
              setSessionId();
×
UNCOV
81
              return close();
×
82
            } else {
UNCOV
83
              clearTimeout(healthcheckTimeout.current);
×
UNCOV
84
              healthcheckTimeout.current = setTimeout(healthcheckFailed, 65 * TIMEOUTS.oneSecond);
×
UNCOV
85
              socketRef.current.sessionId = sid;
×
UNCOV
86
              return setSessionId(sid);
×
87
            }
88
          }
89
          case MessageTypes.Shell:
UNCOV
90
            return onMessageReceived(body);
×
91
          case MessageTypes.Stop:
UNCOV
92
            return close();
×
93
          case MessageTypes.Ping: {
UNCOV
94
            if (healthcheckTimeout.current) {
×
UNCOV
95
              clearTimeout(healthcheckTimeout.current);
×
96
            }
UNCOV
97
            sendMessage({ typ: MessageTypes.Pong });
×
98
            //
UNCOV
99
            const timeout = parseInt(props.timeout);
×
UNCOV
100
            if (timeout > 0) {
×
UNCOV
101
              healthcheckTimeout.current = setTimeout(healthcheckFailed, timeout * TIMEOUTS.oneSecond);
×
102
            }
UNCOV
103
            return;
×
104
          }
105
          default:
UNCOV
106
            break;
×
107
        }
108
      }),
109
    [close, healthcheckFailed, onMessageReceived, onNotify, sendMessage]
110
  );
111

112
  const onSocketError = useCallback(
6✔
113
    error => {
UNCOV
114
      onNotify(`WebSocket error: ${error.message}`);
×
UNCOV
115
      close();
×
UNCOV
116
      clearTimeout(healthcheckTimeout.current);
×
117
    },
118
    [close, onNotify]
119
  );
120

121
  const onSocketOpen = useCallback(() => {
6✔
UNCOV
122
    sendMessage({ typ: MessageTypes.New, props: {} });
×
UNCOV
123
    onOpen(true);
×
124
  }, [onOpen, sendMessage]);
125

126
  const onSocketClose = useCallback(
6✔
127
    e => {
UNCOV
128
      console.log('closing');
×
UNCOV
129
      onClose(e);
×
UNCOV
130
      close();
×
131
    },
132
    [close, onClose]
133
  );
134

135
  const connect = useCallback(
6✔
136
    async deviceId => {
UNCOV
137
      const uri = `${apiUrl.v1}/deviceconnect/devices/${deviceId}/connect`;
×
UNCOV
138
      setSessionId();
×
UNCOV
139
      cookies.set('JWT', token, { path: '/', secure: true, sameSite: 'strict', maxAge: 5 });
×
140

UNCOV
141
      let connectionForbidden = false;
×
142
      /**
143
       * [MEN-6890] Send an HTTP get request to verify if the status is forbidden or not.
144
       * This is needed because in the case of a forbidden request, the backend won't follow the websocket handshake process
145
       * and closes the connection abnormally (from the websockets point of view).
146
       */
UNCOV
147
      await GeneralApi.get(uri).catch(error => {
×
UNCOV
148
        if (error?.response?.status === 403) {
×
UNCOV
149
          onNotify(`Connection error: ${extractErrorMessage(error)}`);
×
UNCOV
150
          connectionForbidden = true;
×
151
        }
152
      });
153

UNCOV
154
      try {
×
UNCOV
155
        if (!connectionForbidden) {
×
UNCOV
156
          socketRef.current = new WebSocket(`wss://${window.location.host}${uri}`);
×
UNCOV
157
          socketRef.current.addEventListener('close', onSocketClose);
×
UNCOV
158
          socketRef.current.addEventListener('error', onSocketError);
×
UNCOV
159
          socketRef.current.addEventListener('message', onSocketMessage);
×
UNCOV
160
          socketRef.current.addEventListener('open', onSocketOpen);
×
161
        }
162
      } catch (error) {
UNCOV
163
        console.log(error);
×
164
      }
UNCOV
165
      return () => {
×
UNCOV
166
        if (socketRef.current) {
×
UNCOV
167
          socketRef.current.removeEventListener('close', onSocketClose);
×
UNCOV
168
          socketRef.current.removeEventListener('error', onSocketError);
×
UNCOV
169
          socketRef.current.removeEventListener('message', onSocketMessage);
×
UNCOV
170
          socketRef.current.removeEventListener('open', onSocketOpen);
×
171
        }
172
      };
173
    },
174
    [onSocketClose, onSocketOpen, onSocketError, onSocketMessage, token, onNotify]
175
  );
176

177
  return [connect, sendMessage, close, socketRef.current?.readyState ?? WebSocket.CLOSED, sessionId];
6✔
178
};
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