UPDATE 01-sep

This commit is contained in:
PAVEL PALMA
2025-09-01 15:31:30 -06:00
parent a782f6c284
commit 4278541fff
18 changed files with 146 additions and 59 deletions

View File

@@ -91,7 +91,7 @@
<dependency> <dependency>
<groupId>com.google.genai</groupId> <groupId>com.google.genai</groupId>
<artifactId>google-genai</artifactId> <artifactId>google-genai</artifactId>
<version>1.8.0</version> <version>1.13.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>

View File

@@ -9,8 +9,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public record ExternalNotRequestDTO( public record ExternalNotRequestDTO(
@JsonProperty("texto") String text, @JsonProperty("texto") String text,
@JsonProperty("telefono") String phoneNumber) { @JsonProperty("telefono") String phoneNumber,
@JsonProperty("parametrosOcultos") java.util.Map<String, String> hiddenParameters
) {
public ExternalNotRequestDTO { public ExternalNotRequestDTO {
} }
} }

View File

@@ -3,5 +3,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List; import java.util.List;
public record QuickReplyDTO( public record QuickReplyDTO(
@JsonProperty("header") String header, @JsonProperty("header") String header,
@JsonProperty("body") String body,
@JsonProperty("button") String button,
@JsonProperty("header_section") String headerSection,
@JsonProperty("preguntas") List<QuestionDTO> preguntas @JsonProperty("preguntas") List<QuestionDTO> preguntas
) {} ) {}

View File

