Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BAH-2962 | Addition of recurring Appointments events publisher #13

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9e7827e
[ Ritesh ] | BAH-3086 - Segregate bahmni patient create and update ev…
riteshghiya90 Jul 10, 2023
04c348d
[ Ritesh ] | BAH-3086 - Changes as per review comments
riteshghiya90 Jul 10, 2023
426b22f
[ Ritesh ] | BAH-3086 - Replaces Header keys with Enum
riteshghiya90 Jul 10, 2023
4e9bf50
[ Ritesh ] | BAH-3086 - Minor fix
riteshghiya90 Jul 10, 2023
6102159
[ Ritesh ] | BAH-3086 - Added non-null check in JMSMessageCreatorTest
riteshghiya90 Jul 11, 2023
976cd80
BAH-3118 | added publish event for appointments
kavitha-sundararajan Jul 20, 2023
657c586
BAH-3118 | added todo for workaround in patient and appointment advice
kavitha-sundararajan Jul 24, 2023
172181b
BAH-3118 | added missing dependency for appointment event
kavitha-sundararajan Jul 24, 2023
d43fe3b
Added workflow dispatch for publishing omod
kavitha-sundararajan Jul 24, 2023
e897a75
[Fix] BAH-3118 | added bahmni maven repository
kavitha-sundararajan Jul 24, 2023
3aa9bcb
[Fix] BAH-3118 | added sonatype nexus repository
kavitha-sundararajan Jul 24, 2023
ba41bfc
[Fix] BAH-3118 | added appointments omod dependency
kavitha-sundararajan Jul 25, 2023
a477277
BAH-3124 | added Encounter create/update events
kavitha-sundararajan Jul 26, 2023
a67910b
Merge pull request #10 from Bahmni/BAH-3124
kavitha-sundararajan Jul 27, 2023
8b30205
[Ritesh] [BAH-3191] Fix issue occured during openmrs version upgrade …
riteshghiya90 Sep 4, 2023
3a334c8
Added recurring appointment publisher to publish events and listen in…
anubhavBeehyv Sep 14, 2023
cb6f0ec
Merge branch 'main' into BAH-2962
anubhavBeehyv Sep 14, 2023
1bcaaf3
Refactored workflow.
anubhavBeehyv Sep 14, 2023
96c9e50
Changes in tests to add context before triggering the events .
anubhavBeehyv Sep 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ target
*.war
*.ear
.idea
api/
.DS_Store

target/
Expand Down
13 changes: 12 additions & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,18 @@
<artifactId>webservices.rest-omod-2.0</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.bahmni.module</groupId>
<artifactId>appointments-ipd-api</artifactId>
<version>1.8.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bahmni.module</groupId>
<artifactId>appointments-ipd-omod</artifactId>
<version>1.8.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.bahmni.module.events.api.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.bahmni.module.events.api.publisher.BahmniEventPublisher;
import org.bahmni.module.events.api.publisher.JMSEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.bahmni.module.events.api.listener;

import com.google.common.collect.Sets;
import org.bahmni.module.events.api.model.BahmniEventType;
import org.bahmni.module.events.api.model.Event;
import org.bahmni.module.events.api.publisher.BahmniEventPublisher;
import org.openmrs.api.context.Context;
import org.openmrs.module.appointments.model.Appointment;
import org.openmrs.module.appointments.web.mapper.AppointmentMapper;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import static org.bahmni.module.events.api.model.BahmniEventType.BAHMNI_APPOINTMENT_CREATED;
import static org.bahmni.module.events.api.model.BahmniEventType.BAHMNI_APPOINTMENT_UPDATED;

public class AppointmentAdvice implements AfterReturningAdvice, MethodBeforeAdvice {

private final BahmniEventPublisher eventPublisher;
private final ThreadLocal<Map<String,Integer>> threadLocal = new ThreadLocal<>();
private final String APPOINTMENT_ID_KEY = "appointmentId";
private final AppointmentMapper appointmentMapper;
private final Set<String> adviceMethodNames = Sets.newHashSet("validateAndSave");

public AppointmentAdvice() {
this.eventPublisher = Context.getRegisteredComponent("bahmniEventPublisher", BahmniEventPublisher.class);
this.appointmentMapper = Context.getRegisteredComponent("appointmentMapper", AppointmentMapper.class);
}

public AppointmentAdvice(BahmniEventPublisher bahmniEventPublisher, AppointmentMapper appointmentMapper) {
this.eventPublisher = bahmniEventPublisher;
this.appointmentMapper = appointmentMapper;
}

@Override
public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) {
if (adviceMethodNames.contains(method.getName())) {
Map<String, Integer> appointmentInfo = threadLocal.get();
// TODO: This is a workaround to avoid publishing duplicate events because currently the event is getting called twice. Need to find out the reason and resolve it.
if (appointmentInfo != null) {
BahmniEventType eventType = appointmentInfo.get(APPOINTMENT_ID_KEY) == null ? BAHMNI_APPOINTMENT_CREATED : BAHMNI_APPOINTMENT_UPDATED;
threadLocal.remove();
Appointment appointment = (Appointment) returnValue;
Object representation = appointmentMapper.constructResponse(appointment);
Event event = new Event(eventType, representation, appointment.getUuid());
eventPublisher.publishEvent(event);
}
}
}

