нормально. Я создал еще одну лямбду, чтобы попробовать использовать возможности GraalVM. Я просто следовал руководству документации Micronaut, все компилируется и собирается
с использованием ./gradlew buildNativeLambda. Запуск опции тестирования в консоли AWS работает, и приложение не возвращает ошибок, однако sendgrid отправляет
обратно 400. Я чувствую, что мне не хватает чего-то основного. Я попробовал добавить все, что мог придумать, в
resources/META-INF/native-image/reflect-config.json . Пробовал разные способы, показанные в документации micronaut, для запуска лямбды, т. е. с помощью метода Controller
, FunctionRequestHandler и FunctionLambdaRuntime. С каждым из них лямбда будет работать и возвращает строку или APIGatewayProxyResponseEvent
без проблем. Кажется, что-то есть с созданием электронной почты, и я чувствую, что на этом этапе я просто швырялся в стену, и ничего
не прилипает, независимо от того, что я меняю, я не получаю новых кодов ошибок или чего-то еще подтолкните меня в правильном направлении.
- Текущий build.gradle – на данный момент он очень раздут, но, как я уже сказал, я застрял в попытках получить это работает, поэтому я, похоже, просто продолжаю
добавлять что-то в надежде, что что-то изменится
Код: Выделить всё
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.micronaut.application") version "4.4.2"
id("com.diffplug.spotless") version "6.23.3"
id("io.micronaut.aot") version "4.4.2"
}
version = "0.1"
group = "example.micronaut"
repositories {
mavenCentral()
}
dependencies {
annotationProcessor("io.micronaut:micronaut-http-validation")
annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
implementation("io.micronaut.email:micronaut-email-sendgrid")
implementation("io.micronaut:micronaut-http-client-jdk")
implementation("jakarta.mail:jakarta.mail-api:2.1.3")
implementation("io.micronaut.aws:micronaut-aws-lambda-events-serde")
implementation("io.micronaut.serde:micronaut-serde-jackson")
runtimeOnly("org.yaml:snakeyaml")
runtimeOnly("ch.qos.logback:logback-classic")
}
application {
mainClass = "example.micronaut.Application"
}
java {
sourceCompatibility = JavaVersion.toVersion("17")
targetCompatibility = JavaVersion.toVersion("17")
}
shadowJar {
// Ensure resources are included
mergeServiceFiles()
include 'EmailTemplate/**'
}
sourceSets {
main {
resources {
srcDirs = ['src/main/resources']
// include([ '**/*.properties', '**/*.yml', '**/*.json', '**/*.png', '**/*.html', '**/*.css','**/*.JPG'])
}
}
}
graalvmNative {
toolchainDetection = false
binaries {
main {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.matching("GraalVM Community")
}
resources.autodetect()
metadataRepository { enabled = true }
imageName.set('graal-mail')
buildArgs.add('--verbose')
buildArgs.add('--initialize-at-build-time=kotlin.coroutines.intrinsics.CoroutineSingletons')
buildArgs.add('--initialize-at-run-time=reactor.core.publisher.Traces$StackWalkerCallSiteSupplierFactory')
buildArgs.add('--initialize-at-run-time=reactor.core.publisher.Traces$ExceptionCallSiteSupplierFactory')
}
}
}
micronaut {
runtime("lambda_provided")
testRuntime("junit5")
processing {
incremental(true)
annotations("example.micronaut.*")
}
aot {
// Please review carefully the optimizations enabled below
// Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
optimizeServiceLoading = false
convertYamlToJava = false
precomputeOperations = true
cacheEnvironment = true
optimizeClassLoading = true
deduceEnvironment = true
optimizeNetty = true
replaceLogbackXml = true
}
}
tasks.named("dockerfileNative") {
baseImage = "amazonlinux:2023"
jdkVersion = "17"
args(
"-XX:MaximumHeapSizePercent=80",
"-Dio.netty.allocator.numDirectArenas=0",
"-Dio.netty.noPreferDirect=true"
)
}
spotless {
java {
licenseHeaderFile(file("LICENSEHEADER"))
}
}
- Конструктор электронной почты — снова на этом этапе я добавляю аннотации к вещам там, где они, вероятно, не нужны, но, кажется, ничто на это не влияет, приложение работает, но из лямбда-версии GraalVM электронные письма отправляются неправильно
Код: Выделить всё
package example.micronaut.services
import com.sendgrid.Response;
import example.micronaut.Util.MimeType;
import example.micronaut.Util.UtilMailService;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.annotation.ReflectiveAccess;
import io.micronaut.email.BodyType;
import io.micronaut.email.Email;
import io.micronaut.email.sendgrid.SendgridEmailSender;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.mail.internet.MimeBodyPart;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@Singleton
@ReflectiveAccess
public class TestCampaign {
private final UtilMailService utilMailService;
private final SendgridEmailSender sendgridEmailSender;
@Value("${micronaut.email.from.email}")
private String fromEmail;
@Inject
public TestCampaign(SendgridEmailSender sendgridEmailSender, UtilMailService utilMailService) {
this.sendgridEmailSender = sendgridEmailSender;
this.utilMailService = utilMailService;
}
public Response sendTestEmail() throws Exception {
AtomicInteger index = new AtomicInteger(0);
Email.Builder emailBuilder = getEmailBuilder();
utilMailService.getContacts("EmailTemplate/EmailListTest.json").forEach(contact -> {
if (index.getAndIncrement() == 0) {
emailBuilder.to(contact);
} else {
emailBuilder.bcc(contact);
}
});
return sendgridEmailSender.send(emailBuilder.build());
}
private Email.Builder getEmailBuilder() throws Exception {
Optional bodyOption = utilMailService.readHtmlFile("EmailTemplate/StdEmail.html");
String body = bodyOption.orElse("Be Aware of Your Prescriptions at work");
return Email.builder()
.from(fromEmail)
.subject("subject")
.body(body, BodyType.HTML)
.attachment(utilMailService.buildAttachment("EmailTemplate/Meds1.png", "meds1.png", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_PNG).build())
.attachment(utilMailService.buildAttachment("EmailTemplate/Meds2.png", "meds2.png", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_PNG).build())
.attachment(utilMailService.buildAttachment("EmailTemplate/Meds3.JPG", "meds3.JPG", MimeBodyPart.ATTACHMENT, MimeType.IMAGE_JPEG).build())
.attachment(utilMailService.buildAttachment("EmailTemplate/AllMeds.png", "AllMeds.png", MimeBodyPart
- Текущая утилита, добавляющая контакты из файла JSON, вложений и HTML-страницы из ресурсов.
Код: Выделить всё
package example.micronaut.Util;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.ReflectiveAccess;
import io.micronaut.core.io.IOUtils;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.core.type.Argument;
import io.micronaut.email.Attachment;
import io.micronaut.email.Contact;
import io.micronaut.serde.ObjectMapper;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.mail.internet.MimeBodyPart;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
@Singleton
@ReflectiveAccess
public class UtilMailService {
private final ResourceResolver resourceResolver;
private final ObjectMapper objectMapper;
@Inject
public UtilMailService(ResourceResolver resourceResolver, ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.resourceResolver = resourceResolver;
}
public @NonNull Attachment.Builder buildAttachment(String path, String name, String disposition, MimeType type) throws Exception {
Optional fileBytes = getClasspathResourceAsBytes(path);
if (fileBytes.isEmpty()) {
throw new IllegalArgumentException("File not found! " + path);
}
Attachment.Builder newAttachment = Attachment.builder().filename(name).contentType(type.getMimeType()).content(fileBytes.get());
if (Objects.equals(disposition, MimeBodyPart.INLINE)) {
newAttachment.id(name).disposition(disposition);
}
return newAttachment;
}
public Optional getClasspathResourceAsBytes(String path) throws Exception {
Optional url = resourceResolver.getResource("classpath:" + path);
if (url.isPresent()) {
try (InputStream inputStream = url.get().openStream()) {
return Optional.of(inputStream.readAllBytes());
}
}
else {
return Optional.empty();
}
}
public List getContacts(String path) throws IOException {
List contactList = new ArrayList();
Map contactMap = readJsonFileToMap(path).orElse(Map.of("[email protected]", "crash"));
contactMap.forEach((key, value) -> {
contactList.add(new Contact(key, value));
});
return contactList;
}
public @Nullable Optional readJsonFileToMap(String resourcePath) throws IOException {
Optional url = resourceResolver.getResource("classpath:" + resourcePath);
if (url.isPresent()) {
try (InputStream inputStream = url.get().openStream()) {
return Optional.of(objectMapper.readValue(inputStream.readAllBytes(), Argument.mapOf(String.class, String.class)));
}
}
else {
return Optional.empty();
}
}
public Optional readHtmlFile(String path) throws Exception {
Optional url = resourceResolver.getResource("classpath:" + path);
if (url.isPresent()) {
return Optional.of(IOUtils.readText(new BufferedReader(new InputStreamReader(url.get().openStream()))));
}
else {
return Optional.empty();
}
}
public Optional getClasspathResourceAsText(String path) throws Exception {
Optional url = resourceResolver.getResource("classpath:" + path);
if (url.isPresent()) {
return Optional.of(IOUtils.readText(new BufferedReader(new InputStreamReader(url.get().openStream()))));
}
else {
return Optional.empty();
}
}
}
- Код контроллера
Код: Выделить всё
package example.micronaut;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.sendgrid.Response;
import example.micronaut.services.EmailSendingService;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import io.micronaut.serde.ObjectMapper;
import jakarta.inject.Inject;
import java.util.Collections;
@Controller
public class HomeController {
private final EmailSendingService emailSendingService;
@Inject
ObjectMapper objectMapper;
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
@Inject
public HomeController(EmailSendingService emailSendingService) {
this.emailSendingService = emailSendingService;
}
@Get
public APIGatewayProxyResponseEvent index(@QueryValue(defaultValue = "test") String campaign) {
try {
Response sendGridResponse = emailSendingService.sendCustomizedEmail(campaign);
String json = new String(objectMapper.writeValueAsBytes(Collections.singletonMap("message", response.getHeaders())));
response.setStatusCode(sendGridResponse.getStatusCode());
response.setBody(json);
}
catch (Exception e) {
response.setStatusCode(500);
response.setBody(String.valueOf(e.getMessage()));
}
return response;
}
}
- текущая конфигурация времени выполнения

[*]тестовый ответ
-- журнал выполнения
Код: Выделить всё
{
"statusCode": 200,
"headers": {
"Date": "Sat, 14 Sep 2024 18:05:23 GMT",
"Content-Type": "application/json"
},
"multiValueHeaders": {
"Date": [
"Sat, 14 Sep 2024 18:05:23 GMT"
],
"Content-Type": [
"application/json"
]
},
"body": "{\"statusCode\":400,\"body\":\"{\\\"message\\\":null}\"}",
"isBase64Encoded": false
}
Код: Выделить всё
START RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7 Version: $LATEST
[36m18:05:23.612 [0;39m [1;30m[main] [0;39m [34mINFO [0;39m [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Status Code: 400
[36m18:05:23.612 [0;39m [1;30m[main] [0;39m [34mINFO [0;39m [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Body: {"errors":[{"message":"The from object must be provided for every email send. It is an object that requires the email parameter, but may also contain a name parameter. e.g. {\"email\" : \"[email protected]\"} or {\"email\" : \"[email protected]\", \"name\" : \"Example Recipient\"}.","field":"from.email","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#message.from"},{"message":"The personalizations field is required and must have at least one personalization.","field":"personalizations","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#-Personalizations-Errors"},{"message":"Unless a valid template_id is provided, the content parameter is required. There must be at least one defined content block. We typically suggest both text/plain and text/html blocks are included, but only one block is required.","field":"content","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#message.content"}]}
[36m18:05:23.612 [0;39m [1;30m[main] [0;39m [34mINFO [0;39m [35mi.m.e.sendgrid.SendgridEmailSender [0;39m - Headers {Strict-Transport-Security=max-age=600; includeSubDomains, Server=nginx, Access-Control-Allow-Origin=https://sendgrid.api-docs.io, Access-Control-Allow-Methods=POST, Connection=keep-alive, X-No-CORS-Reason=https://sendgrid.com/docs/Classroom/Basics/API/cors.html, Content-Length=980, Access-Control-Max-Age=600, Date=Sat, 14 Sep 2024 18:05:23 GMT, Access-Control-Allow-Headers=Authorization, Content-Type, On-behalf-of, x-sg-elas-acl, Content-Type=application/json}
END RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7
REPORT RequestId: b984c570-f7af-4d4c-a929-0ae8cf1fdcd7 Duration: 2722.27 ms Billed Duration: 2723 ms Memory Size: 128 MB Max Memory Used: 113 MB
Я попробовал добавить все, что мог придумать, в resources/META-INF/native-image/reflect-config.json . Пробовал различные способы, показанные в документации
micronaut, для запуска лямбда-выражения, т. е. с использованием метода контроллера, FunctionRequestHandler и FunctionLambdaRuntime. С каждым из них лямбда
работает и без проблем возвращает строку или APIGatewayProxyResponseEvent. Кажется, что-то есть с созданием электронной почты
Подробнее здесь: https://stackoverflow.com/questions/789 ... returned-f