Skip to content

Commit e737c70

Browse files
committed
chore: Add file upload request support
Generated migrate and snapshots using command: ˛˛˛ bash mix ash.codegen create_files_upload_resource ˛˛˛ - Introduce `FileUploadRequest` module to handle file upload requests on Astarte devices. - Add changes for setting upload URLs and sending file upload requests. - Update `HandleTrigger` to process file upload events and update request statuses. - Add tests for file upload request handling and deployment updates. Signed-off-by: Osman Hadzic <osman.hadzic@secomind.com>
1 parent d7ed1d5 commit e737c70

19 files changed

Lines changed: 1382 additions & 95 deletions

File tree

backend/config/test.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ config :edgehog,
116116
:astarte_file_download_request_module,
117117
Edgehog.Astarte.Device.FileDownloadRequestMock
118118

119+
config :edgehog,
120+
:astarte_file_upload_request_module,
121+
Edgehog.Astarte.Device.FileUploadRequestMock
122+
119123
config :edgehog, :astarte_forwarder_session_module, Edgehog.Astarte.Device.ForwarderSessionMock
120124
config :edgehog, :astarte_geolocation_module, Edgehog.Astarte.Device.GeolocationMock
121125
config :edgehog, :astarte_hardware_info_module, Edgehog.Astarte.Device.HardwareInfoMock
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 Edgehog.Astarte.Device.FileUploadRequest.Behaviour do
22+
@moduledoc """
23+
Behaviour for requesting file uploads on Astarte devices.
24+
"""
25+
26+
alias Astarte.Client.AppEngine
27+
alias Edgehog.Astarte.Device.FileUploadRequest.RequestData
28+
29+
@callback request_upload(
30+
client :: AppEngine.t(),
31+
device_id :: String.t(),
32+
request_data :: RequestData.t()
33+
) ::
34+
:ok | {:error, term()}
35+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 Edgehog.Astarte.Device.FileUploadRequest.RequestData do
22+
@moduledoc """
23+
Struct representing the data needed to request a file upload on an Astarte device.
24+
"""
25+
26+
defstruct [
27+
:id,
28+
:url,
29+
:httpHeaderKey,
30+
:httpHeaderValue,
31+
:compression,
32+
:progress,
33+
:source,
34+
:sourceType
35+
]
36+
37+
@type t() :: %__MODULE__{
38+
id: String.t(),
39+
url: String.t(),
40+
httpHeaderKey: String.t(),
41+
httpHeaderValue: String.t(),
42+
compression: String.t(),
43+
progress: boolean(),
44+
source: String.t(),
45+
sourceType: String.t()
46+
}
47+
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 Edgehog.Astarte.Device.FileUploadRequest do
22+
@moduledoc """
23+
Module responsible for handling file upload requests on Astarte devices.
24+
"""
25+
26+
@behaviour Edgehog.Astarte.Device.FileUploadRequest.Behaviour
27+
28+
alias Astarte.Client.AppEngine
29+
alias Edgehog.Error
30+
31+
@interface "io.edgehog.devicemanager.fileTransfer.DeviceToServer"
32+
33+
@impl Edgehog.Astarte.Device.FileUploadRequest.Behaviour
34+
def request_upload(%AppEngine{} = client, device_id, request_data) do
35+
request_data = Map.from_struct(request_data)
36+
37+
client
38+
|> AppEngine.Devices.send_datastream(
39+
device_id,
40+
@interface,
41+
"/request",
42+
request_data
43+
)
44+
|> Error.maybe_match_error(device_id, @interface)
45+
end
46+
end

backend/lib/edgehog/files/file_upload_request.ex

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 Edgehog.Files.FileUploadRequest.Changes.SendFileUploadRequest do
22+
@moduledoc """
23+
Ash change responsible for sending a file upload request to the Astarte device
24+
after the file upload request resource has been created.
25+
"""
26+
27+
use Ash.Resource.Change
28+
29+
alias Edgehog.Files
30+
31+
@impl Ash.Resource.Change
32+
def change(changeset, _opts, _context) do
33+
Ash.Changeset.after_transaction(changeset, &send_file_upload_request/2)
34+
end
35+
36+
defp send_file_upload_request(_changeset, {:ok, file_upload_request} = result) do
37+
case Files.send_file_upload_request(file_upload_request) do
38+
:ok -> result
39+
{:error, reason} -> {:error, reason}
40+
end
41+
end
42+
43+
defp send_file_upload_request(_changeset, result), do: result
44+
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 Edgehog.Files.FileUploadRequest.Changes.SetUploadUrl do
22+
@moduledoc """
23+
Ash change responsible for generating and setting a presigned upload URL
24+
for a file upload request before persisting it.
25+
"""
26+
27+
use Ash.Resource.Change
28+
29+
alias Ash.Resource.Change
30+
31+
@files_storage_module Application.compile_env(
32+
:edgehog,
33+
:files_storage_module,
34+
Edgehog.Storage
35+
)
36+
37+
@impl Change
38+
def change(%Ash.Changeset{valid?: false} = changeset, _opts, _context), do: changeset
39+
40+
@impl Change
41+
def change(changeset, _opts, context) do
42+
tenant_id = context.tenant.tenant_id
43+
file_upload_request_id = Ash.Changeset.get_attribute(changeset, :id)
44+
45+
file_path =
46+
"uploads/tenants/#{tenant_id}/file_upload_requests/#{file_upload_request_id}"
47+
48+
case @files_storage_module.create_presigned_urls(file_path) do
49+
{:ok, %{put_url: put_url}} -> Ash.Changeset.change_attribute(changeset, :url, put_url)
50+
{:error, reason} -> Ash.Changeset.add_error(changeset, reason)
51+
end
52+
end
53+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 Edgehog.Files.FileUploadRequest.FileSource do
22+
@moduledoc false
23+
24+
use Ash.Type.Enum,
25+
values: [
26+
storage: "File source from storage",
27+
streaming: "File source from streaming",
28+
filesystem: "File source from filesystem"
29+
]
30+
31+
def graphql_type(_), do: :file_source
32+
end

0 commit comments

Comments
 (0)