Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
import me.thinkcat.opic.practice.entity.UserRole;
import me.thinkcat.opic.practice.exception.ResourceNotFoundException;
import me.thinkcat.opic.practice.exception.ValidationException;
import me.thinkcat.opic.practice.entity.Question;
import me.thinkcat.opic.practice.repository.AnswerRepository;
import me.thinkcat.opic.practice.repository.QuestionRepository;
import me.thinkcat.opic.practice.repository.SessionRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -36,6 +38,7 @@ public class AnswerService {

private final AnswerRepository answerRepository;
private final SessionRepository sessionRepository;
private final QuestionRepository questionRepository;
private final PresignedUrlService presignedUrlService;
private final FeedbackLambdaService feedbackLambdaService;

Expand Down Expand Up @@ -109,7 +112,10 @@ public AnswerResponse completeAnswerUpload(
Answer updatedAnswer = answerRepository.save(answer);
log.info("event=feedback_requested | who={} | answerId={} | audioUrl={}",
userId, answerId, answer.getAudioUrl());
feedbackLambdaService.invokeSessionFeedbackAsync(answer.getAudioUrl());
String questionText = questionRepository.findById(answer.getQuestionId())
.map(Question::getQuestion)
.orElse(null);
feedbackLambdaService.invokeSessionFeedbackAsync(answer.getAudioUrl(), userId, questionText);
return resolveAnswerResponse(updatedAnswer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ public DrillAnswerResponse submitDrillAnswer(Long userId, UserRole userRole, Lon
DrillAnswer updatedAnswer = drillAnswerRepository.save(answer);
log.info("event=drill_feedback_requested | who={} | drillAnswerId={} | audioUrl={}",
userId, drillAnswerId, answer.getAudioUrl());
feedbackLambdaService.invokeDrillAnswerFeedbackAsync(answer.getAudioUrl());
String questionText = questionRepository.findById(answer.getQuestionId())
.map(Question::getQuestion)
.orElse(null);
feedbackLambdaService.invokeDrillAnswerFeedbackAsync(answer.getAudioUrl(), userId, questionText);
return resolveDrillAnswerResponse(updatedAnswer);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package me.thinkcat.opic.practice.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.thinkcat.opic.practice.config.AwsLambdaConfig;
Expand All @@ -11,6 +13,8 @@
import software.amazon.awssdk.services.lambda.model.InvokeRequest;

import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

@Service
@RequiredArgsConstructor
Expand All @@ -20,21 +24,31 @@ public class FeedbackLambdaService {
private final LambdaAsyncClient lambdaAsyncClient;
private final AwsLambdaConfig lambdaConfig;
private final PresignedUrlProperties s3Properties;
private final ObjectMapper objectMapper;

public void invokeSessionFeedbackAsync(String audioUrl) {
invokeAsync(audioUrl, lambdaConfig.getSessionFeedbackFunctionName());
public void invokeSessionFeedbackAsync(String audioUrl, Long userId, String questionText) {
invokeAsync(audioUrl, lambdaConfig.getSessionFeedbackFunctionName(), userId, questionText);
}

public void invokeDrillAnswerFeedbackAsync(String audioUrl) {
invokeAsync(audioUrl, lambdaConfig.getDrillAnswerFeedbackFunctionName());
public void invokeDrillAnswerFeedbackAsync(String audioUrl, Long userId, String questionText) {
invokeAsync(audioUrl, lambdaConfig.getDrillAnswerFeedbackFunctionName(), userId, questionText);
}

private void invokeAsync(String audioUrl, String lambdaFunctionName) {
String payload = String.format(
"{\"source\":\"WAS\",\"bucket\":\"%s\",\"audioUrl\":\"%s\"}",
s3Properties.getBucket(),
audioUrl
);
private void invokeAsync(String audioUrl, String lambdaFunctionName, Long userId, String questionText) {
Map<String, Object> payloadMap = new LinkedHashMap<>();
payloadMap.put("source", "WAS");
payloadMap.put("bucket", s3Properties.getBucket());
payloadMap.put("audioUrl", audioUrl);
payloadMap.put("userId", userId);
payloadMap.put("questionText", questionText);

String payload;
try {
payload = objectMapper.writeValueAsString(payloadMap);
} catch (JsonProcessingException e) {
log.error("event=feedback_lambda_payload_fail | audioUrl={} | error={}", audioUrl, e.getMessage());
return;
}

InvokeRequest request = InvokeRequest.builder()
.functionName(lambdaFunctionName)
Expand All @@ -45,9 +59,9 @@ private void invokeAsync(String audioUrl, String lambdaFunctionName) {
lambdaAsyncClient.invoke(request)
.whenComplete((response, ex) -> {
if (ex != null) {
log.error("event=feedback_lambda_invoke_fail | audioUrl={} | error={}", audioUrl, ex.getMessage());
log.error("event=feedback_lambda_invoke_fail | audioUrl={} | userId={} | error={}", audioUrl, userId, ex.getMessage());
} else {
log.info("event=feedback_lambda_invoke | audioUrl={} | statusCode={}", audioUrl, response.statusCode());
log.info("event=feedback_lambda_invoke | audioUrl={} | userId={} | statusCode={}", audioUrl, userId, response.statusCode());
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import me.thinkcat.opic.practice.entity.StorageType;
import me.thinkcat.opic.practice.entity.UserRole;
import me.thinkcat.opic.practice.repository.AnswerRepository;
import me.thinkcat.opic.practice.repository.QuestionRepository;
import me.thinkcat.opic.practice.repository.SessionRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -21,7 +22,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
Expand All @@ -32,6 +35,7 @@ class AnswerServiceTest {

@Mock private AnswerRepository answerRepository;
@Mock private SessionRepository sessionRepository;
@Mock private QuestionRepository questionRepository;
@Mock private FeatureFlagService featureFlagService;
@Mock private FeedbackLambdaService feedbackLambdaService;
@Mock private PresignedUrlService presignedUrlService;
Expand Down Expand Up @@ -59,6 +63,7 @@ void setUp() {

given(answerRepository.findById(answerId)).willReturn(Optional.of(pendingAnswer));
given(sessionRepository.findByIdAndUserId(1L, userId)).willReturn(Optional.of(session));
lenient().when(questionRepository.findById(any())).thenReturn(Optional.empty());
given(answerRepository.save(any())).willAnswer(inv -> inv.getArgument(0));
given(presignedUrlService.generateDownloadUrl(anyString()))
.willReturn(mock(PresignedUrlResponse.class));
Expand All @@ -72,7 +77,7 @@ void setUp() {

assertThat(pendingAnswer.getFeedbackStatus()).isEqualTo(FeedbackStatus.REQUESTED);
verify(feedbackLambdaService, times(1))
.invokeSessionFeedbackAsync(pendingAnswer.getAudioUrl());
.invokeSessionFeedbackAsync(eq(pendingAnswer.getAudioUrl()), eq(userId), any());
}

@Test
Expand All @@ -82,7 +87,7 @@ void setUp() {
answerService.completeAnswerUpload(userId, UserRole.FREE, answerId, 5000);

assertThat(pendingAnswer.getFeedbackStatus()).isEqualTo(FeedbackStatus.NONE);
verify(feedbackLambdaService, never()).invokeSessionFeedbackAsync(any());
verify(feedbackLambdaService, never()).invokeSessionFeedbackAsync(any(), any(), any());
}

@Test
Expand All @@ -91,6 +96,6 @@ void setUp() {

assertThat(pendingAnswer.getFeedbackStatus()).isEqualTo(FeedbackStatus.REQUESTED);
verify(feedbackLambdaService, times(1))
.invokeSessionFeedbackAsync(pendingAnswer.getAudioUrl());
.invokeSessionFeedbackAsync(eq(pendingAnswer.getAudioUrl()), eq(userId), any());
}
}