Skip to content

Commit

Permalink
GraphQL: add createBaseImage mutation
Browse files Browse the repository at this point in the history
Allow uploading a base image to a base image collection

Signed-off-by: Riccardo Binetti <[email protected]>
  • Loading branch information
rbino committed Jan 26, 2023
1 parent 86b64f1 commit 579581a
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
35 changes: 35 additions & 0 deletions backend/lib/edgehog_web/resolvers/base_images.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,41 @@ defmodule EdgehogWeb.Resolvers.BaseImages do
{:ok, base_images}
end

def create_base_image(args, resolution) do
default_locale = resolution.context.current_tenant.default_locale

with {:ok, %BaseImageCollection{} = collection} <-
BaseImages.fetch_base_image_collection(args.base_image_collection_id),
:ok <- ensure_default_locale(args[:description], default_locale),
:ok <- ensure_default_locale(args[:release_display_name], default_locale),
args = wrap_localized_field(args, :description),
args = wrap_localized_field(args, :release_display_name),
{:ok, %BaseImage{} = base_image} <- BaseImages.create_base_image(collection, args) do
{:ok, %{base_image: base_image}}
end
end

# TODO: consider extracting all this functions dealing with locale wrapping/unwrapping
# in a dedicated resolver/helper module

# Only allow localized input text that uses the tenant default locale
defp ensure_default_locale(nil, _default_locale), do: :ok
defp ensure_default_locale(%{locale: default_locale}, default_locale), do: :ok
defp ensure_default_locale(%{locale: _other}, _default), do: {:error, :not_default_locale}

# If it's there, wraps a localized field in a map, as the context expects a map
defp wrap_localized_field(args, field) when is_map_key(args, field) do
case Map.fetch!(args, field) do
%{locale: locale, text: text} ->
Map.put(args, field, %{locale => text})

_ ->
args
end
end

defp wrap_localized_field(args, _field), do: args

def extract_localized_description(%BaseImage{} = base_image, _args, resolution) do
# TODO: move this in a middleware
extract_localized_field(base_image, :translated_description, resolution.context)
Expand Down
38 changes: 38 additions & 0 deletions backend/lib/edgehog_web/schema/base_images_types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,43 @@ defmodule EdgehogWeb.Schema.BaseImagesTypes do
middleware Absinthe.Relay.Node.ParseIDs, base_image_collection_id: :base_image_collection
resolve &Resolvers.BaseImages.delete_base_image_collection/2
end

@desc "Create a new base image in a base image collection."
payload field :create_base_image do
input do
@desc "The ID of the Base Image Collection this Base Image will belong to"
field :base_image_collection_id, non_null(:id)

@desc "The base image version"
field :version, non_null(:string)

@desc "The base image file, which will be uploaded to the storage"
field :file, non_null(:upload)

@desc "An optional starting version requirement for the base image"
field :starting_version_requirement, :string

@desc """
An optional localized description. This description can currently only use the \
default tenant locale.
"""
field :description, :localized_text_input

@desc """
An optional relase display name. This can currently only use the \
default tenant locale.
"""
field :release_display_name, :localized_text_input
end

output do
@desc "The created base image."
field :base_image, non_null(:base_image)
end

middleware Absinthe.Relay.Node.ParseIDs, base_image_collection_id: :base_image_collection

resolve &Resolvers.BaseImages.create_base_image/2
end
end
end
162 changes: 162 additions & 0 deletions backend/test/edgehog_web/schema/mutation/create_base_image_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#
# This file is part of Edgehog.
#
# Copyright 2023 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 EdgehogWeb.Schema.Mutation.CreateBaseImageTest do
use EdgehogWeb.ConnCase
use Edgehog.BaseImagesStorageMockCase

import Edgehog.BaseImagesFixtures
import Edgehog.DevicesFixtures

describe "createBaseImage mutation" do
setup do
hardware_type = hardware_type_fixture()
system_model = system_model_fixture(hardware_type)

{:ok, base_image_collection: base_image_collection_fixture(system_model)}
end

test "creates base image with valid data", %{
conn: conn,
api_path: api_path,
tenant: tenant,
base_image_collection: base_image_collection
} do
default_tenant_locale = tenant.default_locale

response =
create_base_image_mutation(conn, api_path,
base_image_collection_id: base_image_collection.id,
version: "2.0.0",
starting_version_requirement: "~> 1.0",
description: localized_text(default_tenant_locale, "Description"),
release_display_name: localized_text(default_tenant_locale, "Display name")
)

base_image = response["data"]["createBaseImage"]["baseImage"]
assert base_image["version"] == "2.0.0"
assert base_image["startingVersionRequirement"] == "~> 1.0"
assert base_image["description"] == "Description"
assert base_image["releaseDisplayName"] == "Display name"
assert base_image["baseImageCollection"]["handle"] == base_image_collection.handle
end

test "fails with invalid data", %{
conn: conn,
api_path: api_path,
base_image_collection: base_image_collection
} do
response =
create_base_image_mutation(conn, api_path,
base_image_collection_id: base_image_collection.id,
version: "invalid"
)

assert response["data"]["createBaseImage"] == nil
assert response["errors"] != nil
end

test "fails when not using the default tenant locale for the description", %{
conn: conn,
api_path: api_path,
base_image_collection: base_image_collection
} do
response =
create_base_image_mutation(conn, api_path,
base_image_collection_id: base_image_collection.id,
description: localized_text("it-IT", "Descrizione")
)

assert response["data"]["createBaseImage"] == nil
assert %{"errors" => [%{"status_code" => 422, "code" => "not_default_locale"}]} = response
end

test "fails when not using the default tenant locale for the release display name", %{
conn: conn,
api_path: api_path,
base_image_collection: base_image_collection
} do
response =
create_base_image_mutation(conn, api_path,
base_image_collection_id: base_image_collection.id,
release_display_name: localized_text("it-IT", "Nome")
)

assert response["data"]["createBaseImage"] == nil
assert %{"errors" => [%{"status_code" => 422, "code" => "not_default_locale"}]} = response
end

test "fails when trying to use a non-existing base image collection", %{
conn: conn,
api_path: api_path
} do
response = create_base_image_mutation(conn, api_path, base_image_collection_id: "123456")

assert %{"errors" => [%{"status_code" => 404, "code" => "not_found"}]} = response
end
end

@query """
mutation CreateBaseImage($input: CreateBaseImageInput!) {
createBaseImage(input: $input) {
baseImage {
version
url
startingVersionRequirement
description
releaseDisplayName
baseImageCollection {
handle
}
}
}
}
"""
defp create_base_image_mutation(conn, api_path, opts) do
base_image_collection_id =
Absinthe.Relay.Node.to_global_id(
:base_image_collection,
opts[:base_image_collection_id],
EdgehogWeb.Schema
)

fake_image = %Plug.Upload{path: "/tmp/ota.bin", filename: "ota.bin"}

input =
Enum.into(opts, %{
version: unique_base_image_version(),
file: "fake_image"
})
|> Map.put(:base_image_collection_id, base_image_collection_id)

variables = %{input: input}

conn = post(conn, api_path, query: @query, variables: variables, fake_image: fake_image)

json_response(conn, 200)
end

defp localized_text(locale, text) do
%{
locale: locale,
text: text
}
end
end

0 comments on commit 579581a

Please sign in to comment.