Skip to content

Commit

Permalink
device: add device_groups relationship
Browse files Browse the repository at this point in the history
Allow retrieving the device groups a device belongs to

Signed-off-by: Riccardo Binetti <[email protected]>
  • Loading branch information
rbino committed Apr 9, 2024
1 parent 5aa025f commit 442275c
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
7 changes: 7 additions & 0 deletions backend/lib/edgehog/devices/device/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ defmodule Edgehog.Devices.Device do
api Edgehog.Labeling
through Edgehog.Labeling.DeviceTag
end

has_many :device_groups, Edgehog.Groups.DeviceGroup do
description "The groups the device belongs to."
writable? false
api Edgehog.Groups
manual ManualRelationships.DeviceGroups
end
end

calculations do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
# This file is part of Edgehog.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

defmodule Edgehog.Devices.Device.ManualRelationships.DeviceGroups do
use Ash.Resource.ManualRelationship
require Ash.Query

alias Edgehog.Devices.Device

@impl true
def load(devices, _opts, %{query: group_query}) do
device_ids = Enum.map(devices, & &1.id)

filtered_devices_query =
Device
|> Ash.Query.select([:id])
|> Ash.Query.filter(id in ^device_ids)

# This produces a `%{device_id1 => [group1, group2], device_id2 => [group2, group3], ...}` map
device_id_to_groups =
group_query
|> Ash.Query.load(devices: filtered_devices_query)
|> Ash.read!()
|> Enum.flat_map(&Enum.map(&1.devices, fn device -> {device.id, &1} end))
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|> Map.new()
|> Map.take(device_ids)

{:ok, device_id_to_groups}
end
end
62 changes: 62 additions & 0 deletions backend/test/edgehog_web/schema/query/device_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,68 @@ defmodule EdgehogWeb.Schema.Query.DeviceTest do
end
end

describe "device groups field" do
import Edgehog.GroupsFixtures

setup %{tenant: tenant} do
fixture = device_fixture(tenant: tenant)

device_id = fixture.device_id

id = AshGraphql.Resource.encode_relay_id(fixture)

document = """
query ($id: ID!) {
device(id: $id) {
deviceGroups {
name
}
}
}
"""

%{device: fixture, device_id: device_id, tenant: tenant, id: id, document: document}
end

test "is empty with no groups", ctx do
%{tenant: tenant, id: id, device_id: device_id, document: document} = ctx

assert %{"deviceGroups" => []} =
device_query(document: document, tenant: tenant, id: id)
|> extract_result!()
end

test "returns matching group", ctx do
%{tenant: tenant, id: id, device_id: device_id, device: device, document: document} = ctx

_device_with_tag =
device
|> add_tags(["foo"])

_matching_group =
device_group_fixture(tenant: tenant, name: "foos", selector: ~s<"foo" in tags>)

assert %{"deviceGroups" => [%{"name" => "foos"}]} =
device_query(document: document, tenant: tenant, id: id)
|> extract_result!()
end

test "doesn't return non matching group", ctx do
%{tenant: tenant, id: id, device_id: device_id, device: device, document: document} = ctx

_device_with_tag =
device
|> add_tags(["foo"])

_non_matching_group =
device_group_fixture(tenant: tenant, name: "foos", selector: ~s<"bar" in tags>)

assert %{"deviceGroups" => []} =
device_query(document: document, tenant: tenant, id: id)
|> extract_result!()
end
end

defp non_existing_device_id(tenant) do
fixture = device_fixture(tenant: tenant)
id = AshGraphql.Resource.encode_relay_id(fixture)
Expand Down
82 changes: 82 additions & 0 deletions backend/test/edgehog_web/schema/query/devices_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,88 @@ defmodule EdgehogWeb.Schema.Query.DevicesTest do
end
end

describe "device groups field" do
import Edgehog.GroupsFixtures

setup %{tenant: tenant} do
fixture_1 = device_fixture(tenant: tenant)
device_id_1 = fixture_1.device_id
fixture_2 = device_fixture(tenant: tenant)
device_id_2 = fixture_2.device_id

document = """
query {
devices {
deviceId
deviceGroups {
name
}
}
}
"""

%{
device_1: fixture_1,
device_id_1: device_id_1,
device_2: fixture_2,
device_id_2: device_id_2,
tenant: tenant,
document: document
}
end

test "is empty with no groups", ctx do
%{tenant: tenant, document: document} = ctx

devices =
devices_query(document: document, tenant: tenant)
|> extract_result!()

Enum.each(devices, &assert(&1["deviceGroups"] == []))
end

test "returns matching groups", ctx do
%{
tenant: tenant,
device_1: device_1,
device_id_1: device_id_1,
device_2: device_2,
device_id_2: device_id_2,
document: document
} = ctx

_device_1_with_tags =
device_1
|> add_tags(["foo", "bar"])

_device_2_with_tags =
device_2
|> add_tags(["bar", "baz"])

_ = device_group_fixture(tenant: tenant, name: "foos", selector: ~s<"foo" in tags>)
_ = device_group_fixture(tenant: tenant, name: "bars", selector: ~s<"bar" in tags>)
_ = device_group_fixture(tenant: tenant, name: "bazs", selector: ~s<"baz" in tags>)

devices =
devices_query(document: document, tenant: tenant)
|> extract_result!()

device_1_groups =
Enum.find_value(devices, &(&1["deviceId"] == device_id_1 && &1["deviceGroups"]))

assert length(device_1_groups) == 2
assert %{"name" => "foos"} in device_1_groups
assert %{"name" => "bars"} in device_1_groups

device_2_groups =
Enum.find_value(devices, &(&1["deviceId"] == device_id_2 && &1["deviceGroups"]))

assert length(device_2_groups) == 2
assert %{"name" => "bars"} in device_2_groups
assert %{"name" => "bazs"} in device_2_groups
end
end

defp devices_query(opts) do
default_document =
"""
Expand Down

0 comments on commit 442275c

Please sign in to comment.