Я ищу написать Aspect, который можно использовать для макетирования данных в зависимости от некоторого значения, переданного в методе.Этот макет заменит фактический вызов REST.Если значение не совпадает, вернитесь к фактическому методу и сделайте вызов к конечной точке REST.
У меня уже есть написанный аспект, который работает в производстве, но очень грязный и должен был реализовать несколько различных методовчтобы удовлетворить потребность возвращать разные объекты и обрабатывать аргументы разных методов.Есть ли способ, которым я мог бы обобщить это так, чтобы у меня был как единый метод для выполнения работы и возможность использовать, если в будущих случаях мне не нужно беспокоиться о типах объектов и сигнатурах методов.
Текущийреализация осуществляется следующим образом.Как я мог бы изменить это, чтобы работать как единый метод, который может обслуживать будущие методы и типы ответов без необходимости изменять мой аспект?
Чтобы уточнить, у меня есть следующие 3 метода, которые я хочу смоделировать с помощью Aspect.как вы видите, у меня есть пользовательская аннотация, которая принимает имя.Ни один из аргументов метода не совпадает.
@MyMock(name = "ABC")
public AResponse getAResponse(ARequest aRequest){
// make a REST call
}
@MyMock(name = "DEF")
public BResponse getBResponse(String fruit, BRequest bRequest){
// make a REST call
}
@MyMock(name = "GHJ")
public CResponse getCResponse(String vehicle, CRequest cRequest, int id){
// make a REST call
}
Класс аспектов в настоящее время выглядит следующим образом.Я использую значение имени в аннотации, чтобы определить, какой метод вызывать и какой тип значения возвращать.Как видите, это не очень масштабируемо.Мне нужно писать новый метод и логику каждый раз, когда я внедряю новые макеты.
@Around("@annotation(myMock)")
public Object getMockedData(ProceedingJoinPoint pjp, MyMock myMock) throws Throwable {
String servName = performMocking.name();
Object[] methodArguments = pjp.getArgs();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Class returnType = signature.getReturnType();
switch (myMock.name()) {
case "ABC":
getA(methodArguments[0]);
break;
case "DEF":
getB(methodArguments[0]);
break;
case "GHJ":
getC(methodArguments[2]);
break;
}
return pjp.proceed(methodArguments);
}
private AResponse getA(ARequest aRequest){
// I use methodArguments[0] (which is aRequest) to decide what value to return as a mock response here.
// There is a getName value in that object which I use to reference
}
private BResponse getB(BRequest bRequest){
// I use methodArguments[0] (which is a String) to decide what value to return as a mock response here.
}
private CResponse getC(CRequest cRequest){
// I use methodArguments[2] (which is an int) to decide what value to return as a mock response here.
}
Все вышеперечисленные методы get вызывают внешний JSON-файл для извлечения фиктивных данных.Содержание файлов выглядит следующим образом.Если ключи совпадают, будет дан ложный ответ, иначе вернитесь к фактическому вызову REST.
{
"ABC": {
"enabled": "true",
"responses": [
{
"a_name": "{some valid json response matching the AResponse Object structure}"
}
]
},
"DEF": {
"enabled": "true",
"responses": [
{
"d_name": "{some valid json response matching the BResponse Object structure}"
}
]
},
"GHJ": {
"enabled": "true",
"responses": [
{
"123": "{some valid json response matching the CResponse Object structure}"
}
]
}
}
РЕДАКТИРОВАТЬ: добавлен мой класс ASPECT следующим образом:
package com.company.a.b.mock;
import com.domain.abc.b.logging.util.MyLogger;
import com.domain.abc.a.util.generic.ConfigHelper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Aspect
@Component
public class AspectMocking {
private final ConfigHelper configHelper;
private final MyLogger myLogger;
public AspectMocking(ConfigHelper configHelper, MyLogger myLogger) {
this.configHelper = configHelper;
this.myLogger = myLogger;
}
@Around("@annotation(myMock)")
public Object getMockedData(ProceedingJoinPoint pjp, MyMock myMock) throws Throwable {
final String env = System.getProperty("spring.profiles.active");
String response = null;
Object returnObject = null;
String logMessage = null;
String servName = myMock.name();
Object[] methodArguments = pjp.getArgs();
try {
if ("test_env1".equals(env) || "test_env2".equals(env)) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Class returnType = signature.getReturnType();
Map allDataFromMockedFile = getMockedDataFromFile();
Map getResultForKey = (Map) allDataFromMockedFile.get(myMock.name());
List result = null;
if(getResultForKey != null){
result = (List) getResultForKey.get("responses");
}
switch (myMock.name()) {
case "ABC":
response = fetchABCMockedData(result, (String) methodArguments[0]);
logMessage = "Fetching ABC mock data for ABC Response: " + response;
break;
case "V2_ABC":
response = fetchABCMockedData(getIntlResult(myMock.name(), (String)methodArguments[2]), (String) methodArguments[0]);
logMessage = "Fetching V2 ABC mock data for V2 ABC Response: " + response;
break;
case "DEF":
response = fetchDEFMockedData(result, (String) methodArguments[0]);
logMessage = "Fetching DEF mock data: " + response;
break;
case "GHJ":
response = fetchGHJMockedOfferData(result, (String) methodArguments[0], (String) methodArguments[1], (String) methodArguments[2]);
logMessage = "Fetching GHJ mock data: " + response;
break;
}
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
returnObject = mapper.readValue(response, returnType);
}
} catch (Exception exp) {
myLogger.addMessageAsWarning(String
.format("Exception occured for service %s while loading the mocked json, so hitting the actual service:"
+ exp.getMessage(), servName));
}
if (returnObject == null) {
returnObject = pjp.proceed(methodArguments);
}
myLogger.addMessage(logMessage);
return returnObject;
}
private List getIntlResult(String name, String locale){
Map localBasedMockFile = getMockedDataFromFile(locale.toLowerCase());
Map localeSpecificMockedData = (Map) localBasedMockFile.get(name);
return (List) localeSpecificMockedData.get("responses");
}
// had to add this recently as we needed locale values now to go to a diff file.
private Map getMockedDataFromFile(String countryCode){
final String DATA_URL = String.format("/a/b/c/%s/data.json", countryCode);
return configHelper.getMAVDataAsObject(DATA_URL, Map.class);
}
private Map getMockedDataFromFile() {
final String DATA_URL = "/a/b/c/zh/mock.json";
return configHelper.getMAVDataAsObject(DATA_URL, Map.class);
}
private String fetchABCMockedData(List<Map> allResponses, String vinNumber) throws IOException {
String response = null;
for (Map m : allResponses) {
String mockedVinNumber = m.keySet().toString().replaceAll("[\\[\\]]", "");
if (vinNumber.equals(mockedVinNumber)) {
response = (String) m.get(mockedVinNumber);
}
}
return response;
}
private String fetchDEFMockedData(List<String> allResponses, String vinNumber) {
return allResponses.contains(vinNumber) ? "true" : "false";
}
private String fetchGHJMockedOfferData(List<Map> allResponses, String journey, String name, String pin) {
String response = null;
String key = journey+"_"+name + "_" + pin;
for (Map m : allResponses) {
String mockedKey = m.keySet().toString().replaceAll("[\\[\\]]", "");
if (mockedKey.equals(key)) {
response = (String) m.get(mockedKey);
}
}
return response;
}
}