Skip to content

Commit db679ce

Browse files
authored
feat: add GraphQL subscriptions for OTA operations (edgehog-device-manager#1220)
- add GraphQL subscription for OTA operation updates - add test for OTA operation subscription Signed-off-by: Osman Hadzic <osman.hadzic@secomind.com>
1 parent 7d46654 commit db679ce

2 files changed

Lines changed: 200 additions & 0 deletions

File tree

backend/lib/edgehog/os_management/ota_operation/ota_operation.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ defmodule Edgehog.OSManagement.OTAOperation do
4444
graphql do
4545
type :ota_operation
4646

47+
subscriptions do
48+
pubsub EdgehogWeb.Endpoint
49+
50+
subscribe :ota_operation do
51+
action_types [:create, :update, :destroy]
52+
end
53+
end
54+
4755
field_names inserted_at: :created_at
4856
end
4957

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#
2+
# This file is part of Edgehog.
3+
#
4+
# Copyright 2026 SECO Mind Srl
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0
19+
#
20+
21+
defmodule EdgehogWeb.Schema.Subscriptions.OSManagement.OTAOperationSubscriptionsTest do
22+
@moduledoc false
23+
use EdgehogWeb.SubsCase
24+
25+
import Edgehog.OSManagementFixtures
26+
27+
describe "OTAOperation subscriptions" do
28+
test "receive data on createManaged", %{socket: socket, tenant: tenant} do
29+
subscribe(socket, query: created_query())
30+
31+
ota_operation = managed_ota_operation_fixture(tenant: tenant)
32+
33+
assert_push "subscription:data", push
34+
assert_created "otaOperation", ota_operation_data, push
35+
36+
assert ota_operation_data["id"] == AshGraphql.Resource.encode_relay_id(ota_operation)
37+
38+
assert ota_operation_data["status"] ==
39+
ota_operation.status
40+
|> to_string()
41+
|> String.upcase()
42+
43+
assert ota_operation_data["baseImageUrl"] == ota_operation.base_image_url
44+
assert ota_operation_data["statusProgress"] == ota_operation.status_progress
45+
assert ota_operation_data["statusCode"] == ota_operation.status_code
46+
assert ota_operation_data["message"] == ota_operation.message
47+
assert ota_operation_data["createdAt"] == DateTime.to_iso8601(ota_operation.inserted_at)
48+
assert ota_operation_data["updatedAt"] == DateTime.to_iso8601(ota_operation.updated_at)
49+
end
50+
51+
test "receive data on manual", %{socket: socket, tenant: tenant} do
52+
subscribe(socket, query: created_query())
53+
54+
ota_operation = manual_ota_operation_fixture(tenant: tenant)
55+
56+
assert_push "subscription:data", push
57+
assert_created "otaOperation", ota_operation_data, push
58+
59+
assert ota_operation_data["id"] == AshGraphql.Resource.encode_relay_id(ota_operation)
60+
61+
assert ota_operation_data["status"] ==
62+
ota_operation.status
63+
|> to_string()
64+
|> String.upcase()
65+
66+
assert ota_operation_data["baseImageUrl"] == ota_operation.base_image_url
67+
assert ota_operation_data["statusProgress"] == ota_operation.status_progress
68+
assert ota_operation_data["statusCode"] == ota_operation.status_code
69+
assert ota_operation_data["message"] == ota_operation.message
70+
assert ota_operation_data["createdAt"] == DateTime.to_iso8601(ota_operation.inserted_at)
71+
assert ota_operation_data["updatedAt"] == DateTime.to_iso8601(ota_operation.updated_at)
72+
end
73+
74+
test "receive data on updateStatus", %{socket: socket, tenant: tenant} do
75+
ota_operation = managed_ota_operation_fixture(tenant: tenant)
76+
subscribe(socket, query: updated_query())
77+
78+
ota_operation =
79+
ota_operation
80+
|> Ash.Changeset.for_update(:update_status, %{status: :success})
81+
|> Ash.update!(tenant: tenant)
82+
83+
assert_push "subscription:data", push, 500
84+
assert_updated "otaOperation", ota_operation_data, push
85+
86+
assert ota_operation_data["id"] == AshGraphql.Resource.encode_relay_id(ota_operation)
87+
assert ota_operation_data["status"] == "SUCCESS"
88+
assert ota_operation_data["baseImageUrl"] == ota_operation.base_image_url
89+
assert ota_operation_data["statusProgress"] == ota_operation.status_progress
90+
assert ota_operation_data["statusCode"] == ota_operation.status_code
91+
assert ota_operation_data["message"] == ota_operation.message
92+
assert ota_operation_data["createdAt"] == DateTime.to_iso8601(ota_operation.inserted_at)
93+
assert ota_operation_data["updatedAt"] == DateTime.to_iso8601(ota_operation.updated_at)
94+
end
95+
96+
test "receive data on markAsTimedOut", %{socket: socket, tenant: tenant} do
97+
ota_operation = managed_ota_operation_fixture(tenant: tenant, status: :failure)
98+
subscribe(socket, query: updated_query())
99+
100+
ota_operation =
101+
ota_operation
102+
|> Ash.Changeset.for_update(:mark_as_timed_out, %{})
103+
|> Ash.update!(tenant: tenant)
104+
105+
assert_push "subscription:data", push, 500
106+
assert_updated "otaOperation", ota_operation_data, push
107+
108+
assert ota_operation_data["id"] == AshGraphql.Resource.encode_relay_id(ota_operation)
109+
assert ota_operation_data["status"] == "FAILURE"
110+
assert ota_operation_data["baseImageUrl"] == ota_operation.base_image_url
111+
assert ota_operation_data["statusProgress"] == ota_operation.status_progress
112+
113+
assert ota_operation_data["statusCode"] ==
114+
ota_operation.status_code
115+
|> to_string()
116+
|> String.upcase()
117+
118+
assert ota_operation_data["message"] == ota_operation.message
119+
assert ota_operation_data["createdAt"] == DateTime.to_iso8601(ota_operation.inserted_at)
120+
assert ota_operation_data["updatedAt"] == DateTime.to_iso8601(ota_operation.updated_at)
121+
end
122+
123+
test "receive data on destroy", %{socket: socket, tenant: tenant} do
124+
ota_operation = managed_ota_operation_fixture(tenant: tenant)
125+
subscribe(socket, query: destroyed_query())
126+
127+
Ash.destroy!(ota_operation, action: :destroy, tenant: tenant)
128+
129+
assert_push "subscription:data", push
130+
assert_destroyed("otaOperation", ota_operation_id, push)
131+
132+
assert ota_operation_id == AshGraphql.Resource.encode_relay_id(ota_operation)
133+
end
134+
end
135+
136+
defp subscribe(socket, opts) do
137+
query = Keyword.fetch!(opts, :query)
138+
139+
ref = push_doc(socket, query)
140+
assert_reply ref, :ok, %{subscriptionId: subscription_id}
141+
142+
subscription_id
143+
end
144+
145+
defp created_query do
146+
"""
147+
subscription {
148+
otaOperation {
149+
created {
150+
id
151+
status
152+
baseImageUrl
153+
statusProgress
154+
statusCode
155+
message
156+
createdAt
157+
updatedAt
158+
}
159+
}
160+
}
161+
"""
162+
end
163+
164+
defp updated_query do
165+
"""
166+
subscription {
167+
otaOperation {
168+
updated {
169+
id
170+
status
171+
baseImageUrl
172+
statusProgress
173+
statusCode
174+
message
175+
createdAt
176+
updatedAt
177+
}
178+
}
179+
}
180+
"""
181+
end
182+
183+
defp destroyed_query do
184+
"""
185+
subscription {
186+
otaOperation {
187+
destroyed
188+
}
189+
}
190+
"""
191+
end
192+
end

0 commit comments

Comments
 (0)