Impl multivalue flow (#17)

* Impl multivalue flow

* Fixes
This commit is contained in:
Egor Cherniak 2023-07-04 18:37:01 +03:00 committed by GitHub
parent 577c60c239
commit de0faa7d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 83 additions and 31 deletions

View File

@ -48,7 +48,7 @@
<dependency> <dependency>
<groupId>dev.vality</groupId> <groupId>dev.vality</groupId>
<artifactId>mayday-proto</artifactId> <artifactId>mayday-proto</artifactId>
<version>1.7-b2b2b3b</version> <version>1.8-d79c25b</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>dev.vality</groupId> <groupId>dev.vality</groupId>

View File

@ -0,0 +1,20 @@
package dev.vality.alert.tg.bot.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ObjectMapperConfig {
@Bean
public ObjectMapper jsonMapper() {
return new ObjectMapper()
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
}
}

View File

@ -21,6 +21,7 @@ import org.telegram.telegrambots.meta.api.objects.inlinequery.result.InlineQuery
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set;
import static dev.vality.alert.tg.bot.utils.StringSearchUtils.*; import static dev.vality.alert.tg.bot.utils.StringSearchUtils.*;
@ -63,7 +64,7 @@ public class InlineHandler implements CommonHandler<AnswerInlineQuery> {
String alertId = substringAlertId(inlineQuery); String alertId = substringAlertId(inlineQuery);
String paramId = substringParamId(inlineQuery); String paramId = substringParamId(inlineQuery);
ParametersData parametersData = parametersDao.getByAlertIdAndParamId(alertId, paramId); ParametersData parametersData = parametersDao.getByAlertIdAndParamId(alertId, paramId);
List<String> options = jsonMapper.toList(parametersData.getOptionsValues()); Set<String> options = jsonMapper.toSet(parametersData.getOptionsValues());
options.forEach(optionValue -> { options.forEach(optionValue -> {
if (isParamInList(optionValue, inlineQuery, alertId, paramId)) { if (isParamInList(optionValue, inlineQuery, alertId, paramId)) {
queryResultArticleList.add(fillInlineQueryResultArticle( queryResultArticleList.add(fillInlineQueryResultArticle(

View File

@ -10,7 +10,7 @@ import org.apache.thrift.TException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import java.util.Map; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@Component @Component
@ -24,14 +24,22 @@ public class CreateParamsRequestMapper {
public SendMessage createRequest(Long userId, String paramName, String paramValue) throws TException { public SendMessage createRequest(Long userId, String paramName, String paramValue) throws TException {
StateData stateData = stateDataDao.getByUserId(userId); StateData stateData = stateDataDao.getByUserId(userId);
Map<String, String> paramMap = jsonMapper.toMap(stateData.getMapParams()); Map<String, Set<String>> paramMap = jsonMapper.toMap(stateData.getMapParams());
ParametersData parametersData = parametersDao.getByAlertIdAndParamName(stateData.getAlertId(), paramName); ParametersData parametersData = parametersDao.getByAlertIdAndParamName(stateData.getAlertId(), paramName);
if (isParamValueMatchToProcess(parametersData, paramValue)) { if (isParamValueMatchToProcess(parametersData, paramValue, paramMap)) {
paramMap.put(paramName, paramValue); Set<String> values = new HashSet<>();
values.add(paramValue);
if (!paramMap.containsKey(paramName)) {
paramMap.put(paramName, values);
} else {
paramMap.get(paramName).addAll(values);
}
stateData.setMapParams(jsonMapper.toJson(paramMap)); stateData.setMapParams(jsonMapper.toJson(paramMap));
stateDataDao.updateParams(userId, stateData.getMapParams()); stateDataDao.updateParams(userId, stateData.getMapParams());
} }
String nextKey = getNextKeyForFill(paramMap); String nextKey = getNextKeyForFill(paramMap, parametersData);
if (nextKey != null) { if (nextKey != null) {
return replyMessagesMapper.createNextParameterRequest(nextKey, stateData); return replyMessagesMapper.createNextParameterRequest(nextKey, stateData);
} else { } else {
@ -39,12 +47,17 @@ public class CreateParamsRequestMapper {
} }
} }
private boolean isParamValueMatchToProcess(ParametersData parametersData, String paramValue) { private boolean isParamValueMatchToProcess(ParametersData parametersData, String paramValue, Map<String,
Set<String>> providedParams) {
return (!parametersData.getMandatory() return (!parametersData.getMandatory()
&& (isValuePattern(paramValue, parametersData) && (isValuePattern(paramValue, parametersData)
|| paramValue.equals(TextConstants.EMPTY_PARAM.getText()))) || paramValue.equals(TextConstants.EMPTY_PARAM.getText())))
|| (parametersData.getMandatory() && !paramValue.equals(TextConstants.EMPTY_PARAM.getText()) || (parametersData.getMandatory()
&& isValuePattern(paramValue, parametersData)); && !paramValue.equals(TextConstants.EMPTY_PARAM.getText())
&& isValuePattern(paramValue, parametersData))
|| (parametersData.getMandatory()
&& paramValue.equals(TextConstants.EMPTY_PARAM.getText())
&& providedParams.containsKey(parametersData.getParamId()));
} }
private boolean isValuePattern(String value, ParametersData parametersData) { private boolean isValuePattern(String value, ParametersData parametersData) {
@ -55,9 +68,17 @@ public class CreateParamsRequestMapper {
return true; return true;
} }
private static String getNextKeyForFill(Map<String, String> map) { private static String getNextKeyForFill(Map<String, Set<String>> map, ParametersData parametersData) {
return map.entrySet().stream() return map.entrySet().stream()
.filter(entry -> null == entry.getValue()) // Если у параметра вообще нет значения
.filter(entry -> null == entry.getValue()
//Если у параметр может принимать несколько значений, а
// пользователь продолжает вводить новые значения
|| !entry.getValue().contains(TextConstants.EMPTY_PARAM.getText())
&& parametersData.getMultipleValues()
//Если параметр обязательный, но пользователь передал только пустое значение
|| entry.getValue().contains(TextConstants.EMPTY_PARAM.getText())
&& parametersData.getMandatory() && entry.getValue().size() == 1)
.findFirst().map(Map.Entry::getKey) .findFirst().map(Map.Entry::getKey)
.orElse(null); .orElse(null);
} }

View File

@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@ -22,13 +23,13 @@ public class JsonMapper {
} }
@SneakyThrows(JsonProcessingException.class) @SneakyThrows(JsonProcessingException.class)
public Map<String, String> toMap(String json) { public Map<String, Set<String>> toMap(String json) {
return objectMapper.readValue(json, new TypeReference<>() { return objectMapper.readValue(json, new TypeReference<>() {
}); });
} }
@SneakyThrows(JsonProcessingException.class) @SneakyThrows(JsonProcessingException.class)
public List<String> toList(String json) { public Set<String> toSet(String json) {
return objectMapper.readValue(json, new TypeReference<>() { return objectMapper.readValue(json, new TypeReference<>() {
}); });
} }

View File

@ -44,8 +44,8 @@ public class ParametersCallbackMapper {
List<ParameterConfiguration> parameterConfigurations) { List<ParameterConfiguration> parameterConfigurations) {
StateData stateData = stateDataDao.getByUserId(userId); StateData stateData = stateDataDao.getByUserId(userId);
stateData.setAlertId(alertId); stateData.setAlertId(alertId);
Map<String, String> mapParams = new HashMap<>(); Map<String, List<String>> mapParams = new HashMap<>();
parameterConfigurations.forEach(param -> mapParams.put(param.getName(), null)); parameterConfigurations.forEach(param -> mapParams.put(param.getName(), new ArrayList<>()));
stateData.setMapParams(jsonMapper.toJson(mapParams)); stateData.setMapParams(jsonMapper.toJson(mapParams));
stateDataDao.save(stateData); stateDataDao.save(stateData);
} }
@ -55,12 +55,13 @@ public class ParametersCallbackMapper {
parametersData.setAlertId(alertId); parametersData.setAlertId(alertId);
parametersData.setParamId(param.getId()); parametersData.setParamId(param.getId());
parametersData.setParamName(param.getName()); parametersData.setParamName(param.getName());
List<String> options = param.isSetOptions() ? new ArrayList<>(param.getOptions()) : null; Set<String> options = param.isSetOptions() ? new HashSet<>(param.getOptions()) : null;
if (options != null && !param.isMandatory()) { if (options != null && (!param.isMandatory() || param.isMultipleValues())) {
options.add(TextConstants.EMPTY_PARAM.getText()); options.add(TextConstants.EMPTY_PARAM.getText());
} }
String optionsValues = jsonMapper.toJson(options); String optionsValues = jsonMapper.toJson(options);
parametersData.setOptionsValues(optionsValues); parametersData.setOptionsValues(optionsValues);
parametersData.setMultipleValues(param.isMultipleValues());
parametersData.setMandatory(param.isMandatory()); parametersData.setMandatory(param.isMandatory());
parametersData.setValueRegexp(param.getValueRegexp()); parametersData.setValueRegexp(param.getValueRegexp());
parametersDao.save(parametersData); parametersDao.save(parametersData);

View File

@ -15,9 +15,9 @@ import org.apache.thrift.TException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static dev.vality.alert.tg.bot.utils.MainMenuBuilder.buildMainInlineKeyboardMarkup; import static dev.vality.alert.tg.bot.utils.MainMenuBuilder.buildMainInlineKeyboardMarkup;
@ -34,18 +34,23 @@ public class ReplyMessagesMapper {
public SendMessage createAlertRequest(long userId) throws TException { public SendMessage createAlertRequest(long userId) throws TException {
StateData stateData = stateDataDao.getByUserId(userId); StateData stateData = stateDataDao.getByUserId(userId);
log.info("Start create alert with stateData {}", stateData); log.info("Start create alert with stateData {}", stateData);
Map<String, String> paramMap = jsonMapper.toMap(stateData.getMapParams()); Map<String, Set<String>> paramMap = jsonMapper.toMap(stateData.getMapParams());
CreateAlertRequest createAlertRequest = new CreateAlertRequest(); CreateAlertRequest createAlertRequest = new CreateAlertRequest();
createAlertRequest.setAlertId(stateData.getAlertId()); createAlertRequest.setAlertId(stateData.getAlertId());
createAlertRequest.setUserId(String.valueOf(userId)); createAlertRequest.setUserId(String.valueOf(userId));
List<ParameterInfo> parameterInfos = new ArrayList<>(); List<ParameterInfo> parameterInfos =
for (String key : paramMap.keySet()) { paramMap.entrySet().stream()
ParametersData parametersData = parametersDao.getByAlertIdAndParamName(stateData.getAlertId(), key); .flatMap(entry -> {
ParameterInfo parameterInfo = new ParameterInfo(); ParametersData parametersData =
parameterInfo.setId(parametersData.getParamId()); parametersDao.getByAlertIdAndParamName(stateData.getAlertId(), entry.getKey());
parameterInfo.setValue(paramMap.get(key)); return entry.getValue().stream()
parameterInfos.add(parameterInfo); .map(value -> {
} ParameterInfo parameterInfo = new ParameterInfo();
parameterInfo.setId(parametersData.getParamId());
parameterInfo.setValue(value);
return parameterInfo;
}).toList().stream();
}).toList();
createAlertRequest.setParameters(parameterInfos); createAlertRequest.setParameters(parameterInfos);
mayDayService.createAlert(createAlertRequest); mayDayService.createAlert(createAlertRequest);
SendMessage message = new SendMessage(); SendMessage message = new SendMessage();

View File

@ -0,0 +1,2 @@
ALTER TABLE alert_tg_bot.parameters_data
ADD COLUMN IF NOT EXISTS multiple_values BOOL DEFAULT false;

View File

@ -15,7 +15,7 @@ public abstract class TestObjectFactory {
StateData stateData = new StateData(); StateData stateData = new StateData();
stateData.setUserId(122233L); stateData.setUserId(122233L);
stateData.setAlertId("45"); stateData.setAlertId("45");
stateData.setMapParams("{\"Процент\":34,\"Имя Терминала\":56}"); stateData.setMapParams("{\"Процент\":[34],\"Имя Терминала\":[56]}");
return stateData; return stateData;
} }
@ -26,6 +26,7 @@ public abstract class TestObjectFactory {
params.setParamId("14"); params.setParamId("14");
params.setOptionsValues("[\"test1\",\"test2\"]"); params.setOptionsValues("[\"test1\",\"test2\"]");
params.setMandatory(false); params.setMandatory(false);
params.setMultipleValues(false);
return params; return params;
} }

View File

@ -45,12 +45,12 @@ class StateDaoImplTest {
@Test @Test
void updateParams() { void updateParams() {
StateData stateData = TestObjectFactory.testStateData(); StateData stateData = TestObjectFactory.testStateData();
stateData.setMapParams("{\"Процент\":56,\"Имя Терминала\":test}"); stateData.setMapParams("{\"Процент\":[56],\"Имя Терминала\":[test]}");
dslContext.insertInto(STATE_DATA) dslContext.insertInto(STATE_DATA)
.set(dslContext.newRecord(STATE_DATA, stateData)) .set(dslContext.newRecord(STATE_DATA, stateData))
.execute(); .execute();
String newMapParams = "{\"Процент\":56,\"Имя Терминала\":new}"; String newMapParams = "{\"Процент\":[56],\"Имя Терминала\":[new]}";
stateDataDao.updateParams(stateData.getUserId(), newMapParams); stateDataDao.updateParams(stateData.getUserId(), newMapParams);
StateDataRecord stateDataRecord = dslContext.fetchAny(STATE_DATA); StateDataRecord stateDataRecord = dslContext.fetchAny(STATE_DATA);