diff --git a/modules/research-framework/research-service/pom.xml b/modules/research-framework/research-service/pom.xml index 79deaf1ee8..ccd45abe81 100644 --- a/modules/research-framework/research-service/pom.xml +++ b/modules/research-framework/research-service/pom.xml @@ -17,233 +17,247 @@ specific language governing permissions and limitations under the License. --> - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - - org.apache.airavata - airavata - 0.21-SNAPSHOT - ../../../pom.xml - + + org.apache.airavata + airavata + 0.21-SNAPSHOT + ../../../pom.xml + - research-service - Airavata Research Service + research-service + Airavata Research Service - - apache-airavata-research-service-${project.version} - + + apache-airavata-research-service-${project.version} + - - - org.mariadb.jdbc - mariadb-java-client - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - org.springframework.boot - spring-boot-starter-web - - - org.apache.logging.log4j - log4j-to-slf4j - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.apache.logging.log4j - log4j-to-slf4j - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - org.springframework.boot - spring-boot-starter-log4j2 - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - - - io.grpc - grpc-stub - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - io.grpc - grpc-protobuf - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - com.google.protobuf - protobuf-java - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - net.devh - grpc-server-spring-boot-starter - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - - - javax.validation - validation-api - - - org.apache.airavata - airavata-api - ${project.version} - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - org.slf4j - slf4j-log4j12 - - - - + + + org.mariadb.jdbc + mariadb-java-client + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.logging.log4j + log4j-to-slf4j + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.apache.logging.log4j + log4j-to-slf4j + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-mail + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + io.grpc + grpc-stub + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + io.grpc + grpc-protobuf + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + com.google.protobuf + protobuf-java + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + net.devh + grpc-server-spring-boot-starter + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + + javax.validation + validation-api + + + org.apache.airavata + airavata-api + ${project.version} + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + org.slf4j + slf4j-log4j12 + + + + - - - - org.apache.maven.plugins - maven-assembly-plugin - - - research-service-distribution-package - package - - single - - - posix - ${research.service.dist.name} - - src/main/assembly/research-service-bin-assembly.xml - - false - - - - - - io.github.ascopes - protobuf-maven-plugin - - 4.30.1 - - ${project.basedir}/src/main/proto - - - - io.grpc - protoc-gen-grpc-java - 1.73.0 - @generated=omit - - - - - - - generate - - generate-sources - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 17 - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - - - src/main/resources/log4j2.xml - - - - - - \ No newline at end of file + + + + org.apache.maven.plugins + maven-assembly-plugin + + + research-service-distribution-package + package + + single + + + posix + ${research.service.dist.name} + + src/main/assembly/research-service-bin-assembly.xml + + false + + + + + + io.github.ascopes + protobuf-maven-plugin + + 4.30.1 + + ${project.basedir}/src/main/proto + + + + io.grpc + protoc-gen-grpc-java + 1.73.0 + @generated=omit + + + + + + + generate + + generate-sources + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/main/resources/log4j2.xml + + + + + + diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/AiravataService.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/AiravataService.java index 8e81ad6b2f..7222de879b 100644 --- a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/AiravataService.java +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/AiravataService.java @@ -21,6 +21,7 @@ import org.apache.airavata.model.security.AuthzToken; import org.apache.airavata.model.user.UserProfile; +import org.apache.airavata.research.service.enums.EmailType; import org.apache.airavata.research.service.model.UserContext; import org.apache.airavata.service.profile.client.ProfileServiceClientFactory; import org.apache.airavata.service.profile.user.cpi.UserProfileService; @@ -28,6 +29,7 @@ import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -36,12 +38,15 @@ public class AiravataService { private static final Logger LOGGER = LoggerFactory.getLogger(AiravataService.class); - @Value("${airavata.user-profile.server.url:airavata.host}") + @Value("${airavata.user-profile.server.url:localhost}") private String profileServerUrl; @Value("${airavata.user-profile.server.port:8962}") private int profileServerPort; + @Autowired + private EmailService emailService; + public UserProfileService.Client userProfileClient() { try { LOGGER.info("User profile client initialized"); @@ -63,6 +68,10 @@ public UserProfile getUserProfile(String userId) { public UserProfile getUserProfile(AuthzToken authzToken, String userId, String gatewayId) { try { + boolean result = userProfileClient().doesUserExist(authzToken, userId, gatewayId); + if (!result) { + sendEmail(userId); + } return userProfileClient().getUserProfileById(authzToken, userId, gatewayId); } catch (TException e) { LOGGER.error("Error while getting user profile with the id: {} in the gateway: {}", userId, gatewayId, e); @@ -70,4 +79,10 @@ public UserProfile getUserProfile(AuthzToken authzToken, String userId, String g "Error while getting user profile with the id: " + userId + " in the gateway: " + gatewayId, e); } } + + private void sendEmail(String to) { + if (!emailService.hasSentEmail(to, EmailType.NEW_USER)) { + emailService.sendSimpleMessage(to, EmailType.NEW_USER); + } + } } diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/EmailService.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/EmailService.java new file mode 100644 index 0000000000..04750517c5 --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/EmailService.java @@ -0,0 +1,66 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service; + +import org.apache.airavata.research.service.enums.EmailType; +import org.apache.airavata.research.service.model.entity.EmailRecord; +import org.apache.airavata.research.service.model.repo.EmailRecordRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +@Service +public class EmailService { + + @Autowired + private JavaMailSender mailSender; + + @Value("${spring.mail.username}") + private String fromEmail; + + @Value("${airavata.research-portal.admin-notification-email}") + private String adminEmail; + + @Autowired + private EmailRecordRepository emailRecordRepository; + + public void sendSimpleMessage(String newUserEmail, EmailType type) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(fromEmail); + message.setTo(adminEmail); + message.setSubject(type.getSubject()); + message.setText(type.getMessage(newUserEmail)); + mailSender.send(message); + + EmailRecord record = new EmailRecord(); + record.setEmailType(type); + record.setUserId(newUserEmail); + emailRecordRepository.save(record); + emailRecordRepository.flush(); + } + + public boolean hasSentEmail(String toEmail, EmailType emailType) { + return emailRecordRepository + .findByUserIdAndEmailType(toEmail, emailType) + .isPresent(); + } +} diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/EmailType.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/EmailType.java new file mode 100644 index 0000000000..77853f900a --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/EmailType.java @@ -0,0 +1,40 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service.enums; + +public enum EmailType { + NEW_USER { + public String getSubject() { + return "New Cybershuttle User"; + } + + public String getMessage(String newUserEmail) { + return String.format( + """ + Hi, %s has just signed up for Cybershuttle. + """, + newUserEmail); + } + }; + + public abstract String getSubject(); + + public abstract String getMessage(String newUserEmail); +} diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/EmailRecord.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/EmailRecord.java new file mode 100644 index 0000000000..8317b41b95 --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/EmailRecord.java @@ -0,0 +1,100 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service.model.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import java.time.Instant; +import org.apache.airavata.research.service.enums.EmailType; +import org.hibernate.annotations.UuidGenerator; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Entity(name = "EMAIL") +@EntityListeners(AuditingEntityListener.class) +public class EmailRecord { + + @Id + @GeneratedValue + @UuidGenerator + @Column(nullable = false, updatable = false, length = 48) + private String id; + + @Column(nullable = false) + private String userId; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private EmailType emailType; + + @Column(nullable = false, updatable = false) + @CreatedDate + private Instant createdAt; + + @Column(nullable = false) + @LastModifiedDate + private Instant updatedAt; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public EmailType getEmailType() { + return emailType; + } + + public void setEmailType(EmailType emailType) { + this.emailType = emailType; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/EmailRecordRepository.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/EmailRecordRepository.java new file mode 100644 index 0000000000..d81ae9005b --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/EmailRecordRepository.java @@ -0,0 +1,32 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service.model.repo; + +import java.util.Optional; +import org.apache.airavata.research.service.enums.EmailType; +import org.apache.airavata.research.service.model.entity.EmailRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmailRecordRepository extends JpaRepository { + + Optional findByUserIdAndEmailType(String userId, EmailType emailType); +} diff --git a/modules/research-framework/research-service/src/main/resources/application.yml b/modules/research-framework/research-service/src/main/resources/application.yml index 0652644a64..042101b531 100644 --- a/modules/research-framework/research-service/src/main/resources/application.yml +++ b/modules/research-framework/research-service/src/main/resources/application.yml @@ -31,6 +31,7 @@ airavata: research-portal: url: http://airavata.host:5173 dev-url: http://airavata.host:5173 + admin-notification-email: "TO_EMAIL@gmail.com" openid: url: "http://airavata.host:18080/realms/default" user-profile: @@ -55,6 +56,17 @@ spring: hibernate: ddl-auto: update open-in-view: false + mail: + host: smtp.gmail.com + port: 587 + username: youremail@gmail.com + password: your-app-password # Use an App Password from Google + properties: + mail: + smtp: + auth: true + starttls: + enable: true springdoc: api-docs: