Skip to content

Commit

Permalink
fix(m360-api): code enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
vincejv committed Aug 29, 2023
1 parent e94b599 commit 60c91e4
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.abavilla.fpi.sms.codec;

import java.util.ArrayList;
import java.util.List;

import com.vincejv.m360.dto.ApiRequest;
Expand All @@ -27,6 +28,7 @@
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.ClassModel;
import org.bson.codecs.pojo.ClassModelBuilder;
import org.bson.codecs.pojo.PojoCodecProvider;

/**
Expand All @@ -39,10 +41,14 @@ public class M360CodecProvider implements CodecProvider {
@SuppressWarnings("rawtypes")
private static final List<Class> discriminatorClasses;

private static final List<String> ignoredFields;

static {
discriminatorClasses = List.of(
ApiRequest.class, BroadcastRequest.class, SMSRequest.class
);

ignoredFields = List.of("appKey", "appSecret");
}

@Override
Expand All @@ -53,12 +59,30 @@ public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
return null; // Don't throw here, this tells
}

private static <T> Codec<T> buildDiscriminatorCodec(Class<T> clazz, CodecRegistry registry) {
ClassModel<T> discriminatorModel = ClassModel.builder(clazz)
.enableDiscriminator(true).build();
private <T> Codec<T> buildDiscriminatorCodec(Class<T> clazz, CodecRegistry registry) {
var dscmntrMdlBldr = ClassModel.builder(clazz)
.enableDiscriminator(true);
if (clazz == BroadcastRequest.class) {
stripNonProperties(dscmntrMdlBldr);
}
return PojoCodecProvider.builder()
.register(discriminatorModel)
.register(dscmntrMdlBldr.build())
.build().get(clazz, registry);
}

private <T> void stripNonProperties(final ClassModelBuilder<T> builder) {
List<String> names = new ArrayList<>();

for (var property : builder.getPropertyModelBuilders()) {
var name = property.getName();
if (ignoredFields.contains(name)) {
names.add(name);
}
}

for (var name : names) {
builder.removeProperty(name);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Uni<BroadcastResponseDto> sendMsg(MsgReqDto msgReqDto) {
smsRequest.setDataCodingScheme(SMSUtil.isEncodeableInGsm0338(msgReqDto.getContent()) ?
DCSCoding.GSM0338.getId() : DCSCoding.UCS2.getId());

var m360Resp = Uni.createFrom().completionStage(() ->
var m360Resp = Uni.createFrom().future(() ->
m360client.sendBroadcastMessage(smsRequest));
return m360Resp.map(resp -> {
var dto = new BroadcastResponseDto();
Expand Down
72 changes: 32 additions & 40 deletions core/src/main/java/com/abavilla/fpi/sms/service/sms/MsgAckSvc.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;

import com.abavilla.fpi.fw.dto.impl.NullDto;
import com.abavilla.fpi.fw.exceptions.ApiSvcEx;
import com.abavilla.fpi.fw.exceptions.OptimisticLockEx;
import com.abavilla.fpi.fw.service.AbsSvc;
import com.abavilla.fpi.fw.util.DateUtil;
import com.abavilla.fpi.sms.entity.sms.LeakAck;
import com.abavilla.fpi.sms.entity.sms.MsgReq;
import com.abavilla.fpi.sms.entity.sms.StateEncap;
import com.abavilla.fpi.sms.repo.sms.MsgReqRepo;
import com.abavilla.fpi.sms.util.M360Const;
Expand All @@ -39,13 +37,12 @@
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.eclipse.microprofile.context.ManagedExecutor;

@ApplicationScoped
public class MsgAckSvc extends AbsSvc<NullDto, LeakAck> {

public static final int MAX_RETRIES = 5;
@Inject
MsgReqRepo msgReqRepo;

Expand All @@ -56,46 +53,41 @@ public class MsgAckSvc extends AbsSvc<NullDto, LeakAck> {
ManagedExecutor executor;

public Uni<Void> acknowledge(String msgId, String ackStsCde, String ackTimestamp) {
ApiStatus apiStatus = ApiStatus.fromId(Integer.parseInt(ackStsCde));
var apiStatus = ApiStatus.fromId(Integer.parseInt(ackStsCde));
var ackTime = DateUtil.modLdtToUtc(
DateUtil.parseStrDateToLdt(ackTimestamp, M360Const.M360_TIMESTAMP_FORMAT));
Uni<Optional<MsgReq>> byMsgId = msgReqRepo.findByMsgId(msgId);
DateUtil.parseStrDateToLdt(ackTimestamp, M360Const.M360_TIMESTAMP_FORMAT));
var byMsgId = msgReqRepo.findByMsgId(msgId);

/* run in background, immediately return response to webhook */
executor.execute(() ->
byMsgId.chain(msgReqOpt -> {
if (msgReqOpt.isPresent()) {
return Uni.createFrom().item(msgReqOpt.get());
} else {
throw new ApiSvcEx("Message Id for acknowledgement not found: " + msgId);
}
})
.onFailure(OptimisticLockEx.class).retry().indefinitely()
.onFailure(ApiSvcEx.class)
.retry().withBackOff(Duration.ofSeconds(3)).withJitter(0.2)
.atMost(5) // Retry for item not found and nothing else
.chain(msgReq -> {
var stateItem = new StateEncap(apiStatus, ackTime);
if (CollectionUtils.isEmpty(msgReq.getApiStatus())) {
msgReq.setApiStatus(List.of(stateItem));
} else {
msgReq.getApiStatus().add(stateItem);
}
msgReq.setLastAcknowledgement(ackTime);
msgReq.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
return msgReqRepo.persistOrUpdate(msgReq);
})
.onFailure().call(ex -> {
Log.error("Error leak ack", ex);
LeakAck leak = new LeakAck();
leak.setDateCreated(LocalDateTime.now(ZoneOffset.UTC));
leak.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
leak.setMsgId(msgId);
leak.setApiStatus(apiStatus);
leak.setTimestamp(ackTime);
return repo.persist(leak);
}).onFailure().recoverWithNull() // still has to recover from Exception (LateAck)
.await().indefinitely()
byMsgId.map(msgReqOpt -> msgReqOpt.orElseThrow(() ->
new ApiSvcEx("Message Id for acknowledgement not found: " + msgId)))
.onFailure(OptimisticLockEx.class).retry().indefinitely()
.onFailure(ApiSvcEx.class)
.retry().withBackOff(Duration.ofSeconds(3)).withJitter(0.2)
.atMost(5) // Retry for item not found and nothing else
.chain(msgReq -> {
var stateItem = new StateEncap(apiStatus, ackTime);
if (ObjectUtils.isEmpty(msgReq.getApiStatus())) {
msgReq.setApiStatus(List.of(stateItem));
} else {
msgReq.getApiStatus().add(stateItem);
}
msgReq.setLastAcknowledgement(ackTime);
msgReq.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
return msgReqRepo.persistOrUpdate(msgReq);
})
.onFailure().call(ex -> {
Log.error("Error leak ack", ex);
var leak = new LeakAck();
leak.setDateCreated(LocalDateTime.now(ZoneOffset.UTC));
leak.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
leak.setMsgId(msgId);
leak.setApiStatus(apiStatus);
leak.setTimestamp(ackTime);
return repo.persist(leak);
})
.await().indefinitely()
);

return Uni.createFrom().voidItem();
Expand Down
112 changes: 45 additions & 67 deletions core/src/main/java/com/abavilla/fpi/sms/service/sms/MsgReqSvc.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,40 +76,7 @@ public Uni<MsgReqStatusDto> sendMsg(MsgReqDto msgReqDto) {
return Uni.createFrom().item(status);
}

return
m360Svc.sendMsg(msgReqDto)
.onFailure().recoverWithItem(ex -> { // | api failure
Log.error("m360 api error: " + ex);
var broadcastResponseDto = new BroadcastResponseDto();
if (ex instanceof ApiSvcEx apiEx) {
broadcastResponseDto.setCode(apiEx.getHttpResponseStatus().code());
broadcastResponseDto.setName(apiEx.getHttpResponseStatus().reasonPhrase());
broadcastResponseDto.setMessage(Collections.singletonList(apiEx.getMessage()));
}
return broadcastResponseDto;
})
.chain(respDto -> {
var msgReq = msgReqMapper.mapFromResponse(respDto);
msgReq.setDateCreated(LocalDateTime.now(ZoneOffset.UTC));
msgReq.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
var stateItem = new StateEncap(respDto.getCode() ==
HttpResponseStatus.CREATED.code() ? ApiStatus.WAIT // success
: ApiStatus.REJ, // api failure
LocalDateTime.now(ZoneOffset.UTC));
msgReq.setApiStatus(List.of(stateItem));
return repo.persist(msgReq).onFailure().recoverWithNull(); // if failed to save to mongo, return null
})
.map(respMongo -> {
Log.debug("mongo save: " + respMongo); // receive request from to save to mongo
if (respMongo != null) {
status.setStatus(respMongo
.getName().equalsIgnoreCase("Created") ? SMSConst.SMS_SENT_SUCCESS :
SMSConst.SMS_API_FAIL); // if both mongo and api is success
} else {
status.setStatus(SMSConst.SMS_MONGO_FAIL); // mongo failure
}
return status;
});
return callMsgSvc(status, msgReqDto);
}