@Override
public void before(Method method, Object[] objects, Object o) {
if (adviceMethodNames.contains(method.getName())) {
Appointment appointment = ((Supplier<Appointment>) objects[0]).get();
Map<String, Integer> appointmentInfo = new HashMap<>(1);
appointmentInfo.put(APPOINTMENT_ID_KEY, appointment.getId());
threadLocal.set(appointmentInfo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.bahmni.module.events.api.listener;

import com.google.common.collect.Sets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bahmni.module.events.api.model.BahmniEventType;
import org.bahmni.module.events.api.model.Event;
import org.bahmni.module.events.api.publisher.BahmniEventPublisher;
import org.openmrs.Encounter;
import org.openmrs.api.context.Context;
import org.openmrs.module.appointments.web.mapper.AppointmentMapper;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.lang.NonNull;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static org.bahmni.module.events.api.model.BahmniEventType.*;

public class EncounterAdvice implements AfterReturningAdvice, MethodBeforeAdvice {

private final Logger log = LogManager.getLogger(EncounterAdvice.class);

private final BahmniEventPublisher eventPublisher;
private final ThreadLocal<Map<String,Integer>> threadLocal = new ThreadLocal<>();
private final String ENCOUNTER_ID_KEY = "encounterId";
private final Set<String> adviceMethodNames = Sets.newHashSet("saveEncounter");

public EncounterAdvice() {
this.eventPublisher = Context.getRegisteredComponent("bahmniEventPublisher", BahmniEventPublisher.class);
}

public EncounterAdvice(BahmniEventPublisher bahmniEventPublisher) {
this.eventPublisher = bahmniEventPublisher;
}

@Override
public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) {
if (adviceMethodNames.contains(method.getName())) {
Map<String, Integer> encounterInfo = threadLocal.get();
// TODO: This is a workaround to avoid publishing duplicate events because currently the event is getting called twice. Need to find out the reason and resolve it.
if (encounterInfo != null) {
BahmniEventType eventType = encounterInfo.get(ENCOUNTER_ID_KEY) == null ? BAHMNI_ENCOUNTER_CREATED : BAHMNI_ENCOUNTER_UPDATED;
threadLocal.remove();
Encounter encounter = (Encounter) returnValue;

Object representation = ConversionUtil.convertToRepresentation(encounter, Representation.FULL);
Event event = new Event(eventType, representation, encounter.getUuid());
eventPublisher.publishEvent(event);

log.info("Successfully published event with uuid : " + encounter.getUuid());
}
}
}

@Override
public void before(Method method, Object[] objects, Object o) {
if (adviceMethodNames.contains(method.getName())) {
Encounter encounter = (Encounter) objects[0];
Map<String, Integer> encounterInfo = new HashMap<>(1);
encounterInfo.put(ENCOUNTER_ID_KEY, encounter.getId());
threadLocal.set(encounterInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,20 @@ public PatientAdvice(BahmniEventPublisher bahmniEventPublisher) {
public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) {
if (adviceMethodNames.contains(method.getName())) {
Map<String, Integer> patientInfo = threadLocal.get();
BahmniEventType eventType = patientInfo != null && patientInfo.get(PATIENT_ID_KEY) == null ? BAHMNI_PATIENT_CREATED : BAHMNI_PATIENT_UPDATED;
threadLocal.remove();
if (patientInfo != null) {
BahmniEventType eventType = patientInfo.get(PATIENT_ID_KEY) == null ? BAHMNI_PATIENT_CREATED : BAHMNI_PATIENT_UPDATED;
threadLocal.remove();

Patient patient = (Patient) returnValue;
Patient patient = (Patient) returnValue;

Object representation = ConversionUtil.convertToRepresentation(patient, Representation.FULL);
Event event = new Event(eventType, representation, patient.getUuid());
eventPublisher.publishEvent(event);
Object representation = ConversionUtil.convertToRepresentation(patient, Representation.FULL);
Event event = new Event(eventType, representation, patient.getUuid());
eventPublisher.publishEvent(event);

log.info("Successfully published event with uuid : " + patient.getUuid());
log.info("Successfully published event with uuid : " + patient.getUuid());
}
}
}

@Override
public void before(Method method, Object[] objects, Object o) {
if (adviceMethodNames.contains(method.getName())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.bahmni.module.events.api.listener;

import com.google.common.collect.Sets;
import org.bahmni.module.events.api.model.BahmniEventType;
import org.bahmni.module.events.api.model.Event;
import org.bahmni.module.events.api.publisher.BahmniEventPublisher;
import org.openmrs.api.context.Context;
import org.openmrs.module.appointments.model.Appointment;
import org.openmrs.module.appointments.model.AppointmentRecurringPattern;
import org.openmrs.module.appointments.web.mapper.AppointmentMapper;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import static org.bahmni.module.events.api.model.BahmniEventType.BAHMNI_RECURRING_APPOINTMENT_CREATED;
import static org.bahmni.module.events.api.model.BahmniEventType.BAHMNI_RECURRING_APPOINTMENT_UPDATED;

public class RecurringAppointmentAdvice implements AfterReturningAdvice, MethodBeforeAdvice {

private final BahmniEventPublisher eventPublisher;
private final ThreadLocal<Map<String,Integer>> threadLocal = new ThreadLocal<>();
private final String RECURRING_APPOINTMENT_ID_KEY = "recurringAppointmentId";
private final AppointmentMapper appointmentMapper;
private final Set<String> adviceMethodNames = Sets.newHashSet("validateAndSave");

public RecurringAppointmentAdvice() {
this.eventPublisher = Context.getRegisteredComponent("bahmniEventPublisher", BahmniEventPublisher.class);
this.appointmentMapper = Context.getRegisteredComponent("appointmentMapper", AppointmentMapper.class);
}

public RecurringAppointmentAdvice(BahmniEventPublisher bahmniEventPublisher, AppointmentMapper appointmentMapper) {
this.eventPublisher = bahmniEventPublisher;
this.appointmentMapper = appointmentMapper;
}

@Override
public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) {
if (adviceMethodNames.contains(method.getName())) {
Map<String, Integer> appointmentInfo = threadLocal.get();
// TODO: This is a workaround to avoid publishing duplicate events because currently the event is getting called twice. Need to find out the reason and resolve it.
if (appointmentInfo != null) {
BahmniEventType eventType = appointmentInfo.get(RECURRING_APPOINTMENT_ID_KEY) == null ? BAHMNI_RECURRING_APPOINTMENT_CREATED : BAHMNI_RECURRING_APPOINTMENT_UPDATED;
threadLocal.remove();
AppointmentRecurringPattern appointmentRecurringPattern = (AppointmentRecurringPattern) returnValue;
Appointment appointment=appointmentRecurringPattern.getAppointments().iterator().next();
Object representation = appointmentMapper.constructResponse(appointment);
Event event = new Event(eventType, representation,appointment.getUuid());
eventPublisher.publishEvent(event);
}
}
}

@Override
public void before(Method method, Object[] objects, Object o) {
if (adviceMethodNames.contains(method.getName())) {
AppointmentRecurringPattern appointmentRecurringPattern = (AppointmentRecurringPattern)objects[0];
Map<String, Integer> appointmentInfo = new HashMap<>(1);
appointmentInfo.put(RECURRING_APPOINTMENT_ID_KEY, appointmentRecurringPattern.getId());
threadLocal.set(appointmentInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

public enum BahmniEventType {
BAHMNI_PATIENT_CREATED("bahmni-patient"),
BAHMNI_PATIENT_UPDATED("bahmni-patient");
BAHMNI_PATIENT_UPDATED("bahmni-patient"),
BAHMNI_APPOINTMENT_CREATED("bahmni-appointment"),
BAHMNI_APPOINTMENT_UPDATED("bahmni-appointment"),
BAHMNI_RECURRING_APPOINTMENT_CREATED("bahmni-recurring-appointment"),
BAHMNI_RECURRING_APPOINTMENT_UPDATED("bahmni-recurring-appointment"),
BAHMNI_ENCOUNTER_CREATED("bahmni-encounter"),
BAHMNI_ENCOUNTER_UPDATED("bahmni-encounter");

private final String topic;
BahmniEventType(String topic) {
this.topic = topic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.bahmni.module.events.api.model;

import org.openmrs.api.context.Context;
import org.openmrs.api.context.UserContext;

import java.time.LocalDateTime;
import java.util.UUID;

Expand All @@ -11,12 +14,13 @@ public class Event {
public final String payloadId;
public final Object payload;
public final LocalDateTime publishedDateTime;

public final UserContext userContext;
public Event(BahmniEventType eventType, Object payload, String payloadId) {
this.eventType = eventType;
this.payload = payload;
this.eventId = UUID.randomUUID().toString();
this.payloadId = payloadId;
this.publishedDateTime = LocalDateTime.now();
this.userContext= Context.getUserContext();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.bahmni.module.events.api.publisher;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bahmni.module.events.api.model.Event;
import org.springframework.context.event.EventListener;
import org.springframework.jms.core.JmsTemplate;

public class EventPublisher {

private static final Logger log = LogManager.getLogger(EventPublisher.class);

private final JmsTemplate jmsTemplate;

private final ObjectMapper objectMapper;

public EventPublisher(JmsTemplate jmsTemplate, ObjectMapper objectMapper) {
this.jmsTemplate = jmsTemplate;
this.objectMapper = objectMapper;
}

@EventListener
public void onApplicationEvent(Event event) {
jmsTemplate.send(event.eventType.topic(), new JMSMessageCreator(objectMapper, event));
log.info("Published Message with id : " + event.payloadId);
}
}
Loading
Loading