/* * 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.mapper.conversation; import com.google.cloud.dialogflow.cx.v3.QueryResult; import com.google.cloud.dialogflow.cx.v3.ResponseMessage; import com.google.cloud.dialogflow.cx.v3.DetectIntentResponse; import com.example.dto.dialogflow.base.DetectIntentResponseDTO; import com.example.dto.dialogflow.conversation.QueryResultDTO; import com.example.util.ProtobufUtil; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Spring component responsible for mapping a Dialogflow CX API response * (`DetectIntentResponse`) to a simplified custom DTO (`DetectIntentResponseDTO`). * It extracts and consolidates the fulfillment text, and converts Protobuf * session parameters into standard Java objects, providing a clean and * decoupled interface for consuming Dialogflow results. */ @Component public class DialogflowResponseMapper { private static final Logger logger = LoggerFactory.getLogger(DialogflowResponseMapper.class); public DetectIntentResponseDTO mapFromDialogflowResponse(DetectIntentResponse response, String sessionId) { logger.info("Starting mapping of Dialogflow DetectIntentResponse for session: {}", sessionId); String responseId = response.getResponseId(); QueryResult dfQueryResult = response.getQueryResult(); logger.debug("Extracted QueryResult object for session: {}", sessionId); StringBuilder responseTextBuilder = new StringBuilder(); if (dfQueryResult.getResponseMessagesList().isEmpty()) { logger.debug("No response messages found in QueryResult for session: {}", sessionId); } for (ResponseMessage message : dfQueryResult.getResponseMessagesList()) { if (message.hasText()) { logger.debug("Processing text response message for session: {}", sessionId); for (String text : message.getText().getTextList()) { if (responseTextBuilder.length() > 0) { responseTextBuilder.append(" "); } responseTextBuilder.append(text); logger.debug("Appended text segment: '{}' to fulfillment text for session: {}", text, sessionId); } } else { logger.debug("Skipping non-text response message type: {} for session: {}", message.getMessageCase(), sessionId); } } String responseText = responseTextBuilder.toString().trim(); Map parameters = new LinkedHashMap<>(); // Inicializamos vacío para evitar NPEs después if (dfQueryResult.hasParameters()) { // Usamos un forEach en lugar de Collectors.toMap para tener control total sobre nulos dfQueryResult.getParameters().getFieldsMap().forEach((key, value) -> { try { Object convertedValue = ProtobufUtil.convertProtobufValueToJavaObject(value); // Si el valor convertido es nulo, decidimos qué hacer. // Lo mejor es poner un String vacío o ignorarlo para que no explote tu lógica. if (convertedValue != null) { parameters.put(key, convertedValue); } else { logger.warn("El parámetro '{}' devolvió un valor nulo al convertir. Se ignorará.", key); // Opcional: parameters.put(key, ""); } } catch (Exception e) { logger.error("Error convirtiendo el parámetro '{}' de Protobuf a Java: {}", key, e.getMessage()); } }); logger.debug("Extracted parameters: {} for session: {}", parameters, sessionId); } else { logger.debug("No parameters found in QueryResult for session: {}. Using empty map.", sessionId); } QueryResultDTO ourQueryResult = new QueryResultDTO(responseText, parameters); logger.debug("Internal QueryResult DTO created for session: {}. Details: {}", sessionId, ourQueryResult); DetectIntentResponseDTO finalResponse = new DetectIntentResponseDTO(responseId, ourQueryResult); logger.info("Finished mapping DialogflowDetectIntentResponse for session: {}. Full response ID: {}", sessionId, responseId); return finalResponse; } }