public Uni<MsgReqStatusDto> sendBulkMsg(BulkMsgReqDto bulkMsgReqDto) {
Expand All @@ -131,39 +98,8 @@ public Uni<MsgReqStatusDto> sendBulkMsg(BulkMsgReqDto bulkMsgReqDto) {
msgReq.setMobileNumber(mobile);
msgReq.setContent(filteredReq.getContent());

var sendMsg = m360Svc.sendMsg(msgReq)
.onFailure().recoverWithItem(ex -> { // | api failure
Log.error("m360 api error: " + ex);
var apiResp = new BroadcastResponseDto();
if (ex instanceof ApiSvcEx apiEx) {
apiResp.setCode(apiEx.getHttpResponseStatus().code());
apiResp.setName(apiEx.getHttpResponseStatus().reasonPhrase());
apiResp.setMessage(Collections.singletonList(apiEx.getMessage()));
}
return apiResp;
})
.chain(apiResp -> {
var resp = msgReqMapper.mapFromResponse(apiResp);
resp.setDateCreated(LocalDateTime.now(ZoneOffset.UTC));
resp.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
var stateItem = new StateEncap(apiResp.getCode() ==
HttpResponseStatus.CREATED.code() ? ApiStatus.WAIT // success
: ApiStatus.REJ, // api failure
LocalDateTime.now(ZoneOffset.UTC));
resp.setApiStatus(List.of(stateItem));
return repo.persist(resp).onFailure().recoverWithNull(); // if failed to save to mongo, return null
})
.map(respMongo -> {
Log.debug("mongo save: " + respMongo); // receive request from to save to mongo
if (respMongo != null) {
status.setStatus(respMongo
.getName().equalsIgnoreCase("Created") ? SMSConst.SMS_SENT_SUCCESS :
SMSConst.SMS_API_FAIL); // if both mongo and api is success
} else {
status.setStatus(SMSConst.SMS_MONGO_FAIL); // mongo failure
}
return status;
});
// | api failure
var sendMsg = callMsgSvc(status, msgReq);
bulkSend = bulkSend.add(sendMsg);
}

Expand All @@ -178,6 +114,48 @@ public Uni<MsgReqStatusDto> sendBulkMsg(BulkMsgReqDto bulkMsgReqDto) {
});
}

private Uni<MsgReq> mapBroadcastRespToMsgReq(BroadcastResponseDto respDto) {
var msgReq = msgReqMapper.mapFromResponse(respDto);
msgReq.setDateCreated(LocalDateTime.now(ZoneOffset.UTC));
msgReq.setDateUpdated(LocalDateTime.now(ZoneOffset.UTC));
var stateItem = new StateEncap(respDto.getCode() ==
HttpResponseStatus.CREATED.code() ? ApiStatus.WAIT // success
: ApiStatus.REJ, // api failure
LocalDateTime.now(ZoneOffset.UTC));
msgReq.setApiStatus(List.of(stateItem));
return repo.persist(msgReq).onFailure().recoverWithNull();
}

private BroadcastResponseDto recoverFromError(Throwable ex) {
Log.error("m360 api error: " + ex);
var broadcastResponseDto = new BroadcastResponseDto();
if (ex instanceof ApiSvcEx apiEx) {
broadcastResponseDto.setCode(apiEx.getHttpResponseStatus().code());
broadcastResponseDto.setName(apiEx.getHttpResponseStatus().reasonPhrase());
broadcastResponseDto.setMessage(Collections.singletonList(apiEx.getMessage()));
}
return broadcastResponseDto;
}

private Uni<MsgReqStatusDto> callMsgSvc(MsgReqStatusDto status, MsgReqDto msgReq) {
return m360Svc.sendMsg(msgReq)
.onFailure().recoverWithItem(this::recoverFromError) // | api failure
.chain(this::mapBroadcastRespToMsgReq)
.map(respMongo -> determineMsgReqSts(status, respMongo));
}

private static MsgReqStatusDto determineMsgReqSts(MsgReqStatusDto status, MsgReq respMongo) {
Log.debug("mongo save: " + respMongo); // receive request from to save to mongo
if (respMongo != null) {
status.setStatus(respMongo
.getName().equalsIgnoreCase("Created") ? SMSConst.SMS_SENT_SUCCESS :
SMSConst.SMS_API_FAIL); // if both mongo and api is success
} else {
status.setStatus(SMSConst.SMS_MONGO_FAIL); // mongo failure
}
return status;
}

/**
* Checks the validity and formats the given phone number to the E164 format as accepted by backend SMS API.
* @param msgReqDto {@link MsgReqDto} Object containing phone number given, the formatted phone number will be
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
<dependency>
<groupId>com.vincejv</groupId>
<artifactId>m360-api-client</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</dependency>

</dependencies>
Expand Down

0 comments on commit 60c91e4

Please sign in to comment.