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

source-academy / backend / e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

31 Mar 2025 09:12AM UTC coverage: 19.982% (-73.6%) from 93.607%
e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

Pull #1236

github

RichDom2185
Redate migrations to maintain total ordering
Pull Request #1236: Added Exam mode

12 of 57 new or added lines in 8 files covered. (21.05%)

2430 existing lines in 97 files now uncovered.

671 of 3358 relevant lines covered (19.98%)

3.03 hits per line

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

59.68
/lib/cadet_web/controllers/devices_controller.ex
1
defmodule CadetWeb.DevicesController do
2
  use CadetWeb, :controller
3

4
  use PhoenixSwagger
5

6
  alias Cadet.{Devices, DisplayHelper}
7
  alias Cadet.Devices.Device
8

9
  def index(conn, _params) do
UNCOV
10
    render(conn, "index.json",
×
UNCOV
11
      registrations: Devices.get_user_registrations(conn.assigns.current_user)
×
12
    )
13
  end
14

15
  def register(conn, %{"title" => title, "type" => type, "secret" => secret}) do
UNCOV
16
    case Devices.register(title, type, secret, conn.assigns.current_user) do
×
17
      {:ok, registration} ->
UNCOV
18
        render(conn, "show.json", registration: registration)
×
19

20
      {:error, :conflicting_device} ->
UNCOV
21
        send_resp(conn, :bad_request, "There is a device with the same secret but different type")
×
22

23
      {:error, changeset = %Ecto.Changeset{}} ->
UNCOV
24
        send_resp(conn, :bad_request, DisplayHelper.full_error_messages(changeset))
×
25
    end
26
  end
27

28
  def edit(conn, %{"id" => device_id, "title" => title}) do
UNCOV
29
    with {:get_registration, registration} when not is_nil(registration) <-
×
30
           {:get_registration,
UNCOV
31
            Devices.get_user_registration(conn.assigns.current_user, device_id)},
×
UNCOV
32
         {:rename, {:ok, _}} <- {:rename, Devices.rename_registration(registration, title)} do
×
UNCOV
33
      send_resp(conn, :no_content, "")
×
34
    else
35
      {:get_registration, nil} ->
UNCOV
36
        send_resp(conn, :not_found, "Registration not found")
×
37

38
      {:rename, {:error, changeset = %Ecto.Changeset{}}} ->
UNCOV
39
        send_resp(conn, :bad_request, DisplayHelper.full_error_messages(changeset))
×
40
    end
41
  end
42

43
  def deregister(conn, %{"id" => device_id}) do
UNCOV
44
    with {:get_registration, registration} when not is_nil(registration) <-
×
45
           {:get_registration,
UNCOV
46
            Devices.get_user_registration(conn.assigns.current_user, device_id)},
×
UNCOV
47
         {:delete, {:ok, _}} <- {:delete, Devices.delete_registration(registration)} do
×
UNCOV
48
      send_resp(conn, :no_content, "")
×
49
    else
50
      {:get_registration, nil} ->
UNCOV
51
        send_resp(conn, :not_found, "Registration not found")
×
52
    end
53
  end
54

55
  def get_ws_endpoint(conn, %{"id" => device_id}) do
UNCOV
56
    with {:get_registration, registration} when not is_nil(registration) <-
×
57
           {:get_registration,
UNCOV
58
            Devices.get_user_registration(conn.assigns.current_user, device_id)},
×
UNCOV
59
         {:get_ws_endpoint, {:ok, endpoint}} <-
×
60
           {:get_ws_endpoint,
UNCOV
61
            Devices.get_device_ws_endpoint(registration.device, conn.assigns.current_user)} do
×
UNCOV
62
      json(conn, camel_casify_atom_keys(endpoint))
×
63
    else
64
      {:get_registration, nil} ->
UNCOV
65
        send_resp(conn, :not_found, "Registration not found")
×
66

67
      {:get_ws_endpoint, error} ->
UNCOV
68
        send_sentry_error(error)
×
69

UNCOV
70
        send_resp(conn, :internal_server_error, "Upstream AWS error")
×
71
    end
72
  end
73

74
  # The following two handlers are almost identical
75
  # The reason they are separate is so we can avoid the devices having to parse
76
  # JSON
77

78
  def get_cert(conn, %{"secret" => secret}) do
79
    case Devices.get_device_key_cert(secret) do
3✔
80
      {:ok, {_, cert}} ->
81
        text(conn, cert)
1✔
82

83
      {:error, :no_such_device} ->
84
        send_resp(conn, :not_found, "Device not found")
1✔
85

86
      {:error, error} ->
87
        send_sentry_error(error)
1✔
88

89
        send_resp(conn, :internal_server_error, "Upstream AWS error")
1✔
90
    end
91
  end
92

93
  def get_key(conn, %{"secret" => secret}) do
94
    case Devices.get_device_key_cert(secret) do
3✔
95
      {:ok, {key, _}} ->
96
        text(conn, key)
1✔
97

98
      {:error, :no_such_device} ->
99
        send_resp(conn, :not_found, "Device not found")
1✔
100

101
      {:error, error} ->
102
        send_sentry_error(error)
1✔
103

104
        send_resp(conn, :internal_server_error, "Upstream AWS error")
1✔
105
    end
106
  end
107

108
  def get_client_id(conn, %{"secret" => secret}) do
109
    case Devices.get_device(secret) do
2✔
110
      %Device{id: id} -> text(conn, Devices.get_thing_name(id))
1✔
111
      nil -> send_resp(conn, :not_found, "Device not found")
1✔
112
    end
113
  end
114

115
  @spec get_mqtt_endpoint(Plug.Conn.t(), any) :: Plug.Conn.t()
