Skip to content

Commit 5339c49

Browse files
committed
refactor: campaign subscriptions
- remove `noop` action on campaign resource that was used to trigger subscriptions when campaign target was updated - add subscriptions by campaign to campaign targets - update the frontend accordingly Signed-off-by: ArnelaL <arnela.lisic@secomind.com>
1 parent 0f09b2e commit 5339c49

9 files changed

Lines changed: 501 additions & 375 deletions

File tree

backend/lib/edgehog/campaigns/campaign/campaign.ex

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,6 @@ defmodule Edgehog.Campaigns.Campaign do
180180
change Changes.StartExecution
181181
end
182182

183-
update :trigger_subscription do
184-
description """
185-
This is a nop action used only to trigger subscriptions.
186-
"""
187-
end
188-
189183
destroy :destroy do
190184
description "Deletes a Campaign"
191185
primary? true

backend/lib/edgehog/campaigns/campaign_target/campaign_target.ex

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
3535
alias Edgehog.Campaigns.CampaignMechanism.FileDownload
3636
alias Edgehog.Campaigns.CampaignTarget
3737
alias Edgehog.Campaigns.CampaignTarget.Changes
38-
alias Edgehog.Campaigns.CampaignTarget.Changes.TriggerCampaignSubscription
3938
alias Edgehog.Containers.Release
4039
alias Edgehog.Files.File
4140

@@ -45,11 +44,27 @@ defmodule Edgehog.Campaigns.CampaignTarget do
4544

4645
graphql do
4746
type :campaign_target
47+
48+
subscriptions do
49+
pubsub EdgehogWeb.Endpoint
50+
51+
subscribe :campaign_targets_by_campaign do
52+
action_types [:create, :update]
53+
read_action :read_by_campaign
54+
relay_id_translations campaign_id: :campaign
55+
end
56+
end
4857
end
4958

5059
actions do
5160
defaults [:read]
5261

