Skip to content

Commit 6ee3e2f

Browse files
authored
Merge branch 'main' into open-telemetry-tracing
2 parents 9c549fe + 80dd9e9 commit 6ee3e2f

File tree

12 files changed

+260
-25
lines changed

12 files changed

+260
-25
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
branches:
66
- main
77
tags:
8-
- 'v*'
8+
- "v*"
99
workflow_dispatch:
1010
pull_request:
1111

@@ -26,7 +26,7 @@ jobs:
2626
services:
2727
db:
2828
image: postgres:15
29-
ports: ['5432:5432']
29+
ports: ["5432:5432"]
3030
env:
3131
POSTGRES_PASSWORD: postgres
3232
options: >-
@@ -121,6 +121,8 @@ jobs:
121121
- name: Extract metadata (tags, labels) for Docker
122122
id: meta
123123
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
124+
env:
125+
DOCKER_METADATA_PR_HEAD_SHA: true
124126
with:
125127
images: ghcr.io/nerves-hub/nerves-hub
126128
tags: |
@@ -132,14 +134,15 @@ jobs:
132134
type=raw,enable={{is_default_branch}},value=latest
133135
# tag event (eg. "v1.2.3")
134136
type=ref,event=tag
135-
137+
136138
- name: Check if PR publish
139+
continue-on-error: true
137140
if: ${{ github.event_name == 'pull_request' }}
138141
id: pr_publish_check
139142
env:
140143
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
141144
run: |
142-
commits=$(gh pr view ${{ github.head_ref }} --json commits --jq '.commits[] | .messageHeadline + " " + .messageBody')
145+
commits=$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '.commits[] | .messageHeadline + " " + .messageBody')
143146
144147
if [[ $commits =~ \[publish\] ]]; then
145148
echo "true"

assets/css/_custom.scss

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,18 @@
1313
right: 0;
1414
margin: auto;
1515
z-index: 999;
16-
// text-align: center;
16+
}
17+
18+
.alarms-banner {
19+
display: flex;
20+
flex-direction: row;
21+
justify-content: right;
22+
padding-top: 2px;
23+
padding-bottom: 2px;
24+
}
25+
26+
.alarms-banner a {
27+
color: #f78080;
1728
}
1829

1930
.opacity-1 {
@@ -244,19 +255,28 @@
244255
}
245256
}
246257