116
  def get_mqtt_endpoint(conn, _params) do
117
    # we have the secret but we don't check it currently
118
    {:ok, endpoint} = Devices.get_endpoint_address()
1✔
119
    text(conn, endpoint)
1✔
120
  end
121

122
  swagger_path :index do
1✔
123
    get("/devices")
124

125
    summary("Returns the devices registered by the user")
126

127
    security([%{JWT: []}])
128

129
    produces("application/json")
130

131
    response(200, "OK", Schema.array(:Device))
132
    response(401, "Unauthorised")
133
  end
134

135
  swagger_path :register do
1✔
136
    post("/devices")
137

138
    summary("Registers a new device")
139

140
    security([%{JWT: []}])
141

142
    consumes("application/json")
143
    produces("application/json")
144

145
    parameters do
146
      device(
147
        :body,
148
        Schema.ref(:RegisterDevicePayload),
149
        "Device details",
150
        required: true
151
      )
152
    end
153

154
    response(200, "OK", Schema.ref(:Device))
155
    response(400, "Conflicting device type or missing or invalid parameters")
156
    response(401, "Unauthorised")
157
  end
158

159
  swagger_path :edit do
1✔
160
    post("/devices/{id}")
161

162
    summary("Edits the given device")
163

164
    security([%{JWT: []}])
165

166
    consumes("application/json")
167

168
    parameters do
169
      id(:path, :integer, "Device ID", required: true)
170
      device(:body, Schema.ref(:EditDevicePayload), "Device details", required: true)
171
    end
172

173
    response(204, "OK")
174
    response(400, "Missing or invalid parameters")
175
    response(401, "Unauthorised")
176
    response(404, "Device not found")
177
  end
178

179
  swagger_path :deregister do
1✔
180
    PhoenixSwagger.Path.delete("/devices/{id}")
181

182
    summary("Unregisters the given device")
183

184
    security([%{JWT: []}])
185

186
    consumes("application/json")
187

188
    parameters do
189
      id(:path, :integer, "Device ID", required: true)
190
    end
191

192
    response(204, "OK")
193
    response(401, "Unauthorised")
194
    response(404, "Device not found")
195
  end
196

197
  swagger_path :get_ws_endpoint do
1✔
198
    get("/devices/{id}/ws_endpoint")
199

200
    summary("Generates a WebSocket endpoint URL for the given device")
201

202
    security([%{JWT: []}])
203

204
    produces("application/json")
205

206
    parameters do
207
      id(:path, :integer, "Device ID", required: true)
208
    end
209

210
    response(200, "OK", Schema.ref(:WebSocketEndpoint))
211
    response(401, "Unauthorised")
212
    response(404, "Device not found")
213
  end
214

215
  swagger_path :get_cert do
1✔
216
    get("/devices/{secret}/cert")
217

218
    summary("Returns the device's PEM-encoded client certificate")
219

220
    produces("text/plain")
221

222
    parameters do
223
      secret(:path, :string, "Device secret", required: true)
224
    end
225

226
    response(200, "OK", %PhoenixSwagger.Schema{type: :string})
227
    response(404, "Device not found")
228
  end
229

230
  swagger_path :get_key do
1✔
231
    get("/devices/{secret}/key")
232

233
    summary("Returns the device's PEM-encoded client key")
234

235
    produces("text/plain")
236

237
    parameters do
238
      secret(:path, :string, "Device secret", required: true)
239
    end
240

241
    response(200, "OK", %PhoenixSwagger.Schema{type: :string})
242
    response(404, "Device not found")
243
  end
244

245
  swagger_path :get_client_id do
1✔
246
    get("/devices/{secret}/client_id")
247

248
    summary("Returns the device's MQTT client ID")
249

250
    produces("text/plain")
251

252
    parameters do
253
      secret(:path, :string, "Device secret", required: true)
254
    end
255

256
    response(200, "OK", %PhoenixSwagger.Schema{type: :string})
257
    response(404, "Device not found")
258
  end
259

260
  swagger_path :get_mqtt_endpoint do
1✔
261
    get("/devices/{secret}/mqtt_endpoint")
262

263
    summary("Returns the MQTT endpoint the device should connect to")
264

265
    produces("text/plain")
266

267
    parameters do
268
      secret(:path, :string, "Device secret", required: true)
269
    end
270

271
    response(200, "OK", %PhoenixSwagger.Schema{type: :string})
272
    response(404, "Device not found")
273
  end
274

275
  def swagger_definitions do
276
    %{
1✔
277
      Device:
278
        swagger_schema do
1✔
279
          properties do
1✔
280
            id(:integer, "Device ID (unique to user)", required: true)
281
            type(:string, "User type", required: true)
282
            title(:string, "User-given device title", required: true)
283
            secret(:string, "Device unique secret", required: true)
1✔
284
          end
285
        end,
286
      WebSocketEndpoint:
287
        swagger_schema do
1✔
288
          properties do
1✔
289
            endpoint(:string, "Endpoint URL", required: true)
290
            clientNamePrefix(:string, "Client name prefix to use", required: true)
291
            thingName(:string, "Device name", required: true)
1✔
292
          end
293
        end,
294

295
      # Schemas for payloads to modify data
296
      RegisterDevicePayload:
297
        swagger_schema do
1✔
298
          properties do
1✔
299
            type(:string, "User type", required: true)
300
            title(:string, "User-given device title", required: true)
301
            secret(:string, "Device unique secret", required: true)
1✔
302
          end
303
        end,
304
      EditDevicePayload:
305
        swagger_schema do
1✔
306
          properties do
1✔
307
            title(:string, "User-given device title", required: true)
1✔
308
          end
309
        end
310
    }
311
  end
312
end
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