From b6e81889f6978849c56bb1eea25a80f6a52af1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jamie=20Rodr=C3=ADguez?= Date: Mon, 4 Sep 2023 13:53:49 +0200 Subject: [PATCH] Make frontend listen for events when instances' presence changes (#1779) * Make frontend listen for events when instances' presence changes * Db instances updated in sap systems slice when absent state changes * Addressing review feedbacks --------- Co-authored-by: Carmine Di Monaco --- .../js/lib/test-utils/factories/databases.js | 1 + .../js/lib/test-utils/factories/sapSystems.js | 1 + assets/js/state/channels.js | 2 + assets/js/state/databases.js | 10 +++ assets/js/state/databases.test.js | 28 +++++++++ assets/js/state/sagas/databases.js | 21 +++++++ assets/js/state/sagas/databases.test.js | 63 +++++++++++++++++++ assets/js/state/sagas/sapSystems.js | 19 ++++++ assets/js/state/sagas/sapSystems.test.js | 49 +++++++++++++++ assets/js/state/sapSystems.js | 18 ++++++ assets/js/state/sapSystems.test.js | 54 ++++++++++++++++ 11 files changed, 266 insertions(+) diff --git a/assets/js/lib/test-utils/factories/databases.js b/assets/js/lib/test-utils/factories/databases.js index 4e2d84e108..ecb6f46b79 100644 --- a/assets/js/lib/test-utils/factories/databases.js +++ b/assets/js/lib/test-utils/factories/databases.js @@ -20,6 +20,7 @@ export const databaseInstanceFactory = Factory.define(() => ({ start_priority: faker.datatype.number({ min: 1, max: 9 }).toString(), system_replication: '', system_replication_status: '', + absent_at: null, })); export const databaseFactory = Factory.define(({ params }) => { diff --git a/assets/js/lib/test-utils/factories/sapSystems.js b/assets/js/lib/test-utils/factories/sapSystems.js index ac97b38577..76204e7b45 100644 --- a/assets/js/lib/test-utils/factories/sapSystems.js +++ b/assets/js/lib/test-utils/factories/sapSystems.js @@ -29,6 +29,7 @@ export const sapSystemApplicationInstanceFactory = Factory.define(() => ({ sid: faker.random.alphaNumeric(3, { casing: 'upper' }), start_priority: faker.datatype.number({ min: 1, max: 9 }).toString(), sap_system_id: faker.datatype.uuid(), + absent_at: null, })); export const sapSystemFactory = Factory.define(({ params }) => { diff --git a/assets/js/state/channels.js b/assets/js/state/channels.js index aea94ada44..48a9dabfaf 100644 --- a/assets/js/state/channels.js +++ b/assets/js/state/channels.js @@ -40,6 +40,7 @@ const processChannelEvents = (reduxStore, socket) => { 'sap_system_health_changed', 'application_instance_registered', 'application_instance_moved', + 'application_instance_absent_at_changed', 'application_instance_deregistered', 'application_instance_health_changed', 'sap_system_deregistered', @@ -52,6 +53,7 @@ const processChannelEvents = (reduxStore, socket) => { 'database_restored', 'database_health_changed', 'database_instance_registered', + 'database_instance_absent_at_changed', 'database_instance_deregistered', 'database_instance_health_changed', 'database_instance_system_replication_changed', diff --git a/assets/js/state/databases.js b/assets/js/state/databases.js index 5ee2354f6a..9a9de759a7 100644 --- a/assets/js/state/databases.js +++ b/assets/js/state/databases.js @@ -89,6 +89,13 @@ export const databasesListSlice = createSlice({ } ); }, + updateDatabaseInstanceAbsentAt: (state, { payload: instance }) => { + state.databaseInstances = updateInstance( + state.databaseInstances, + instance, + { absent_at: instance.absent_at } + ); + }, setDatabaseInstanceDeregistering: (state, { payload: instance }) => { state.databaseInstances = updateInstance( state.databaseInstances, @@ -115,6 +122,8 @@ export const DATABASE_DEREGISTERED = 'DATABASE_DEREGISTERED'; export const DATABASE_RESTORED = 'DATABASE_RESTORED'; export const DATABASE_HEALTH_CHANGED = 'DATABASE_HEALTH_CHANGED'; export const DATABASE_INSTANCE_REGISTERED = 'DATABASE_INSTANCE_REGISTERED'; +export const DATABASE_INSTANCE_ABSENT_AT_CHANGED = + 'DATABASE_INSTANCE_ABSENT_AT_CHANGED'; export const DATABASE_INSTANCE_DEREGISTERED = 'DATABASE_INSTANCE_DEREGISTERED'; export const DATABASE_INSTANCE_HEALTH_CHANGED = 'DATABASE_INSTANCE_HEALTH_CHANGED'; @@ -137,6 +146,7 @@ export const { updateDatabaseHealth, updateDatabaseInstanceHealth, updateDatabaseInstanceSystemReplication, + updateDatabaseInstanceAbsentAt, addTagToDatabase, removeTagFromDatabase, setDatabaseInstanceDeregistering, diff --git a/assets/js/state/databases.test.js b/assets/js/state/databases.test.js index 3536b6c32e..96c7b320b0 100644 --- a/assets/js/state/databases.test.js +++ b/assets/js/state/databases.test.js @@ -4,6 +4,7 @@ import databaseReducer, { upsertDatabaseInstances, updateDatabaseInstanceHealth, updateDatabaseInstanceSystemReplication, + updateDatabaseInstanceAbsentAt, setDatabaseInstanceDeregistering, unsetDatabaseInstanceDeregistering, } from '@state/databases'; @@ -129,6 +130,33 @@ describe('Databases reducer', () => { expect(databaseReducer(initialState, action)).toEqual(expectedState); }); + it('should update the absent_at field of an database instance', () => { + const instance = databaseInstanceFactory.build(); + const absentAt = Date.now(); + + const initialState = { + databaseInstances: [instance], + }; + + const instanceToUpdate = { + ...instance, + absent_at: absentAt, + }; + + const action = updateDatabaseInstanceAbsentAt(instanceToUpdate); + + const expectedState = { + databaseInstances: [ + { + ...instance, + absent_at: absentAt, + }, + ], + }; + + expect(databaseReducer(initialState, action)).toEqual(expectedState); + }); + it('should set database instance in deregistering state', () => { const instance = databaseInstanceFactory.build(); diff --git a/assets/js/state/sagas/databases.js b/assets/js/state/sagas/databases.js index 8990d83ce9..b4ba0e855e 100644 --- a/assets/js/state/sagas/databases.js +++ b/assets/js/state/sagas/databases.js @@ -7,6 +7,7 @@ import { DATABASE_RESTORED, DATABASE_HEALTH_CHANGED, DATABASE_INSTANCE_REGISTERED, + DATABASE_INSTANCE_ABSENT_AT_CHANGED, DATABASE_INSTANCE_DEREGISTERED, DATABASE_INSTANCE_HEALTH_CHANGED, DATABASE_INSTANCE_SYSTEM_REPLICATION_CHANGED, @@ -16,6 +17,7 @@ import { updateDatabaseHealth, updateDatabaseInstanceHealth, updateDatabaseInstanceSystemReplication, + updateDatabaseInstanceAbsentAt, removeDatabase, removeDatabaseInstance, setDatabaseInstanceDeregistering, @@ -29,6 +31,7 @@ import { updateSAPSystemDatabaseInstanceSystemReplication, setDatabaseInstanceDeregisteringToSAPSystem, unsetDatabaseInstanceDeregisteringToSAPSystem, + updateDatabaseInstanceAbsentToSAPSystem, } from '@state/sapSystems'; import { getDatabase } from '@state/selectors/sapSystem'; @@ -111,6 +114,20 @@ function* databaseInstanceSystemReplicationChanged({ payload }) { yield put(updateSAPSystemDatabaseInstanceSystemReplication(payload)); } +export function* databaseInstanceAbsentAtChanged({ payload }) { + yield put(updateDatabaseInstanceAbsentAt(payload)); + yield put(updateDatabaseInstanceAbsentToSAPSystem(payload)); + const { sid, absent_at, instance_number } = payload; + yield put( + notify({ + text: `The database instance ${instance_number} from ${sid} is ${ + absent_at ? 'absent' : 'present again' + }`, + icon: 'ℹ️', + }) + ); +} + export function* deregisterDatabaseInstance({ payload, payload: { sid, sap_system_id, host_id, instance_number }, @@ -141,6 +158,10 @@ export function* watchDatabase() { yield takeEvery(DATABASE_RESTORED, databaseRestored); yield takeEvery(DATABASE_HEALTH_CHANGED, databaseHealthChanged); yield takeEvery(DATABASE_INSTANCE_REGISTERED, databaseInstanceRegistered); + yield takeEvery( + DATABASE_INSTANCE_ABSENT_AT_CHANGED, + databaseInstanceAbsentAtChanged + ); yield takeEvery(DATABASE_INSTANCE_DEREGISTERED, databaseInstanceDeregistered); yield takeEvery( DATABASE_INSTANCE_HEALTH_CHANGED, diff --git a/assets/js/state/sagas/databases.test.js b/assets/js/state/sagas/databases.test.js index c80b4f0a01..cb5148cf4f 100644 --- a/assets/js/state/sagas/databases.test.js +++ b/assets/js/state/sagas/databases.test.js @@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; import { recordSaga } from '@lib/test-utils'; import { + databaseInstanceAbsentAtChanged, databaseDeregistered, databaseInstanceDeregistered, databaseRestored, @@ -12,6 +13,7 @@ import { removeDatabase, removeDatabaseInstance, appendDatabase, + updateDatabaseInstanceAbsentAt, setDatabaseInstanceDeregistering, unsetDatabaseInstanceDeregistering, } from '@state/databases'; @@ -20,6 +22,7 @@ import { upsertDatabaseInstancesToSapSystem, setDatabaseInstanceDeregisteringToSAPSystem, unsetDatabaseInstanceDeregisteringToSAPSystem, + updateDatabaseInstanceAbsentToSAPSystem, } from '@state/sapSystems'; import { databaseFactory, @@ -150,4 +153,64 @@ describe('SAP Systems sagas', () => { unsetDatabaseInstanceDeregisteringToSAPSystem(instance), ]); }); + + it('should update the absent_at field when the database instance is marked present', async () => { + const { sap_system_id, instance_number, host_id, sid, absent_at } = + databaseInstanceFactory.build(); + + const dispatched = await recordSaga(databaseInstanceAbsentAtChanged, { + payload: { sap_system_id, instance_number, host_id, sid, absent_at }, + }); + + expect(dispatched).toEqual([ + updateDatabaseInstanceAbsentAt({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + updateDatabaseInstanceAbsentToSAPSystem({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + notify({ + text: `The database instance ${instance_number} from ${sid} is present again`, + icon: 'ℹ️', + }), + ]); + }); + + it('should update the absent_at field when the database instance is marked absent', async () => { + const { sap_system_id, instance_number, host_id, sid, absent_at } = + databaseInstanceFactory.build({ absent_at: new Date().toISOString() }); + + const dispatched = await recordSaga(databaseInstanceAbsentAtChanged, { + payload: { sap_system_id, instance_number, host_id, sid, absent_at }, + }); + + expect(dispatched).toEqual([ + updateDatabaseInstanceAbsentAt({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + updateDatabaseInstanceAbsentToSAPSystem({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + notify({ + text: `The database instance ${instance_number} from ${sid} is absent`, + icon: 'ℹ️', + }), + ]); + }); }); diff --git a/assets/js/state/sagas/sapSystems.js b/assets/js/state/sagas/sapSystems.js index e03ff4a98e..2d19ecd982 100644 --- a/assets/js/state/sagas/sapSystems.js +++ b/assets/js/state/sagas/sapSystems.js @@ -7,6 +7,7 @@ import { APPLICATION_INSTANCE_REGISTERED, APPLICATION_INSTANCE_MOVED, APPLICATION_INSTANCE_HEALTH_CHANGED, + APPLICATION_INSTANCE_ABSENT_AT_CHANGED, APPLICATION_INSTANCE_DEREGISTERED, SAP_SYSTEM_DEREGISTERED, SAP_SYSTEM_RESTORED, @@ -19,6 +20,7 @@ import { removeApplicationInstance, updateApplicationInstanceHost, updateApplicationInstanceHealth, + updateApplicationInstanceAbsentAt, removeSAPSystem, updateSAPSystem, setApplicationInstanceDeregistering, @@ -79,6 +81,19 @@ function* applicationInstanceHealthChanged({ payload }) { yield put(updateApplicationInstanceHealth(payload)); } +export function* applicationInstanceAbsentAtChanged({ payload }) { + yield put(updateApplicationInstanceAbsentAt(payload)); + const { sid, absent_at } = payload; + yield put( + notify({ + text: `The application instance ${sid} is now ${ + absent_at ? 'absent' : 'present' + }.`, + icon: 'ℹ️', + }) + ); +} + export function* sapSystemDeregistered({ payload: { id, sid } }) { yield put(removeSAPSystem({ id })); yield put( @@ -142,6 +157,10 @@ export function* watchSapSystem() { applicationInstanceRegistered ); yield takeEvery(APPLICATION_INSTANCE_MOVED, applicationInstanceMoved); + yield takeEvery( + APPLICATION_INSTANCE_ABSENT_AT_CHANGED, + applicationInstanceAbsentAtChanged + ); yield takeEvery( APPLICATION_INSTANCE_DEREGISTERED, applicationInstanceDeregistered diff --git a/assets/js/state/sagas/sapSystems.test.js b/assets/js/state/sagas/sapSystems.test.js index 1fa10f2a76..4a0c70d5de 100644 --- a/assets/js/state/sagas/sapSystems.test.js +++ b/assets/js/state/sagas/sapSystems.test.js @@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import { recordSaga } from '@lib/test-utils'; import { applicationInstanceMoved, + applicationInstanceAbsentAtChanged, applicationInstanceDeregistered, sapSystemDeregistered, sapSystemRestored, @@ -15,6 +16,7 @@ import { upsertDatabaseInstancesToSapSystem, updateApplicationInstanceHost, upsertApplicationInstances, + updateApplicationInstanceAbsentAt, removeApplicationInstance, updateSAPSystem, setApplicationInstanceDeregistering, @@ -88,6 +90,53 @@ describe('SAP Systems sagas', () => { ); }); + it('should update the absent_at field when the application instance is marked absent', async () => { + const { sap_system_id, instance_number, host_id, sid } = + sapSystemApplicationInstanceFactory.build(); + const absent_at = Date.now(); + + const dispatched = await recordSaga(applicationInstanceAbsentAtChanged, { + payload: { sap_system_id, instance_number, host_id, sid, absent_at }, + }); + + expect(dispatched).toEqual([ + updateApplicationInstanceAbsentAt({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + notify({ + text: `The application instance ${sid} is now absent.`, + icon: 'ℹ️', + }), + ]); + }); + + it('should update the absent_at field when the application instance is marked present', async () => { + const { sap_system_id, instance_number, host_id, sid, absent_at } = + sapSystemApplicationInstanceFactory.build(); + + const dispatched = await recordSaga(applicationInstanceAbsentAtChanged, { + payload: { sap_system_id, instance_number, host_id, sid, absent_at }, + }); + + expect(dispatched).toEqual([ + updateApplicationInstanceAbsentAt({ + sap_system_id, + instance_number, + host_id, + sid, + absent_at, + }), + notify({ + text: `The application instance ${sid} is now present.`, + icon: 'ℹ️', + }), + ]); + }); + it('should remove the application instance', async () => { const { sap_system_id, host_id, instance_number } = sapSystemApplicationInstanceFactory.build(); diff --git a/assets/js/state/sapSystems.js b/assets/js/state/sapSystems.js index 4797f43b1f..39493bd643 100644 --- a/assets/js/state/sapSystems.js +++ b/assets/js/state/sapSystems.js @@ -149,6 +149,20 @@ export const sapSystemsListSlice = createSlice({ } ); }, + updateApplicationInstanceAbsentAt: (state, { payload: instance }) => { + state.applicationInstances = updateInstance( + state.applicationInstances, + instance, + { absent_at: instance.absent_at } + ); + }, + updateDatabaseInstanceAbsentToSAPSystem: (state, { payload: instance }) => { + state.databaseInstances = updateInstance( + state.databaseInstances, + instance, + { absent_at: instance.absent_at } + ); + }, setApplicationInstanceDeregistering: (state, { payload: instance }) => { state.applicationInstances = updateInstance( state.applicationInstances, @@ -191,6 +205,8 @@ export const SAP_SYSTEM_HEALTH_CHANGED = 'SAP_SYSTEM_HEALTH_CHANGED'; export const APPLICATION_INSTANCE_REGISTERED = 'APPLICATION_INSTANCE_REGISTERED'; export const APPLICATION_INSTANCE_MOVED = 'APPLICATION_INSTANCE_MOVED'; +export const APPLICATION_INSTANCE_ABSENT_AT_CHANGED = + 'APPLICATION_INSTANCE_ABSENT_AT_CHANGED'; export const APPLICATION_INSTANCE_DEREGISTERED = 'APPLICATION_INSTANCE_DEREGISTERED'; export const APPLICATION_INSTANCE_HEALTH_CHANGED = @@ -223,9 +239,11 @@ export const { removeTagFromSAPSystem, removeSAPSystem, updateSAPSystem, + updateApplicationInstanceAbsentAt, setApplicationInstanceDeregistering, unsetApplicationInstanceDeregistering, setDatabaseInstanceDeregisteringToSAPSystem, + updateDatabaseInstanceAbsentToSAPSystem, unsetDatabaseInstanceDeregisteringToSAPSystem, } = sapSystemsListSlice.actions; diff --git a/assets/js/state/sapSystems.test.js b/assets/js/state/sapSystems.test.js index 1db50f0afe..9901adbfd3 100644 --- a/assets/js/state/sapSystems.test.js +++ b/assets/js/state/sapSystems.test.js @@ -6,6 +6,7 @@ import sapSystemsReducer, { updateApplicationInstanceHealth, updateSAPSystemDatabaseInstanceHealth, updateSAPSystemDatabaseInstanceSystemReplication, + updateApplicationInstanceAbsentAt, removeApplicationInstance, removeDatabaseInstanceFromSapSystem, updateSAPSystem, @@ -13,6 +14,7 @@ import sapSystemsReducer, { unsetApplicationInstanceDeregistering, setDatabaseInstanceDeregisteringToSAPSystem, unsetDatabaseInstanceDeregisteringToSAPSystem, + updateDatabaseInstanceAbsentToSAPSystem, } from '@state/sapSystems'; import { sapSystemFactory, @@ -184,6 +186,33 @@ describe('SAP Systems reducer', () => { expect(sapSystemsReducer(initialState, action)).toEqual(expectedState); }); + it('should update the absent_at field of an application instance', () => { + const instance = sapSystemApplicationInstanceFactory.build(); + const absentAt = Date.now(); + + const initialState = { + applicationInstances: [instance], + }; + + const instanceToUpdate = { + ...instance, + absent_at: absentAt, + }; + + const action = updateApplicationInstanceAbsentAt(instanceToUpdate); + + const expectedState = { + applicationInstances: [ + { + ...instance, + absent_at: absentAt, + }, + ], + }; + + expect(sapSystemsReducer(initialState, action)).toEqual(expectedState); + }); + it('should remove an application instance from state', () => { const [instance1, instance2] = sapSystemApplicationInstanceFactory.buildList(2); @@ -346,4 +375,29 @@ describe('SAP Systems reducer', () => { expect(sapSystemsReducer(initialState, action)).toEqual(expectedState); }); + + it('should update absent state from database instance', () => { + const instance = databaseInstanceFactory.build({ + absent_at: new Date().toISOString(), + }); + + const initialState = { + databaseInstances: [instance], + }; + + const action = updateDatabaseInstanceAbsentToSAPSystem({ + ...instance, + absent_at: null, + }); + + const expectedState = { + databaseInstances: [ + { + ...instance, + absent_at: null, + }, + ], + }; + expect(sapSystemsReducer(initialState, action)).toEqual(expectedState); + }); });