247-
.device-limit-indicator {
248-
background-image: url("/images/icons/device.svg");
249-
background-position: right center;
258+
.navbar-indicator {
259+
background-position: left center;
250260
background-repeat: no-repeat;
251261
background-size: 1.5rem;
252-
padding-right: 2rem;
253-
letter-spacing: 4px;
262+
padding-left: 1.8rem;
263+
letter-spacing: 2px;
254264

255265
@media(max-width: 860px) {
256266
display: none;
257267
}
258268
}
259269

270+
.device-limit-indicator {
271+
background-image: url("/images/icons/device.svg");
272+
}
273+
274+
.alarms-indicator {
275+
background-image: url("/images/icons/bell.svg");
276+
margin-left: 1.3rem;
277+
font-weight: 400;
278+
}
279+
260280
html {
261281
.btn-group>form:not(:first-child) .btn {
262282
margin-left: -1px;
@@ -345,6 +365,7 @@ html {
345365
cursor: pointer;
346366
display: flex;
347367
gap: 4px;
368+
348369
label {
349370
font-size: 14px;
350371
border-radius: 3.2px;
@@ -360,6 +381,7 @@ html {
360381
padding-right: 8px;
361382
justify-content: center;
362383
flex-grow: 1;
384+
363385
input[type=radio] {
364386
appearance: none;
365387
}

assets/css/_form.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ html {
267267
.filter-form {
268268
&.device-filters {
269269
display: grid;
270-
grid-template-columns: 3fr 3fr 3fr 2fr 2fr;
270+
grid-template-columns: 2fr 2fr 2fr 2fr 3fr;
271271
grid-column-gap: 1rem;
272272

273273
@media (max-width: 860px) {
Lines changed: 5 additions & 0 deletions
Loading

lib/nerves_hub/devices.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule NervesHub.Devices do
1313
alias NervesHub.Deployments.Deployment
1414
alias NervesHub.Deployments.Orchestrator
1515
alias NervesHub.Devices.CACertificate
16+
alias NervesHub.Devices.Alarms
1617
alias NervesHub.Devices.Device
1718
alias NervesHub.Devices.DeviceCertificate
1819
alias NervesHub.Devices.DeviceHealth
@@ -163,6 +164,15 @@ defmodule NervesHub.Devices do
163164
{_, ""} ->
164165
query
165166

167+
{:alarm_status, "with"} ->
168+
where(query, [d], d.id in subquery(Alarms.query_devices_with_alarms()))
169+
170+
{:alarm_status, "without"} ->
171+
where(query, [d], d.id not in subquery(Alarms.query_devices_with_alarms()))
172+
173+
{:alarm, value} ->
174+
where(query, [d], d.id in subquery(Alarms.query_devices_with_alarm(value)))
175+
166176
{:connection, _value} ->
167177
where(query, [d], d.connection_status == ^String.to_atom(value))
168178

lib/nerves_hub/devices/alarms.ex

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
defmodule NervesHub.Devices.Alarms do
2+
import Ecto.Query
3+
alias NervesHub.Repo
4+
alias NervesHub.Devices.Device
5+
alias NervesHub.Devices.DeviceHealth
6+
7+
@doc """
8+
Selects device id:s for devices that has alarm(s) in it's latest health record.
9+
Used when filtering devices.
10+
"""
11+
def query_devices_with_alarms() do
12+
(lr in subquery(latest_row_query()))
13+
|> from()
14+
|> where([lr], lr.rn == 1)
15+
|> where([lr], fragment("?->'alarms' != '{}'", lr.data))
16+
|> join(:inner, [lr], d in Device, on: lr.device_id == d.id)
17+
|> select([lr, o], o.id)
18+
end
19+
20+
@doc """
21+
Selects device id:s for devices that has provided alarm in it's latest health record.
22+
Used when filtering devices.
23+
"""
24+
def query_devices_with_alarm(alarm) do
25+
(lr in subquery(latest_row_query()))
26+
|> from()
27+
|> where([lr], lr.rn == 1)
28+
|> where(
29+
[lr],
30+
fragment(
31+
"EXISTS (SELECT 1 FROM jsonb_each_text(?) WHERE value ILIKE ?)",
32+
lr.data,
33+
^"%#{alarm}%"
34+
)
35+
)
36+
|> join(:inner, [lr], d in Device, on: lr.device_id == d.id)
37+
|> select([lr, o], o.id)
38+
end
39+
40+
@doc """
41+
Creates a list with all current alarm types for a product.
42+
"""
43+
def get_current_alarm_types(product_id) do
44+
query_current_alarms(product_id)
45+
|> Repo.all()
46+
|> Enum.map(fn %{data: data} ->
47+
Map.keys(data["alarms"])
48+
end)
49+
|> List.flatten()
50+
|> Enum.uniq()
51+
|> Enum.map(&String.trim_leading(&1, "Elixir."))
52+
end
53+
54+
@doc """
55+
Counts number of devices currently alarming, within a product.
56+
"""
57+
def current_alarms_count(product_id) do
58+
product_id
59+
|> query_current_alarms()
60+
|> select([a], count(a))
61+
|> Repo.one!()
62+
end
63+
64+
@doc """
65+
Selects latest health per device if alarms is populated and device belongs to product.
66+
"""
67+
def query_current_alarms(product_id) do
68+
(lr in subquery(latest_row_query()))
69+
|> from()
70+
|> where([lr], lr.rn == 1)
71+
|> where([lr], fragment("?->'alarms' != '{}'", lr.data))
72+
|> where([lr], lr.device_id in subquery(device_product_query(product_id)))
73+
end
74+
75+
defp latest_row_query() do
76+
DeviceHealth
77+
|> select([dh], %{
78+
device_id: dh.device_id,
79+
data: dh.data,
80+
inserted_at: dh.inserted_at,
81+
rn: row_number() |> over(partition_by: dh.device_id, order_by: [desc: dh.inserted_at])
82+
})
83+
end
84+
85+
defp device_product_query(product_id) do
86+
Device
87+
|> select([:id])
88+
|> where(product_id: ^product_id)
89+
end
90+
end

lib/nerves_hub_web/channels/device_channel.ex

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ defmodule NervesHubWeb.DeviceChannel do
340340
end
341341

342342
def handle_in("fwup_progress", %{"value" => percent}, %{assigns: %{device: device}} = socket) do
343-
device_internal_broadcast!(device, "fwup_progress", %{percent: percent})
343+
device_internal_broadcast!(socket, device, "fwup_progress", %{percent: percent})
344344

345345
# if this is the first fwup we see, then mark it as an update attempt
346346
if socket.assigns[:update_started?] do
@@ -370,9 +370,11 @@ defmodule NervesHubWeb.DeviceChannel do
370370

371371
{:ok, device} = Devices.update_device(device, %{connection_metadata: metadata})
372372

373-
device_internal_broadcast!(device, "location:updated", location)
373+
socket = assign(socket, :device, device)
374374

375-
{:reply, :ok, assign(socket, :device, device)}
375+
device_internal_broadcast!(socket, device, "location:updated", location)
376+
377+
{:reply, :ok, socket}
376378
end
377379

378380
def handle_in("connection_types", %{"values" => types}, %{assigns: %{device: device}} = socket) do
@@ -434,7 +436,7 @@ defmodule NervesHubWeb.DeviceChannel do
434436
{:health_report, Devices.save_device_health(device_health)},
435437
{:metrics_report, {_, _}} <-
436438
{:metrics_report, Metrics.save_metrics(socket.assigns.device.id, metrics)} do
437-
device_internal_broadcast!(socket.assigns.device, "health_check_report", %{})
439+
device_internal_broadcast!(socket, socket.assigns.device, "health_check_report", %{})
438440
else
439441
{:health_report, {:error, err}} ->
440442
Logger.warning("Failed to save health check data: #{inspect(err)}")
@@ -513,9 +515,9 @@ defmodule NervesHubWeb.DeviceChannel do
513515
Phoenix.PubSub.unsubscribe(NervesHub.PubSub, topic)
514516
end
515517

516-
defp device_internal_broadcast!(device, event, payload) do
518+
defp device_internal_broadcast!(socket, device, event, payload) do
517519
topic = "device:#{device.identifier}:internal"
518-
NervesHubWeb.DeviceEndpoint.broadcast_from!(self(), topic, event, payload)
520+
socket.endpoint.broadcast_from!(self(), topic, event, payload)
519521
end
520522

521523
defp maybe_send_public_keys(device, socket, params) do

lib/nerves_hub_web/components/navigation.ex

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule NervesHubWeb.Components.Navigation do
22
use NervesHubWeb, :component
33

44
alias NervesHub.Devices
5+
alias NervesHub.Devices.Alarms
56
alias NervesHub.Products.Product
67

78
import NervesHubWeb.Components.SimpleActiveLink
@@ -115,6 +116,7 @@ defmodule NervesHubWeb.Components.Navigation do
115116

116117
if Enum.any?(links) do
117118
assigns = %{
119+
org: assigns.org,
118120
product: assigns.product,
119121
links: links
120122
}
@@ -129,8 +131,19 @@ defmodule NervesHubWeb.Components.Navigation do
129131
</.link>
130132
</li>
131133
</ul>
132-
<div :if={device_count = device_count(@product)} class="device-limit-indicator" title="Device total" aria-label="Device total">
133-
<%= device_count %>
134+
<div class="flex-row align-items-center justify-content-between">
135+
<div :if={device_count = device_count(@product)} class="navbar-indicator device-limit-indicator" title="Device total" aria-label="Device total">
136+
<%= device_count %>
137+
</div>
138+
<.link
139+
:if={alarms_count = alarms_count(@product)}
140+
navigate={~p"/org/#{@org.name}/#{@product.name}/devices?alarm_status=with"}
141+
class="navbar-indicator alarms-indicator"
142+
title="Devices alarming"
143+
aria-label="Devices alarming"
144+
>
145+
<%= alarms_count %>
146+
</.link>
134147
</div>
135148
</nav>
136149
</div>
@@ -308,4 +321,7 @@ defmodule NervesHubWeb.Components.Navigation do
308321
def device_count(_conn) do
309322
nil
310323
end
324+
325+
def alarms_count(%Product{} = product), do: Alarms.current_alarms_count(product.id)
326+
def alarms_count(_conn), do: nil
311327
end

lib/nerves_hub_web/live/devices/index.ex

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule NervesHubWeb.Live.Devices.Index do
77

88
alias NervesHub.AuditLogs
99
alias NervesHub.Devices
10+
alias NervesHub.Devices.Alarms
1011
alias NervesHub.Firmwares
1112
alias NervesHub.Products.Product
1213
alias NervesHub.Tracker
@@ -29,7 +30,9 @@ defmodule NervesHubWeb.Live.Devices.Index do
2930
device_id: "",
3031
tag: "",
3132
updates: "",
32-
has_no_tags: false
33+
has_no_tags: false,
34+
alarm_status: "",
35+
alarm: ""
3336
}
3437

3538
@filter_types %{
@@ -41,7 +44,9 @@ defmodule NervesHubWeb.Live.Devices.Index do
4144
device_id: :string,
4245
tag: :string,
4346
updates: :string,
44-
has_no_tags: :boolean
47+
has_no_tags: :boolean,
48+
alarm_status: :string,
49+
alarm: :string
4550
}
4651

4752
@default_page 1
@@ -79,6 +84,7 @@ defmodule NervesHubWeb.Live.Devices.Index do
7984
|> assign(:valid_tags, true)
8085
|> assign(:device_tags, "")
8186
|> assign(:total_entries, 0)
87+
|> assign(:current_alarms, Alarms.get_current_alarm_types(product.id))
8288
|> subscribe_and_refresh_device_list_timer()
8389
|> ok()
8490
end

0 commit comments

Comments
 (0)