+ {affectedSystems && affectedSystems.length ? (
- {packages.map((pkg) => (
- - {pkg}
+ {affectedSystems.map(({ name }) => (
+ - {name}
))}
) : (
diff --git a/assets/js/pages/AdvisoryDetails/AdvisoryDetails.stories.jsx b/assets/js/pages/AdvisoryDetails/AdvisoryDetails.stories.jsx
index 18ee771edf..eca6b4176b 100644
--- a/assets/js/pages/AdvisoryDetails/AdvisoryDetails.stories.jsx
+++ b/assets/js/pages/AdvisoryDetails/AdvisoryDetails.stories.jsx
@@ -28,8 +28,45 @@ However, the post didn't come by today, and I am starting to wonder, if my Geeko
4815162342: 'Geekos unexpectedly eating quiches',
},
cves: ['CVE-2024-35938'],
+ affected_packages: [
+ {
+ name: 'libprocps7',
+ version: '3.3.15',
+ release: '7.34.1',
+ epoch: '150000',
+ arch_label: 'x86_64',
+ },
+ ],
+ affected_systems: [
+ {
+ name: 'vmdrbddev01',
+ },
+ ],
},
- packages: undefined,
- affectsPackageMaintanaceStack: false,
+ affectsPackageMaintenanceStack: false,
+ },
+};
+
+export const Empty = {
+ args: {
+ advisoryName: 'SUSE-15-SP4-2023-3369',
+ errata: {
+ errata_details: {
+ issue_date: Date.now(),
+ update_date: Date.now(),
+ synopsis: 'I think my Geekos ate my quiche 🦎🦎',
+ advisory_status: 'stable',
+ type: 'security_advisory',
+ description: `My Geekos really love the cakes I order from the crab bakery.
+Yesterday, I left before the post arrived. Normally, the post just delivers my packages the next day.
+However, the post didn't come by today, and I am starting to wonder, if my Geekos ate my quiche. AITA? 😟`,
+ reboot_suggested: true,
+ },
+ fixes: {},
+ cves: [],
+ affected_packages: [],
+ affected_systems: [],
+ },
+ affectsPackageMaintenanceStack: false,
},
};
diff --git a/assets/js/pages/AdvisoryDetails/AdvisoryDetails.test.jsx b/assets/js/pages/AdvisoryDetails/AdvisoryDetails.test.jsx
index c336562c57..862a7bbbce 100644
--- a/assets/js/pages/AdvisoryDetails/AdvisoryDetails.test.jsx
+++ b/assets/js/pages/AdvisoryDetails/AdvisoryDetails.test.jsx
@@ -11,13 +11,18 @@ import AdvisoryDetails from './AdvisoryDetails';
describe('AdvisoryDetails', () => {
it('displays a message, when the CVE, packages or fixes section is empty', () => {
- const errata = advisoryErrataFactory.build({ cves: [], fixes: {} });
+ const errata = advisoryErrataFactory.build({
+ cves: [],
+ fixes: {},
+ affected_packages: [],
+ affected_systems: [],
+ });
render(
);
- expect(screen.getAllByText('No data available').length).toBe(3);
+ expect(screen.getAllByText('No data available').length).toBe(4);
});
it('displays relevant errata data', () => {
@@ -34,22 +39,27 @@ describe('AdvisoryDetails', () => {
expect(screen.getByText(errata.errata_details.description)).toBeVisible();
});
- it('displays packages', () => {
+ it('displays affected packages', () => {
const errata = advisoryErrataFactory.build();
const advisoryName = faker.lorem.word();
- const packages = faker.word.words(2).split(' ');
+ render(
);
- render(
-
- );
+ errata.affected_packages.forEach(({ name }) => {
+ const el = screen.getByText(name, { exact: false });
+ expect(el).toBeVisible();
+ });
+ });
+
+ it('displays affected systems', () => {
+ const errata = advisoryErrataFactory.build();
+ const advisoryName = faker.lorem.word();
- packages.forEach((expectedWord) => {
- expect(screen.getByText(expectedWord)).toBeVisible();
+ render(
);
+
+ errata.affected_systems.forEach(({ name }) => {
+ const el = screen.getByText(name);
+ expect(el).toBeVisible();
});
});
diff --git a/lib/trento_web/controllers/fallback_controller.ex b/lib/trento_web/controllers/fallback_controller.ex
index 25cfd95abc..5a3eed9717 100644
--- a/lib/trento_web/controllers/fallback_controller.ex
+++ b/lib/trento_web/controllers/fallback_controller.ex
@@ -138,6 +138,20 @@ defmodule TrentoWeb.FallbackController do
|> render(:"422", reason: "Unable to retrieve Bugzilla fixes for this advisory.")
end
+ def call(conn, {:error, :error_getting_affected_packages}) do
+ conn
+ |> put_status(:unprocessable_entity)
+ |> put_view(ErrorView)
+ |> render(:"422", reason: "Unable to retrieve affected packages for this advisory.")
+ end
+
+ def call(conn, {:error, :error_getting_affected_systems}) do
+ conn
+ |> put_status(:unprocessable_entity)
+ |> put_view(ErrorView)
+ |> render(:"422", reason: "Unable to retrieve affected systems for this advisory.")
+ end
+
def call(conn, {:error, :connection_test_failed}) do
conn
|> put_status(:unprocessable_entity)
diff --git a/lib/trento_web/controllers/v1/suse_manager_controller.ex b/lib/trento_web/controllers/v1/suse_manager_controller.ex
index 69cc4f3da2..68db614285 100644
--- a/lib/trento_web/controllers/v1/suse_manager_controller.ex
+++ b/lib/trento_web/controllers/v1/suse_manager_controller.ex
@@ -96,8 +96,16 @@ defmodule TrentoWeb.V1.SUSEManagerController do
def errata_details(conn, %{advisory_name: advisory_name}) do
with {:ok, errata_details} <- Discovery.get_errata_details(advisory_name),
{:ok, cves} <- Discovery.get_cves(advisory_name),
- {:ok, fixes} <- Discovery.get_bugzilla_fixes(advisory_name) do
- render(conn, %{errata_details: errata_details, cves: cves, fixes: fixes})
+ {:ok, fixes} <- Discovery.get_bugzilla_fixes(advisory_name),
+ {:ok, affected_packages} <- Discovery.get_affected_packages(advisory_name),
+ {:ok, affected_systems} <- Discovery.get_affected_systems(advisory_name) do
+ render(conn, %{
+ errata_details: errata_details,
+ cves: cves,
+ fixes: fixes,
+ affected_packages: affected_packages,
+ affected_systems: affected_systems
+ })
end
end
end
diff --git a/lib/trento_web/openapi/v1/schema/available_software_updates.ex b/lib/trento_web/openapi/v1/schema/available_software_updates.ex
index e5e2f3ce76..0fb84c6b87 100644
--- a/lib/trento_web/openapi/v1/schema/available_software_updates.ex
+++ b/lib/trento_web/openapi/v1/schema/available_software_updates.ex
@@ -197,6 +197,64 @@ defmodule TrentoWeb.OpenApi.V1.Schema.AvailableSoftwareUpdates do
})
end
+ defmodule AffectedPackages do
+ @moduledoc false
+ OpenApiSpex.schema(%{
+ title: "AffectedPackages",
+ description: "Response returned from the get affected packages endpoint",
+ type: :array,
+ additionalProperties: false,
+ items: %Schema{
+ title: "AffectedPackage",
+ description: "Metadata for a package effected by an advisory",
+ type: :object,
+ properties: %{
+ name: %Schema{
+ type: :string,
+ description: "Package name"
+ },
+ arch_label: %Schema{
+ type: :string,
+ description: "Package architecture"
+ },
+ version: %Schema{
+ type: :string,
+ description: "Package upstream version"
+ },
+ release: %Schema{
+ type: :string,
+ description: "Package RPM release number"
+ },
+ epoch: %Schema{
+ type: :string,
+ description: "Package epoch number"
+ }
+ }
+ }
+ })
+ end
+
+ defmodule AffectedSystems do
+ @moduledoc false
+ OpenApiSpex.schema(%{
+ title: "AffectedSystems",
+ description: "Response returned from the get affected systems endpoint",
+ type: :array,
+ additionalProperties: false,
+ items: %Schema{
+ title: "AffectedSystem",
+ description: "Metadata for a system effected by an advisory",
+ type: :object,
+ properties: %{
+ name: %Schema{
+ type: :string,
+ description: "System name"
+ }
+ }
+ }
+ })
+ end
+
defmodule ErrataDetailsResponse do
@moduledoc false
OpenApiSpex.schema(%{
@@ -207,7 +265,9 @@ defmodule TrentoWeb.OpenApi.V1.Schema.AvailableSoftwareUpdates do
properties: %{
errata_details: ErrataDetails,
cves: CVEs,
- fixes: AdvisoryFixes
+ fixes: AdvisoryFixes,
+ affected_packages: AffectedPackages,
+ affected_systems: AffectedSystems
}
})
end
diff --git a/lib/trento_web/views/v1/suse_manager_view.ex b/lib/trento_web/views/v1/suse_manager_view.ex
index 8c26911e9b..f8213e4fca 100644
--- a/lib/trento_web/views/v1/suse_manager_view.ex
+++ b/lib/trento_web/views/v1/suse_manager_view.ex
@@ -66,7 +66,9 @@ defmodule TrentoWeb.V1.SUSEManagerView do
def render("errata_details.json", %{
errata_details: errata_details = %{errataFrom: errataFrom},
cves: cves,
- fixes: fixes
+ fixes: fixes,
+ affected_packages: affected_packages,
+ affected_systems: affected_systems
}),
do: %{
errata_details:
@@ -74,6 +76,8 @@ defmodule TrentoWeb.V1.SUSEManagerView do
|> Map.drop([:errataFrom])
|> Map.put(:errata_from, errataFrom),
cves: cves,
- fixes: fixes
+ fixes: fixes,
+ affected_packages: affected_packages,
+ affected_systems: affected_systems
}
end
diff --git a/test/trento_web/controllers/v1/suse_manager_controller_test.exs b/test/trento_web/controllers/v1/suse_manager_controller_test.exs
index d5b24a4804..e26eae7ae9 100644
--- a/test/trento_web/controllers/v1/suse_manager_controller_test.exs
+++ b/test/trento_web/controllers/v1/suse_manager_controller_test.exs
@@ -184,6 +184,18 @@ defmodule TrentoWeb.V1.SUSEManagerControllerTest do
{:ok, fixes}
end)
+ affected_packages = build_list(10, :affected_package)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:ok, affected_packages}
+ end)
+
+ affected_systems = build_list(10, :affected_system)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:ok, affected_systems}
+ end)
+
json =
conn
|> get("/api/v1/software_updates/errata_details/#{advisory_name}")
@@ -218,7 +230,9 @@ defmodule TrentoWeb.V1.SUSEManagerControllerTest do
reboot_suggested: ^reboot_suggested,
restart_suggested: ^restart_suggested
},
- cves: ^cves
+ cves: ^cves,
+ affected_packages: ^affected_packages,
+ affected_systems: ^affected_systems
} = result
end
@@ -240,6 +254,14 @@ defmodule TrentoWeb.V1.SUSEManagerControllerTest do
{:ok, build(:bugzilla_fix)}
end)
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:ok, build_list(10, :affected_package)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:ok, build_list(10, :affected_system)}
+ end)
+
advisory_name = Faker.Pokemon.name()
conn
@@ -266,6 +288,14 @@ defmodule TrentoWeb.V1.SUSEManagerControllerTest do
{:ok, build(:bugzilla_fix)}
end)
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:ok, build_list(10, :affected_package)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:ok, build_list(10, :affected_system)}
+ end)
+
advisory_name = Faker.Pokemon.name()
conn
@@ -292,6 +322,82 @@ defmodule TrentoWeb.V1.SUSEManagerControllerTest do
{:error, :error_getting_fixes}
end)
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:ok, build_list(10, :affected_package)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:ok, build_list(10, :affected_system)}
+ end)
+
+ advisory_name = Faker.Pokemon.name()
+
+ conn
+ |> get("/api/v1/software_updates/errata_details/#{advisory_name}")
+ |> json_response(:unprocessable_entity)
+ |> assert_schema("UnprocessableEntity", api_spec)
+ end
+
+ test "should return 422 when advisory affected packages are not found", %{
+ conn: conn,
+ api_spec: api_spec
+ } do
+ insert_software_updates_settings()
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_errata_details, 1, fn _ ->
+ {:ok, build(:errata_details)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_cves, 1, fn _ ->
+ {:ok, build_list(10, :cve)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_bugzilla_fixes, 1, fn _ ->
+ {:ok, build(:bugzilla_fix)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:error, :error_getting_affected_packages}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:ok, build_list(10, :affected_system)}
+ end)
+
+ advisory_name = Faker.Pokemon.name()
+
+ conn
+ |> get("/api/v1/software_updates/errata_details/#{advisory_name}")
+ |> json_response(:unprocessable_entity)
+ |> assert_schema("UnprocessableEntity", api_spec)
+ end
+
+ test "should return 422 when advisory affected systems are not found", %{
+ conn: conn,
+ api_spec: api_spec
+ } do
+ insert_software_updates_settings()
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_errata_details, 1, fn _ ->
+ {:ok, build(:errata_details)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_cves, 1, fn _ ->
+ {:ok, build_list(10, :cve)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_bugzilla_fixes, 1, fn _ ->
+ {:ok, build(:bugzilla_fix)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_packages, 1, fn _ ->
+ {:ok, build_list(10, :affected_package)}
+ end)
+
+ expect(Trento.SoftwareUpdates.Discovery.Mock, :get_affected_systems, 1, fn _ ->
+ {:error, :error_getting_affected_systems}
+ end)
+
advisory_name = Faker.Pokemon.name()
conn
diff --git a/test/trento_web/views/v1/suse_manager_view_test.exs b/test/trento_web/views/v1/suse_manager_view_test.exs
index 03c70fe697..a459485bad 100644
--- a/test/trento_web/views/v1/suse_manager_view_test.exs
+++ b/test/trento_web/views/v1/suse_manager_view_test.exs
@@ -33,16 +33,24 @@ defmodule TrentoWeb.V1.SUSEManagerViewTest do
fixes = build(:bugzilla_fix)
+ affected_packages = build_list(10, :affected_package)
+
+ affected_systems = build_list(10, :affected_system)
+
assert %{
errata_details: ^expected_errata_details,
cves: ^cves,
- fixes: ^fixes
+ fixes: ^fixes,
+ affected_packages: ^affected_packages,
+ affected_systems: ^affected_systems
} =
render(SUSEManagerView, "errata_details.json", %{
errata_details:
Map.put(errata_details_sans_errata_from, :errataFrom, errata_from),
cves: cves,
- fixes: fixes
+ fixes: fixes,
+ affected_packages: affected_packages,
+ affected_systems: affected_systems
})
end
end