diff --git a/assets/js/lib/test-utils/factories/sapSystems.js b/assets/js/lib/test-utils/factories/sapSystems.js index 55e60c6f51..6d50f42f82 100644 --- a/assets/js/lib/test-utils/factories/sapSystems.js +++ b/assets/js/lib/test-utils/factories/sapSystems.js @@ -15,6 +15,7 @@ const roles = () => 'GATEWAY', 'ICMAN', 'IGS', + 'J2EE', ]); export const sapSystemApplicationInstanceFactory = Factory.define(() => ({ diff --git a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.jsx b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.jsx index d00c0d00f7..4243fa2b71 100644 --- a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.jsx +++ b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.jsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-unstable-nested-components */ import React, { useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; -import { filter } from 'lodash'; +import { filter, uniq, flatMap } from 'lodash'; import { getEnsaVersionLabel } from '@lib/model/sapSystems'; @@ -77,6 +77,17 @@ function SapSystemsOverview({ title: 'Tenant', key: 'tenant', }, + { + title: 'Type', + key: 'applicationInstances', + render: (content) => + uniq(flatMap(content, ({ features }) => features.split('|'))) + .filter((item) => item === 'J2EE' || item === 'ABAP') + .map((item) => (item === 'J2EE' ? 'JAVA' : item)) + .toSorted() + .join('/'), + }, + { title: 'DB Address', key: 'dbAddress', diff --git a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.stories.jsx b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.stories.jsx index 4b5431d3a1..6646a864b6 100644 --- a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.stories.jsx +++ b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.stories.jsx @@ -5,6 +5,7 @@ import { MemoryRouter } from 'react-router-dom'; import { clusterFactory, hostFactory, + sapSystemApplicationInstanceFactory, sapSystemFactory, } from '@lib/test-utils/factories'; @@ -19,6 +20,7 @@ const enrichInstances = (systems, instanceType) => const cluster = clusterFactory.build(); return { ...instance, + features: 'ABAP', host: { ...hostFactory.build({ id: instance.host_id, @@ -49,6 +51,28 @@ const enrichedAbsentDatabaseInstances = enrichInstances( 'database_instances' ); +const sapSystemTypes = [ + 'ABAP', + 'J2EE', + 'ABAP|J2EE', + 'J2EE|ABAP', + 'SOME_SAP_SYSTEM_FEATURE|NOT_A_REAL_SYSTEM', +]; + +const sapSystemsWithCustomTypes = sapSystemTypes.map((type) => { + const sapSystemID = faker.string.uuid(); + return sapSystemFactory.build({ + id: sapSystemID, + application_instances: sapSystemApplicationInstanceFactory.buildList(2, { + sap_system_id: sapSystemID, + features: type, + }), + }); +}); +const sapSystemApplicationInstances = sapSystemsWithCustomTypes + .map((sapSystem) => sapSystem.application_instances) + .flat(); + enrichedAbsentApplicationInstances[1].absent_at = faker.date .past() .toISOString(); @@ -71,10 +95,6 @@ export default { control: { type: 'array' }, description: 'Application instances', }, - userAbilities: { - control: { type: 'array' }, - description: 'User profile abilities', - }, databaseInstances: { control: { type: 'array' }, description: 'Database instances', @@ -87,6 +107,10 @@ export default { defaultValue: { summary: false }, }, }, + userAbilities: { + control: { type: 'array' }, + description: 'User profile abilities', + }, onTagAdd: { action: 'Add tag', description: 'Called when a new tag is added', @@ -140,3 +164,11 @@ export const UnauthorizedCleanUp = { userAbilities: [], }, }; +export const SapSystemsWithDifferentTypes = { + args: { + userAbilities, + sapSystems: sapSystemsWithCustomTypes, + applicationInstances: sapSystemApplicationInstances, + databaseInstances: {}, + }, +}; diff --git a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.test.jsx b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.test.jsx index edd759c2ec..81d796abc7 100644 --- a/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.test.jsx +++ b/assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.test.jsx @@ -8,6 +8,7 @@ import userEvent from '@testing-library/user-event'; import { clusterFactory, hostFactory, + sapSystemApplicationInstanceFactory, sapSystemFactory, } from '@lib/test-utils/factories'; import { renderWithRouter } from '@lib/test-utils'; @@ -41,16 +42,25 @@ describe('SapSystemsOverviews component', () => { }); it('should display the correct content for a SAP system main row', () => { - const sapSystem = sapSystemFactory.build({ ensa_version: 'ensa1' }); - const { + const sapSystemType = 'ABAP'; + const sapSystemID = faker.string.uuid(); + + const sapSystem = sapSystemFactory.build({ id: sapSystemID, - sid, + ensa_version: 'ensa1', + application_instances: sapSystemApplicationInstanceFactory.buildList( + 2, + { sap_system_id: sapSystemID, features: sapSystemType } + ), + }); + const { tenant, db_host: dbAddress, application_instances: applicationInstances, database_instances: databaseInstances, database_id: databaseID, database_sid: attachedRdbms, + sid, } = sapSystem; renderWithRouter( @@ -80,13 +90,95 @@ describe('SapSystemsOverviews component', () => { tenant ); expect(mainRow.querySelector('td:nth-child(5)')).toHaveTextContent( - dbAddress + sapSystemType ); expect(mainRow.querySelector('td:nth-child(6)')).toHaveTextContent( + dbAddress + ); + expect(mainRow.querySelector('td:nth-child(7)')).toHaveTextContent( 'ENSA1' ); }); + it('should display the correct SAP system type JAVA or ABAP', () => { + const sapSystemTypes = [ + 'ABAP', + 'J2EE', + 'SOME_SAP_SYSTEM_FEATURE|NOT_A_REAL_SYSTEM', + ]; + + const expectedSapSystemTypes = ['ABAP', 'JAVA', '']; + + const sapSystems = sapSystemTypes.map((type) => { + const sapSystemID = faker.string.uuid(); + return sapSystemFactory.build({ + id: sapSystemID, + application_instances: sapSystemApplicationInstanceFactory.buildList( + 2, + { sap_system_id: sapSystemID, features: type } + ), + }); + }); + + const sapSystemApplicationInstances = sapSystems + .map((sapSystem) => sapSystem.application_instances) + .flat(); + + renderWithRouter( + + ); + const rows = screen.getByRole('table').querySelectorAll('tbody > tr'); + expectedSapSystemTypes.forEach((expectedType, index) => { + const rowIndex = index * 2; + const sapSystemRow = rows[rowIndex]; + expect(sapSystemRow.querySelector('td:nth-child(5)')).toHaveTextContent( + expectedType + ); + }); + }); + + it('should display the correct SAP system type JAVA and ABAP', () => { + const expectedSapSystemTypes = 'ABAP/JAVA'; + const sapSystemID = faker.string.uuid(); + const sapSystem = sapSystemFactory.build({ + id: sapSystemID, + application_instances: [ + sapSystemApplicationInstanceFactory.build({ + sap_system_id: sapSystemID, + features: 'ABAP', + }), + sapSystemApplicationInstanceFactory.build({ + sap_system_id: sapSystemID, + features: 'J2EE', + }), + sapSystemApplicationInstanceFactory.build({ + sap_system_id: sapSystemID, + features: 'SOME_SAP_SYSTEM_FEATURE|OTHER_SAP_APP', + }), + ], + }); + + const { application_instances: applicationInstances } = sapSystem; + + renderWithRouter( + + ); + const rows = screen.getByRole('table').querySelectorAll('tbody > tr'); + expect(rows[0].querySelector('td:nth-child(5)')).toHaveTextContent( + expectedSapSystemTypes + ); + }); + it('should display the correct content for a SAP system instances', () => { const sapSystem = sapSystemFactory.build(); const { diff --git a/lib/trento/sap_systems/sap_system.ex b/lib/trento/sap_systems/sap_system.ex index ce8d37489d..70a0edc720 100644 --- a/lib/trento/sap_systems/sap_system.ex +++ b/lib/trento/sap_systems/sap_system.ex @@ -638,7 +638,7 @@ defmodule Trento.SapSystems.SapSystem do database_health: database_health } ) do - if instances_have_abap?(instances) and instances_have_messageserver?(instances) do + if instances_have_abap_or_java?(instances) and instances_have_messageserver?(instances) do %SapSystemRestored{ db_host: db_host, health: health, @@ -659,7 +659,7 @@ defmodule Trento.SapSystems.SapSystem do database_health: database_health } ) do - if instances_have_abap?(instances) and instances_have_messageserver?(instances) do + if instances_have_abap_or_java?(instances) and instances_have_messageserver?(instances) do %SapSystemRestored{ health: health, db_host: db_host, @@ -683,7 +683,7 @@ defmodule Trento.SapSystems.SapSystem do database_health: database_health } ) do - if instances_have_abap?(instances) and instances_have_messageserver?(instances) do + if instances_have_abap_or_java?(instances) and instances_have_messageserver?(instances) do %SapSystemRegistered{ sap_system_id: sap_system_id, sid: sid, @@ -794,7 +794,7 @@ defmodule Trento.SapSystems.SapSystem do }, deregistered_at ) do - unless instances_have_abap?(instances) and instances_have_messageserver?(instances) do + unless instances_have_abap_or_java?(instances) and instances_have_messageserver?(instances) do %SapSystemDeregistered{sap_system_id: sap_system_id, deregistered_at: deregistered_at} end end @@ -814,6 +814,14 @@ defmodule Trento.SapSystems.SapSystem do Enum.any?(instances, fn %{features: features} -> features =~ "ABAP" end) end + defp instances_have_java?(instances) do + Enum.any?(instances, fn %{features: features} -> features =~ "J2EE" end) + end + + defp instances_have_abap_or_java?(instances) do + instances_have_abap?(instances) or instances_have_java?(instances) + end + def instances_have_messageserver?(instances) do Enum.any?(instances, fn %{features: features} -> features =~ "MESSAGESERVER" end) end diff --git a/test/e2e/cypress/e2e/sap_systems_overview.cy.js b/test/e2e/cypress/e2e/sap_systems_overview.cy.js index d072d60ef3..116707ee36 100644 --- a/test/e2e/cypress/e2e/sap_systems_overview.cy.js +++ b/test/e2e/cypress/e2e/sap_systems_overview.cy.js @@ -73,7 +73,8 @@ context('SAP Systems Overview', () => { .within(() => { cy.get('td').eq(2).contains(attachedDatabase.sid); cy.get('td').eq(3).contains(attachedDatabase.tenant); - cy.get('td').eq(4).contains(attachedDatabase.dbAddress); + + cy.get('td').eq(5).contains(attachedDatabase.dbAddress); }); }); it(`should have a link to the attached HANA database with id: ${attachedDatabase.id}`, () => { diff --git a/test/trento/sap_systems/sap_system_test.exs b/test/trento/sap_systems/sap_system_test.exs index a19037b715..0b6459f9c6 100644 --- a/test/trento/sap_systems/sap_system_test.exs +++ b/test/trento/sap_systems/sap_system_test.exs @@ -124,6 +124,92 @@ defmodule Trento.SapSystems.SapSystemTest do ) end + test "should register a SAP System and add an application instance when a MESSAGESERVER instance is already present and a new JAVA instance is added" do + sap_system_id = Faker.UUID.v4() + sid = Faker.StarWars.planet() + db_host = Faker.Internet.ip_v4_address() + tenant = Faker.Beer.style() + instance_hostname = Faker.Airports.iata() + http_port = 80 + https_port = 443 + start_priority = "0.9" + host_id = Faker.UUID.v4() + ensa_version = EnsaVersion.ensa1() + java_system_type = "J2EE" + + initial_events = [ + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + features: "MESSAGESERVER", + instance_number: "00" + ) + ] + + assert_events_and_state( + initial_events, + RegisterApplicationInstance.new!(%{ + sap_system_id: sap_system_id, + sid: sid, + db_host: db_host, + tenant: tenant, + instance_number: "10", + instance_hostname: instance_hostname, + features: java_system_type, + http_port: http_port, + https_port: https_port, + start_priority: start_priority, + host_id: host_id, + health: :passing, + ensa_version: ensa_version, + database_health: :passing + }), + [ + %ApplicationInstanceRegistered{ + sap_system_id: sap_system_id, + sid: sid, + instance_number: "10", + instance_hostname: instance_hostname, + features: java_system_type, + http_port: http_port, + https_port: https_port, + start_priority: start_priority, + host_id: host_id, + health: :passing + }, + %SapSystemRegistered{ + sap_system_id: sap_system_id, + sid: sid, + db_host: db_host, + tenant: tenant, + health: :passing, + database_health: :passing, + ensa_version: ensa_version + } + ], + fn state -> + assert %SapSystem{ + sid: ^sid, + ensa_version: ^ensa_version, + database_health: :passing, + instances: [ + %Instance{ + sid: ^sid, + instance_number: "10", + features: ^java_system_type, + host_id: ^host_id, + health: :passing, + absent_at: nil + }, + %Instance{ + features: "MESSAGESERVER" + } + ] + } = state + end + ) + end + test "should move an application instance if the host_id changed and the instance number already exists and the application is clustered" do sap_system_id = Faker.UUID.v4() sid = fake_sid() @@ -1243,16 +1329,21 @@ defmodule Trento.SapSystems.SapSystemTest do end describe "tombstoning" do - test "should tombstone a deregistered SAP system when no application instances are left" do + test "should tombstone a deregistered SAP system when no ABAP or JAVA application instances are left" do sap_system_id = UUID.uuid4() deregistered_at = DateTime.utc_now() message_server_host_id = UUID.uuid4() message_server_instance_number = "00" abap_host_id = UUID.uuid4() + java_host_id = UUID.uuid4() abap_instance_number = "01" + java_instance_number = "02" application_sid = fake_sid() + # Sap System type + abap_system_type = "ABAP" + java_system_type = "J2EE" initial_events = [ build( @@ -1266,11 +1357,19 @@ defmodule Trento.SapSystems.SapSystemTest do build( :application_instance_registered_event, sap_system_id: sap_system_id, - features: "ABAP|GATEWAY|ICMAN|IGS", + features: abap_system_type, host_id: abap_host_id, sid: application_sid, instance_number: abap_instance_number ), + build( + :application_instance_registered_event, + sap_system_id: sap_system_id, + features: java_system_type, + host_id: java_host_id, + sid: application_sid, + instance_number: java_instance_number + ), build( :sap_system_registered_event, sap_system_id: sap_system_id, @@ -1292,6 +1391,12 @@ defmodule Trento.SapSystems.SapSystemTest do host_id: abap_host_id, instance_number: abap_instance_number, deregistered_at: deregistered_at + }, + %DeregisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: java_host_id, + instance_number: java_instance_number, + deregistered_at: deregistered_at } ], [ @@ -1311,6 +1416,12 @@ defmodule Trento.SapSystems.SapSystemTest do instance_number: abap_instance_number, deregistered_at: deregistered_at }, + %ApplicationInstanceDeregistered{ + sap_system_id: sap_system_id, + host_id: java_host_id, + instance_number: java_instance_number, + deregistered_at: deregistered_at + }, %SapSystemTombstoned{ sap_system_id: sap_system_id }