@@ -1,3 +1,8 @@
/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.exception; package com.example.exception;
public class DialogflowClientException extends RuntimeException { public class DialogflowClientException extends RuntimeException {

View File

@@ -1,3 +1,8 @@
/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.exception; package com.example.exception;
public class FirestorePersistenceException extends RuntimeException { public class FirestorePersistenceException extends RuntimeException {

View File

@@ -1,3 +1,8 @@
/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.exception; package com.example.exception;
public class GeminiClientException extends Exception { public class GeminiClientException extends Exception {

View File

@@ -47,7 +47,6 @@ public class ConversationManagerService {
private final ConversationContextMapper conversationContextMapper; private final ConversationContextMapper conversationContextMapper;
private final DataLossPrevention dataLossPrevention; private final DataLossPrevention dataLossPrevention;
private final String dlpTemplateCompleteFlow; private final String dlpTemplateCompleteFlow;
private final String dlpTemplatePersistFlow;
public ConversationManagerService( public ConversationManagerService(
DialogflowClientService dialogflowServiceClient, DialogflowClientService dialogflowServiceClient,
@@ -60,8 +59,7 @@ public class ConversationManagerService {
NotificationContextMapper notificationContextMapper, NotificationContextMapper notificationContextMapper,
ConversationContextMapper conversationContextMapper, ConversationContextMapper conversationContextMapper,
DataLossPrevention dataLossPrevention, DataLossPrevention dataLossPrevention,
@Value("${google.cloud.dlp.dlpTemplateCompleteFlow}") String dlpTemplateCompleteFlow, @Value("${google.cloud.dlp.dlpTemplateCompleteFlow}") String dlpTemplateCompleteFlow) {
@Value("${google.cloud.dlp.dlpTemplatePersistFlow}") String dlpTemplatePersistFlow) {
this.dialogflowServiceClient = dialogflowServiceClient; this.dialogflowServiceClient = dialogflowServiceClient;
this.firestoreConversationService = firestoreConversationService; this.firestoreConversationService = firestoreConversationService;
this.memoryStoreConversationService = memoryStoreConversationService; this.memoryStoreConversationService = memoryStoreConversationService;
@@ -73,7 +71,6 @@ public class ConversationManagerService {
this.conversationContextMapper = conversationContextMapper; this.conversationContextMapper = conversationContextMapper;
this.dataLossPrevention = dataLossPrevention; this.dataLossPrevention = dataLossPrevention;
this.dlpTemplateCompleteFlow = dlpTemplateCompleteFlow; this.dlpTemplateCompleteFlow = dlpTemplateCompleteFlow;
this.dlpTemplatePersistFlow = dlpTemplatePersistFlow;
} }
public Mono<DetectIntentResponseDTO> manageConversation(ExternalConvRequestDTO externalrequest) { public Mono<DetectIntentResponseDTO> manageConversation(ExternalConvRequestDTO externalrequest) {
return dataLossPrevention.getObfuscatedString(externalrequest.message(), dlpTemplateCompleteFlow) return dataLossPrevention.getObfuscatedString(externalrequest.message(), dlpTemplateCompleteFlow)
@@ -193,10 +190,7 @@ public class ConversationManagerService {
private Mono<DetectIntentResponseDTO> processDialogflowRequest(ConversationSessionDTO session, DetectIntentRequestDTO request, String userId, String userMessageText, String userPhoneNumber, boolean newSession) { private Mono<DetectIntentResponseDTO> processDialogflowRequest(ConversationSessionDTO session, DetectIntentRequestDTO request, String userId, String userMessageText, String userPhoneNumber, boolean newSession) {
final String finalSessionId = session.sessionId(); final String finalSessionId = session.sessionId();
return dataLossPrevention.getObfuscatedString(userMessageText, dlpTemplatePersistFlow) ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(userMessageText);
.flatMap(obfuscatedUserMessageText -> {
ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(obfuscatedUserMessageText);
return this.persistConversationTurn(userId, finalSessionId, userEntry, userPhoneNumber) return this.persistConversationTurn(userId, finalSessionId, userEntry, userPhoneNumber)
.doOnSuccess(v -> logger.debug("User entry successfully persisted for session {}. Proceeding to Dialogflow...", finalSessionId)) .doOnSuccess(v -> logger.debug("User entry successfully persisted for session {}. Proceeding to Dialogflow...", finalSessionId))
@@ -210,7 +204,6 @@ public class ConversationManagerService {
}) })
.doOnError(error -> logger.error("Overall error during conversation management for session {}: {}", finalSessionId, error.getMessage(), error)) .doOnError(error -> logger.error("Overall error during conversation management for session {}: {}", finalSessionId, error.getMessage(), error))
)); ));
});
} }
private Mono<DetectIntentResponseDTO> startNotificationConversation(ConversationContext context, DetectIntentRequestDTO request, NotificationDTO notification) { private Mono<DetectIntentResponseDTO> startNotificationConversation(ConversationContext context, DetectIntentRequestDTO request, NotificationDTO notification) {
final String userId = context.userId(); final String userId = context.userId();

View File

@@ -1,3 +1,8 @@
/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.service.notification; package com.example.service.notification;
import com.example.dto.dialogflow.conversation.ConversationEntryDTO; import com.example.dto.dialogflow.conversation.ConversationEntryDTO;

View File

@@ -1,3 +1,8 @@
/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.service.notification; package com.example.service.notification;
import com.example.dto.dialogflow.notification.ExternalNotRequestDTO; import com.example.dto.dialogflow.notification.ExternalNotRequestDTO;
@@ -103,7 +108,11 @@ public Mono<DetectIntentResponseDTO> processNotification(ExternalNotRequestDTO e
parameters.put("telefono", telefono); parameters.put("telefono", telefono);
parameters.put(NOTIFICATION_TEXT_PARAM, newNotificationEntry.texto()); parameters.put(NOTIFICATION_TEXT_PARAM, newNotificationEntry.texto());
// Use a TextInputDTO to correctly build the QueryInputDTO if (externalRequest.hiddenParameters() != null && !externalRequest.hiddenParameters().isEmpty()) {
parameters.putAll(externalRequest.hiddenParameters());
}
// Use a TextInputDTO to correctly build the QueryInputDTO
TextInputDTO textInput = new TextInputDTO(newNotificationEntry.texto()); TextInputDTO textInput = new TextInputDTO(newNotificationEntry.texto());
QueryInputDTO queryInput = new QueryInputDTO(textInput, null, defaultLanguageCode); QueryInputDTO queryInput = new QueryInputDTO(textInput, null, defaultLanguageCode);

View File

@@ -11,7 +11,6 @@ import com.example.dto.dialogflow.conversation.ConversationEntryEntity;
import com.example.dto.dialogflow.conversation.ConversationEntryType; import com.example.dto.dialogflow.conversation.ConversationEntryType;
import com.example.dto.dialogflow.conversation.ExternalConvRequestDTO; import com.example.dto.dialogflow.conversation.ExternalConvRequestDTO;
import com.example.dto.quickreplies.QuickReplyScreenRequestDTO; import com.example.dto.quickreplies.QuickReplyScreenRequestDTO;
import com.example.service.conversation.DataLossPrevention;
import com.example.dto.quickreplies.QuestionDTO; import com.example.dto.quickreplies.QuestionDTO;
import com.example.dto.quickreplies.QuickReplyDTO; import com.example.dto.quickreplies.QuickReplyDTO;
import com.example.service.conversation.FirestoreConversationService; import com.example.service.conversation.FirestoreConversationService;
@@ -20,10 +19,14 @@ import com.example.util.SessionIdGenerator;
import java.time.Instant; import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.IntStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.example.dto.dialogflow.conversation.QueryResultDTO;
import com.example.service.conversation.ConversationManagerService;
import org.springframework.context.annotation.Lazy;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@Service @Service
@@ -32,20 +35,17 @@ public class QuickRepliesManagerService {
private final MemoryStoreConversationService memoryStoreConversationService; private final MemoryStoreConversationService memoryStoreConversationService;
private final FirestoreConversationService firestoreConversationService; private final FirestoreConversationService firestoreConversationService;
private final QuickReplyContentService quickReplyContentService; private final QuickReplyContentService quickReplyContentService;
private final DataLossPrevention dataLossPrevention; private final ConversationManagerService conversationManagerService;
private final String dlpTemplatePersistFlow;
public QuickRepliesManagerService( public QuickRepliesManagerService(
@Lazy ConversationManagerService conversationManagerService,
MemoryStoreConversationService memoryStoreConversationService, MemoryStoreConversationService memoryStoreConversationService,
FirestoreConversationService firestoreConversationService, FirestoreConversationService firestoreConversationService,
QuickReplyContentService quickReplyContentService, QuickReplyContentService quickReplyContentService) {
DataLossPrevention dataLossPrevention, this.conversationManagerService = conversationManagerService;
@Value("${google.cloud.dlp.dlpTemplatePersistFlow}") String dlpTemplatePersistFlow) {
this.memoryStoreConversationService = memoryStoreConversationService; this.memoryStoreConversationService = memoryStoreConversationService;
this.firestoreConversationService = firestoreConversationService; this.firestoreConversationService = firestoreConversationService;
this.quickReplyContentService = quickReplyContentService; this.quickReplyContentService = quickReplyContentService;
this.dataLossPrevention = dataLossPrevention;
this.dlpTemplatePersistFlow = dlpTemplatePersistFlow;
} }
public Mono<DetectIntentResponseDTO> startQuickReplySession(QuickReplyScreenRequestDTO externalRequest) { public Mono<DetectIntentResponseDTO> startQuickReplySession(QuickReplyScreenRequestDTO externalRequest) {
@@ -88,13 +88,27 @@ public class QuickRepliesManagerService {
.flatMap(session -> { .flatMap(session -> {
String userId = session.userId(); String userId = session.userId();
String sessionId = session.sessionId(); String sessionId = session.sessionId();
return dataLossPrevention.getObfuscatedString(externalRequest.message(), dlpTemplatePersistFlow)
.flatMap(obfuscatedMessage -> {
ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(obfuscatedMessage);
long userMessagesCount = session.entries().stream() ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(externalRequest.message());
.filter(e -> e.entity() == ConversationEntryEntity.USUARIO)
.count(); List<ConversationEntryDTO> entries = session.entries();
int lastInitIndex = IntStream.range(0, entries.size())
.map(i -> entries.size() - 1 - i)
.filter(i -> {
ConversationEntryDTO entry = entries.get(i);
return entry.entity() == ConversationEntryEntity.SISTEMA && entry.type() == ConversationEntryType.INICIO;
})
.findFirst()
.orElse(-1);
long userMessagesCount;
if (lastInitIndex != -1) {
userMessagesCount = entries.subList(lastInitIndex + 1, entries.size()).stream()
.filter(e -> e.entity() == ConversationEntryEntity.USUARIO)
.count();
} else {
userMessagesCount = 0;
}
if (userMessagesCount == 0) { // Is the first user message in the Quick-Replies flow if (userMessagesCount == 0) { // Is the first user message in the Quick-Replies flow
// This is the second message of the flow. Return the full list. // This is the second message of the flow. Return the full list.
@@ -118,33 +132,32 @@ public class QuickRepliesManagerService {
.filter(p -> p.titulo().equalsIgnoreCase(externalRequest.message().trim())) .filter(p -> p.titulo().equalsIgnoreCase(externalRequest.message().trim()))
.toList(); .toList();
QuickReplyDTO responseQuickReplyDTO;
if (!matchedPreguntas.isEmpty()) { if (!matchedPreguntas.isEmpty()) {
responseQuickReplyDTO = new QuickReplyDTO(quickReplyDTO.header(), // Matched question, return the answer
matchedPreguntas); String respuesta = matchedPreguntas.get(0).respuesta();
} else { QueryResultDTO queryResult = new QueryResultDTO(respuesta, null);
responseQuickReplyDTO = new QuickReplyDTO(quickReplyDTO.header(), DetectIntentResponseDTO response = new DetectIntentResponseDTO(sessionId, queryResult, null);
Collections.emptyList());
}
// End the quick reply flow by clearing the pantallaContexto return memoryStoreConversationService
return memoryStoreConversationService
.updateSession(session.withPantallaContexto(null)) .updateSession(session.withPantallaContexto(null))
.then(persistConversationTurn(userId, sessionId, .then(persistConversationTurn(userId, sessionId,
ConversationEntryDTO.forAgentWithMessage( ConversationEntryDTO.forAgentWithMessage(respuesta),
responseQuickReplyDTO.toString()),
userPhoneNumber, null)) userPhoneNumber, null))
.thenReturn(new DetectIntentResponseDTO(sessionId, null, .thenReturn(response);
responseQuickReplyDTO)); } else {
// No match, delegate to Dialogflow
return memoryStoreConversationService
.updateSession(session.withPantallaContexto(null))
.then(conversationManagerService.manageConversation(externalRequest));
}
}); });
} else { } else {
// Should not happen. End the flow. // Should not happen. End the flow.
return memoryStoreConversationService.updateSession(session.withPantallaContexto(null)) return memoryStoreConversationService.updateSession(session.withPantallaContexto(null))
.then(Mono.just(new DetectIntentResponseDTO(session.sessionId(), null, .then(Mono.just(new DetectIntentResponseDTO(session.sessionId(), null,
new QuickReplyDTO("Flow Error", Collections.emptyList())))); new QuickReplyDTO("Flow Error", null, null, null, Collections.emptyList()))));
} }
}); });
});
} }
private Mono<Void> persistConversationTurn(String userId, String sessionId, ConversationEntryDTO entry, private Mono<Void> persistConversationTurn(String userId, String sessionId, ConversationEntryDTO entry,

View File

@@ -28,7 +28,7 @@ public class QuickReplyContentService {
logger.info("Fetching quick replies from Firestore for document: {}", collectionId); logger.info("Fetching quick replies from Firestore for document: {}", collectionId);
if (collectionId == null || collectionId.isBlank()) { if (collectionId == null || collectionId.isBlank()) {
logger.warn("collectionId is null or empty. Returning empty quick replies."); logger.warn("collectionId is null or empty. Returning empty quick replies.");
return Mono.just(new QuickReplyDTO("empty", Collections.emptyList())); return Mono.just(new QuickReplyDTO("empty", null, null, null, Collections.emptyList()));
} }
return Mono.fromCallable(() -> { return Mono.fromCallable(() -> {
try { try {
@@ -45,11 +45,14 @@ public class QuickReplyContentService {
.filter(DocumentSnapshot::exists) .filter(DocumentSnapshot::exists)
.map(document -> { .map(document -> {
String header = document.getString("header"); String header = document.getString("header");
String body = document.getString("body");
String button = document.getString("button");
String headerSection = document.getString("header_section");
List<Map<String, Object>> preguntasData = (List<Map<String, Object>>) document.get("preguntas"); List<Map<String, Object>> preguntasData = (List<Map<String, Object>>) document.get("preguntas");
List<QuestionDTO> preguntas = preguntasData.stream() List<QuestionDTO> preguntas = preguntasData.stream()
.map(p -> new QuestionDTO((String) p.get("titulo"), (String) p.get("descripcion"), (String) p.get("respuesta"))) .map(p -> new QuestionDTO((String) p.get("titulo"), (String) p.get("descripcion"), (String) p.get("respuesta")))
.toList(); .toList();
return new QuickReplyDTO(header, preguntas); return new QuickReplyDTO(header, body, button, headerSection, preguntas);
}) })
.doOnSuccess(quickReplyDTO -> { .doOnSuccess(quickReplyDTO -> {
if (quickReplyDTO != null) { if (quickReplyDTO != null) {

View File

@@ -9,15 +9,18 @@ import com.example.repository.FirestoreBaseRepository;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.DocumentSnapshot;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@Component @Component
@@ -32,12 +35,17 @@ public class FirestoreDataImporter {
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired
private Environment env;
@PostConstruct @PostConstruct
public void importDataOnStartup() { public void importDataOnStartup() {
try { if (Boolean.parseBoolean(env.getProperty("firestore.data.importer.enabled"))) {
importQuickReplies(); try {
} catch (Exception e) { importQuickReplies();
logger.error("Failed to import data to Firestore on startup", e); } catch (Exception e) {
logger.error("Failed to import data to Firestore on startup", e);
}
} }
} }
@@ -67,13 +75,19 @@ public class FirestoreDataImporter {
logger.warn("Resource not found: {}", resourcePath); logger.warn("Resource not found: {}", resourcePath);
return; return;
} }
Map<String, Object> data = objectMapper.readValue(inputStream, new TypeReference<Map<String, Object>>() {}); Map<String, Object> localData = objectMapper.readValue(inputStream, new TypeReference<Map<String, Object>>() {});
DocumentReference docRef = firestoreBaseRepository.getDocumentReference(collectionPath, documentId); DocumentReference docRef = firestoreBaseRepository.getDocumentReference(collectionPath, documentId);
if (!firestoreBaseRepository.documentExists(docRef)) {
firestoreBaseRepository.setDocument(docRef, data); if (firestoreBaseRepository.documentExists(docRef)) {
logger.debug("Successfully imported {} to Firestore.", documentId); DocumentSnapshot documentSnapshot = firestoreBaseRepository.getDocumentSnapshot(docRef);
Map<String, Object> firestoreData = documentSnapshot.getData();
if (!Objects.equals(localData, firestoreData)) {
firestoreBaseRepository.setDocument(docRef, localData);
logger.info("Successfully updated {} in Firestore.", documentId);
}
} else { } else {
logger.debug("{} already exists in Firestore. Skipping import.", documentId); firestoreBaseRepository.setDocument(docRef, localData);
logger.info("Successfully imported {} to Firestore.", documentId);
} }
} }
} }

View File

@@ -64,4 +64,7 @@ messagefilter.prompt=prompts/message_filter_prompt.txt
# (DLP) Configuration # (DLP) Configuration
# ========================================================= # =========================================================
google.cloud.dlp.dlpTemplateCompleteFlow=${DLP_TEMPLATE_COMPLETE_FLOW} google.cloud.dlp.dlpTemplateCompleteFlow=${DLP_TEMPLATE_COMPLETE_FLOW}
google.cloud.dlp.dlpTemplatePersistFlow=IMC_INSPECT_NAME # =========================================================
# Quick-replies Preset-data
# =========================================================
firestore.data.importer.enabled=true

View File

@@ -58,3 +58,12 @@ messagefilter.temperature=${MESSAGE_FILTER_TEMPERATURE}
messagefilter.maxOutputTokens=${MESSAGE_FILTER_MAX_OUTPUT_TOKENS} messagefilter.maxOutputTokens=${MESSAGE_FILTER_MAX_OUTPUT_TOKENS}
messagefilter.topP=${MESSAGE_FILTER_TOP_P} messagefilter.topP=${MESSAGE_FILTER_TOP_P}
messagefilter.prompt=prompts/message_filter_prompt.txt messagefilter.prompt=prompts/message_filter_prompt.txt
# =========================================================
# (DLP) Configuration
# =========================================================
google.cloud.dlp.dlpTemplateCompleteFlow=${DLP_TEMPLATE_COMPLETE_FLOW}
google.cloud.dlp.dlpTemplatePersistFlow=${DLP_TEMPLATE_PERSIST_FLOW}
# =========================================================
# Quick-replies Preset-data
# =========================================================
firestore.data.importer.enabled=true

View File

@@ -58,3 +58,12 @@ messagefilter.temperature=${MESSAGE_FILTER_TEMPERATURE}
messagefilter.maxOutputTokens=${MESSAGE_FILTER_MAX_OUTPUT_TOKENS} messagefilter.maxOutputTokens=${MESSAGE_FILTER_MAX_OUTPUT_TOKENS}
messagefilter.topP=${MESSAGE_FILTER_TOP_P} messagefilter.topP=${MESSAGE_FILTER_TOP_P}
messagefilter.prompt=prompts/message_filter_prompt.txt messagefilter.prompt=prompts/message_filter_prompt.txt
# =========================================================
# (DLP) Configuration
# =========================================================
google.cloud.dlp.dlpTemplateCompleteFlow=${DLP_TEMPLATE_COMPLETE_FLOW}
google.cloud.dlp.dlpTemplatePersistFlow=${DLP_TEMPLATE_PERSIST_FLOW}
# =========================================================
# Quick-replies Preset-data
# =========================================================
firestore.data.importer.enabled=true

View File

@@ -1 +1 @@
spring.profiles.active=${SPRING_PROFILE} spring.profiles.active=dev

View File

@@ -1,5 +1,8 @@
{ {
"header": "preguntas sobre pagos", "header": "preguntas frecuentes",
"body": "Aquí tienes las preguntas frecuentes que suelen hacernos algunos de nuestros clientes",
"button": "Ver",
"header_section": "preguntas sobre pagos",
"preguntas": [ "preguntas": [
{ {
"titulo": "Donde veo mi historial de pagos?", "titulo": "Donde veo mi historial de pagos?",

View File

@@ -54,13 +54,16 @@ public class QuickReplyContentServiceTest {
// Given // Given
String collectionId = "home"; String collectionId = "home";
String header = "home_header"; String header = "home_header";
String body = "home_body";
String button = "home_button";
String headerSection = "home_header_section";
List<Map<String, Object>> preguntas = Collections.singletonList( List<Map<String, Object>> preguntas = Collections.singletonList(
Map.of("titulo", "title", "descripcion", "description", "respuesta", "response") Map.of("titulo", "title", "descripcion", "description", "respuesta", "response")
); );
List<QuestionDTO> questionDTOs = Collections.singletonList( List<QuestionDTO> questionDTOs = Collections.singletonList(
new QuestionDTO("title", "description", "response") new QuestionDTO("title", "description", "response")
); );
QuickReplyDTO expected = new QuickReplyDTO(header, questionDTOs); QuickReplyDTO expected = new QuickReplyDTO(header, body, button, headerSection, questionDTOs);
when(firestore.collection("artifacts")).thenReturn(collectionReference); when(firestore.collection("artifacts")).thenReturn(collectionReference);
when(collectionReference.document("default-app-id")).thenReturn(documentReference); when(collectionReference.document("default-app-id")).thenReturn(documentReference);
@@ -70,6 +73,9 @@ public class QuickReplyContentServiceTest {
when(apiFuture.get()).thenReturn(documentSnapshot); when(apiFuture.get()).thenReturn(documentSnapshot);
when(documentSnapshot.exists()).thenReturn(true); when(documentSnapshot.exists()).thenReturn(true);
when(documentSnapshot.getString("header")).thenReturn(header); when(documentSnapshot.getString("header")).thenReturn(header);
when(documentSnapshot.getString("body")).thenReturn(body);
when(documentSnapshot.getString("button")).thenReturn(button);
when(documentSnapshot.getString("header_section")).thenReturn(headerSection);
when(documentSnapshot.get("preguntas")).thenReturn(preguntas); when(documentSnapshot.get("preguntas")).thenReturn(preguntas);
// When // When
@@ -112,7 +118,7 @@ public class QuickReplyContentServiceTest {
// Then // Then
StepVerifier.create(result) StepVerifier.create(result)
.expectNext(new QuickReplyDTO("empty", Collections.emptyList())) .expectNext(new QuickReplyDTO("empty", null, null, null, Collections.emptyList()))
.verifyComplete(); .verifyComplete();
} }
} }