62+
read :read_by_campaign do
63+
argument :campaign_id, :uuid, allow_nil?: false
64+
65+
get_by :campaign_id
66+
end
67+
5368
read :next_valid_target do
5469
description """
5570
Returns the next valid target.
@@ -176,7 +191,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
176191
require_atomic? false
177192

178193
change set_attribute(:status, :in_progress)
179-
change TriggerCampaignSubscription
180194
end
181195

182196
update :mark_as_failed do
@@ -188,7 +202,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
188202

189203
change set_attribute(:completion_timestamp, arg(:completion_timestamp))
190204
change set_attribute(:status, :failed)
191-
change TriggerCampaignSubscription
192205
end
193206

194207
update :mark_as_successful do
@@ -200,7 +213,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
200213

201214
change set_attribute(:completion_timestamp, arg(:completion_timestamp))
202215
change set_attribute(:status, :successful)
203-
change TriggerCampaignSubscription
204216
end
205217

206218
update :increase_retry_count do
@@ -211,8 +223,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
211223
require_atomic? false
212224

213225
accept [:latest_attempt]
214-
215-
change TriggerCampaignSubscription
216226
end
217227

218228
# Deployment related updates
@@ -234,7 +244,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
234244

235245
change set_attribute(:status, :in_progress)
236246
change Changes.LinkDeployment
237-
change TriggerCampaignSubscription
238247
end
239248

240249
update :set_deployment do
@@ -248,7 +257,6 @@ defmodule Edgehog.Campaigns.CampaignTarget do
248257
require_atomic? false
249258

250259
change set_attribute(:status, :in_progress)
251-
change TriggerCampaignSubscription
252260
end
253261

254262
# Firmware upgrade related updates

backend/lib/edgehog/campaigns/campaign_target/changes/trigger_campaign_subscription.ex

Lines changed: 0 additions & 36 deletions
This file was deleted.

backend/test/edgehog_web/schema/subscriptions/campaign/campaign_subscriptions_test.exs

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -249,63 +249,6 @@ defmodule EdgehogWeb.Schema.Subscriptions.Campaign.CampaignSubscriptionsTest do
249249

250250
refute_push "subscription:data", _payload, 100
251251
end
252-
253-
test "receives data on campaign target update", %{
254-
socket: socket,
255-
tenant: tenant
256-
} do
257-
campaign =
258-
1
259-
|> campaign_with_targets_fixture(
260-
tenant: tenant,
261-
mechanism_type: :deployment_deploy
262-
)
263-
|> Ash.load!(:campaign_targets)
264-
265-
[campaign_target] = campaign.campaign_targets
266-
267-
assert campaign_target.status == :idle
268-
269-
campaign_updated_query = """
270-
subscription CampaignUpdated($id: ID!) {
271-
campaign(id: $id) {
272-
updated {
273-
id
274-
name
275-
status
276-
campaignTargets{
277-
edges{
278-
node {
279-
id
280-
status
281-
}
282-
}
283-
}
284-
}
285-
}
286-
}
287-
"""
288-
289-
subscribe(
290-
socket,
291-
query: campaign_updated_query,
292-
variables: %{
293-
"id" => AshGraphql.Resource.encode_relay_id(campaign)
294-
}
295-
)
296-
297-
Campaigns.mark_target_as_successful!(campaign_target, tenant: tenant)
298-
299-
assert_push "subscription:data", push
300-
301-
assert_updated("campaign", campaign_data, push)
302-
303-
assert campaign_data["id"] == AshGraphql.Resource.encode_relay_id(campaign)
304-
305-
[%{"node" => campaign_target}] = campaign_data["campaignTargets"]["edges"]
306-
307-
assert campaign_target["status"] == "SUCCESSFUL"
308-
end
309252
end
310253

311254
defp subscribe(socket, opts \\ []) do
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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.Campaign.CampaignTargetSubscriptionsTest do
22+
@moduledoc false
23+
use EdgehogWeb.SubsCase
24+
25+
import Edgehog.CampaignsFixtures
26+
27+
alias Edgehog.Campaigns
28+
29+
test "receive data on campaign target update for a specific campaign", %{
30+
socket: socket,
31+
tenant: tenant
32+
} do
33+
campaign =
34+
campaign_with_targets_fixture(2, tenant: tenant, mechanism_type: :deployment_deploy)
35+
36+
[campaign_target] =
37+
campaign
38+
|> Ash.load!(:campaign_targets)
39+
|> Map.get(:campaign_targets, [])
40+
|> Enum.take(1)
41+
42+
subscribe(socket,
43+
variables: %{
44+
"campaignId" => AshGraphql.Resource.encode_relay_id(campaign)
45+
}
46+
)
47+
48+
Campaigns.mark_target_as_successful(campaign_target)
49+
50+
assert_push "subscription:data", push
51+
assert_updated("campaignTargetsByCampaign", campaign_targets_data, push)
52+
53+
assert campaign_targets_data["id"] == AshGraphql.Resource.encode_relay_id(campaign_target)
54+
end
55+
56+
test "receives multiple updates for the same campaign", %{
57+
socket: socket,
58+
tenant: tenant
59+
} do
60+
campaign =
61+
campaign_with_targets_fixture(2, tenant: tenant, mechanism_type: :deployment_deploy)
62+
63+
[t1, t2] =
64+
campaign
65+
|> Ash.load!(:campaign_targets)
66+
|> Map.get(:campaign_targets)
67+
68+
subscribe(socket,
69+
variables: %{
70+
"campaignId" => AshGraphql.Resource.encode_relay_id(campaign)
71+
}
72+
)
73+
74+
Campaigns.mark_target_as_successful(t1)
75+
assert_push "subscription:data", _
76+
77+
Campaigns.mark_target_as_successful(t2)
78+
assert_push "subscription:data", _
79+
end
80+
81+
test "payload contains expected fields", %{
82+
socket: socket,
83+
tenant: tenant
84+
} do
85+
campaign =
86+
campaign_with_targets_fixture(1, tenant: tenant, mechanism_type: :deployment_deploy)
87+
88+
[target] =
89+
campaign
90+
|> Ash.load!(:campaign_targets)
91+
|> Map.get(:campaign_targets)
92+
93+
subscribe(socket,
94+
variables: %{
95+
"campaignId" => AshGraphql.Resource.encode_relay_id(campaign)
96+
}
97+
)
98+
99+
Campaigns.mark_target_as_successful(target)
100+
101+
assert_push "subscription:data", push
102+
assert_updated("campaignTargetsByCampaign", data, push)
103+
104+
assert Map.has_key?(data, "id")
105+
assert Map.has_key?(data, "status")
106+
assert Map.has_key?(data, "device")
107+
end
108+
109+
test "does not receive updates for a different campaign", %{
110+
socket: socket,
111+
tenant: tenant
112+
} do
113+
campaign1 =
114+
campaign_with_targets_fixture(1, tenant: tenant, mechanism_type: :deployment_deploy)
115+
116+
campaign2 =
117+
campaign_with_targets_fixture(1, tenant: tenant, mechanism_type: :deployment_deploy)
118+
119+
[target1] =
120+
campaign1
121+
|> Ash.load!(:campaign_targets)
122+
|> Map.get(:campaign_targets)
123+
124+
[target2] =
125+
campaign2
126+
|> Ash.load!(:campaign_targets)
127+
|> Map.get(:campaign_targets)
128+
129+
subscribe(socket,
130+
variables: %{
131+
"campaignId" => AshGraphql.Resource.encode_relay_id(campaign1)
132+
}
133+
)
134+
135+
Campaigns.mark_target_as_successful(target2)
136+
137+
refute_push "subscription:data", _
138+
139+
Campaigns.mark_target_as_successful(target1)
140+
141+
assert_push "subscription:data", _
142+
end
143+
144+
defp subscribe(socket, opts) do
145+
default_sub_gql = """
146+
subscription($campaignId: ID!) {
147+
campaignTargetsByCampaign(campaignId: $campaignId) {
148+
updated {
149+
id
150+
status
151+
device {
152+
id
153+
name
154+
}
155+
}
156+
}
157+
}
158+
"""
159+
160+
sub_gql = Keyword.get(opts, :query, default_sub_gql)
161+
variables = Keyword.get(opts, :variables, %{})
162+
163+
ref = push_doc(socket, sub_gql, variables: variables)
164+
assert_reply ref, :ok, %{subscriptionId: subscription_id}
165+
166+
subscription_id
167+
end
168+
end

0 commit comments

Comments
 (0)