Merge remote-tracking branch 'origin/feature-gmail-scan' into dev

This commit is contained in:
heyethereum
2024-08-04 19:37:47 +08:00
17 changed files with 593 additions and 27 deletions

View File

@@ -23,5 +23,9 @@ public class APIConstants {
public static final String API_URL_USER_SET_BOOKMARK = "/user/setBookmark";
public static final String API_URL_USER_DELETE_BOOKMARK = "/user/deleteBookmark";
public static final String API_URL_USER_DELETE_ALL_BOOKMARK = "/user/deleteAllBookmark";
public static final String API_URL_GMAIL_GET_EMAILS = "/gmail/getEmails";
public static final String API_URL_GMAIL_GET_SCANNED_EMAILS = "/gmail/getScannedEmails";
}

View File

@@ -27,8 +27,10 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;
import static com.safeqr.app.constants.APIConstants.*;
import java.io.IOException;
import java.lang.Thread;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
@RestController
@@ -100,14 +102,32 @@ public class GmailController {
return new ResponseEntity<>(json.toString(), HttpStatus.OK);
}
@GetMapping(value = "/gmail/getEmails", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getUserEmails(@RequestHeader(name = "accessToken") String accessToken) throws IOException, InterruptedException {
@GetMapping(value = API_URL_GMAIL_GET_SCANNED_EMAILS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ScannedGmailResponseDto> getUserScannedEmails(@RequestHeader(name = "X-USER-ID") String userId) {
logger.info("Invoking GET User scanned Emails endpoint");
return ResponseEntity.ok(gmailService.fetchScannedGmail(userId));
}
@GetMapping(value = API_URL_GMAIL_GET_EMAILS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getUserEmails(@RequestHeader(name = "accessToken") String accessToken,
@RequestHeader(name = "refreshToken") String refreshToken,
@RequestHeader(name = "X-USER-ID") String userId
) {
logger.info("Invoking GET Scan User Emails endpoints");
if (accessToken == null || accessToken.isEmpty()) {
return new ResponseEntity<>("Access token is missing", HttpStatus.BAD_REQUEST);
}
logger.info("accessToken -> {}", accessToken);
logger.info("refreshToken -> {}", refreshToken);
logger.info("userId -> {}", userId);
return new ResponseEntity<>(gmailService.getEmail(accessToken), HttpStatus.OK);
CompletableFuture.runAsync(() -> {
gmailService.getEmailAsync(userId, accessToken, refreshToken);
}).exceptionally(throwable -> {
logger.error("Unexpected error occurred while processing emails", throwable);
return null;
});
return new ResponseEntity<>("Scan Gmail Request is being processed", HttpStatus.ACCEPTED);
}
}

View File

@@ -0,0 +1,40 @@
package com.safeqr.app.gmail.entity;
import com.safeqr.app.qrcode.entity.QRCodeEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.UuidGenerator;
import java.util.UUID;
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "gmail_cid", schema = "safeqr")
public class GmailCidEntity {
@Id
@GeneratedValue(generator = "UUID")
@UuidGenerator
@Column(updatable = false, nullable = false)
private UUID id;
@Column(name = "gmail_id")
private UUID gmailId;
@Column(name = "cid")
private String cid;
@Column(name = "attachment_id")
private String attachmentId;
@Column(name = "decoded_content")
private String decodedContent;
@Column(name = "qr_code_id")
private UUID qrCodeId;
}

View File

@@ -0,0 +1,52 @@
package com.safeqr.app.gmail.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.UuidGenerator;
import java.time.OffsetDateTime;
import java.util.UUID;
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "gmail_emails", schema = "safeqr")
public class GmailEmailEntity {
@Id
@GeneratedValue(generator = "UUID")
@UuidGenerator
@Column(updatable = false, nullable = false)
private UUID id;
@Column(name = "user_id")
private String userId;
@Column(name = "message_id")
private String messageId;
@Column(name = "thread_id")
private String threadId;
@Column(name = "history_id")
private Long historyId;
@Column(name= "subject")
private String subject;
@Column(name = "date_received")
private OffsetDateTime dateReceived;
@Column(name = "date_created")
private OffsetDateTime dateCreated;
@PrePersist
public void prePersist() {
dateCreated = OffsetDateTime.now();
}
}

View File

@@ -0,0 +1,38 @@
package com.safeqr.app.gmail.entity;
import com.safeqr.app.qrcode.entity.QRCodeEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.UuidGenerator;
import java.util.UUID;
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "gmail_urls", schema = "safeqr")
public class GmailUrlEntity {
@Id
@GeneratedValue(generator = "UUID")
@UuidGenerator
@Column(updatable = false, nullable = false)
private UUID id;
@Column(name = "gmail_id")
private UUID gmailId;
@Column(name = "image_url")
private String imageUrl;
@Column(name = "decoded_content")
private String decodedContent;
@Column(name = "qr_code_id")
private UUID qrCodeId;
}

View File

@@ -1,6 +1,7 @@
package com.safeqr.app.gmail.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.safeqr.app.qrcode.model.QRCodeModel;
import lombok.Data;
import java.util.ArrayList;
@@ -8,6 +9,7 @@ import java.util.List;
@Data
public class EmailMessage {
private String messageId;
private String threadId;
private String subject;
private String historyId;
private String date;
@@ -18,13 +20,18 @@ public class EmailMessage {
@JsonInclude(JsonInclude.Include.NON_EMPTY)
List<QRCodeByURL> qrCodeByURL;
public EmailMessage(String messageId, String subject, String historyId, String date) {
@JsonInclude(JsonInclude.Include.NON_EMPTY)
List<QRCodeModel<?>> decodedContentsDetails;
public EmailMessage(String messageId, String threadId ,String subject, String historyId, String date) {
this.messageId = messageId;
this.threadId = threadId;
this.subject = subject;
this.historyId = historyId;
this.date = date;
this.qrCodeByContentId = new ArrayList<>();
this.qrCodeByURL = new ArrayList<>();
this.decodedContentsDetails = new ArrayList<>();
}
public void addQRCodeByContentId(QRCodeByContentId qrCode) {
this.qrCodeByContentId.add(qrCode);
@@ -34,6 +41,10 @@ public class EmailMessage {
this.qrCodeByURL.add(qrCode);
}
public void addQRCodeModel(QRCodeModel<?> qrCode) {
this.decodedContentsDetails.add(qrCode);
}
public boolean hasQRCodes() {
return !qrCodeByContentId.isEmpty() || !qrCodeByURL.isEmpty();
}

View File

@@ -1,5 +1,6 @@
package com.safeqr.app.gmail.model;
import com.safeqr.app.qrcode.model.QRCodeModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -14,4 +15,8 @@ public class QRCodeByContentId {
private String attachmentId;
private List<String> decodedContent;
private int totalQRCodeFound;
public List<String> getDecodedContent() {
return decodedContent;
}
}

View File

@@ -0,0 +1,11 @@
package com.safeqr.app.gmail.repository;
import com.safeqr.app.gmail.entity.GmailCidEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface GmailCidRespository extends JpaRepository<GmailCidEntity, UUID> {
List<GmailCidEntity> findByGmailId(UUID gmailId);
}

View File

@@ -0,0 +1,12 @@
package com.safeqr.app.gmail.repository;
import com.safeqr.app.gmail.entity.GmailEmailEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface GmailEmailRespository extends JpaRepository<GmailEmailEntity, UUID> {
List<GmailEmailEntity> findByUserId(String userId);
}

View File

@@ -0,0 +1,11 @@
package com.safeqr.app.gmail.repository;
import com.safeqr.app.gmail.entity.GmailUrlEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface GmailUrlsRespository extends JpaRepository<GmailUrlEntity, UUID> {
List<GmailUrlEntity> findByGmailId(UUID gmailId);
}

View File

@@ -1,32 +1,53 @@
package com.safeqr.app.gmail.service;
import com.google.api.client.auth.oauth2.BearerToken;
import com.google.api.client.auth.oauth2.ClientParametersAuthentication;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.googleapis.auth.oauth2.GoogleRefreshTokenRequest;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.*;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.qrcode.QRCodeMultiReader;
import com.google.zxing.qrcode.encoder.QRCode;
import com.safeqr.app.gmail.dto.ScannedGmailResponseDto;
import com.safeqr.app.gmail.entity.GmailCidEntity;
import com.safeqr.app.gmail.entity.GmailEmailEntity;
import com.safeqr.app.gmail.entity.GmailUrlEntity;
import com.safeqr.app.gmail.model.EmailMessage;
import com.safeqr.app.gmail.model.QRCodeByContentId;
import com.safeqr.app.gmail.model.QRCodeByURL;
import com.safeqr.app.gmail.repository.GmailCidRespository;
import com.safeqr.app.gmail.repository.GmailEmailRespository;
import com.safeqr.app.gmail.repository.GmailUrlsRespository;
import com.safeqr.app.qrcode.model.QRCodeModel;
import com.safeqr.app.qrcode.service.QRCodeTypeService;
import com.safeqr.app.user.service.UserService;
import com.safeqr.app.utils.DateParsingUtils;
import org.apache.commons.codec.binary.Base64;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.gmail.Gmail;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.Thread;
import java.net.ConnectException;
import java.net.URI;
@@ -35,50 +56,288 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpTimeoutException;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.api.client.googleapis.auth.oauth2.GoogleOAuthConstants.TOKEN_SERVER_URL;
import static com.safeqr.app.constants.APIConstants.APPLICATION_NAME;
@Service
public class GmailService {
@Value("${gmail.client.clientId}")
private String clientId;
@Value("${gmail.client.clientSecret}")
private String clientSecret;
private final GmailEmailRespository gmailEmailRespository;
private final GmailCidRespository gmailCidRespository;
private final GmailUrlsRespository gmailUrlsRespository;
private final QRCodeTypeService qrCodeTypeService;
private final UserService userService;
private static final Logger logger = LoggerFactory.getLogger(GmailService.class);
private static final HttpTransport httpTransport = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final long MAX_RESULTS = 100L;
private Gmail getGmailService(String accessToken) {
Credential userCredentials = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken);
public GmailService(GmailEmailRespository gmailEmailRespository,
GmailCidRespository gmailCidRespository,
GmailUrlsRespository gmailUrlsRespository,
QRCodeTypeService qrCodeTypeService,
UserService userService) {
this.gmailEmailRespository = gmailEmailRespository;
this.gmailCidRespository = gmailCidRespository;
this.gmailUrlsRespository = gmailUrlsRespository;
this.qrCodeTypeService = qrCodeTypeService;
this.userService = userService;
}
private Gmail getGmailService(String accessToken, String refreshToken) throws IOException {
Credential userCredentials = new Credential.Builder(BearerToken.authorizationHeaderAccessMethod())
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setTokenServerUrl(new GenericUrl(TOKEN_SERVER_URL))
.setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret))
.build()
.setAccessToken(accessToken)
.setRefreshToken(refreshToken);
return new Gmail.Builder(httpTransport, JSON_FACTORY, userCredentials)
.setApplicationName(APPLICATION_NAME)
.build();
}
// Renew the access token if it has expired using the refresh token.
private String refreshAccessToken(String refreshToken) throws IOException {
TokenResponse response = new GoogleRefreshTokenRequest(
httpTransport, JSON_FACTORY, refreshToken, clientId, clientSecret)
.execute();
return response.getAccessToken();
}
private Gmail refreshAndGetGmailService(String accessToken, String refreshToken) throws IOException {
try {
return getGmailService(accessToken, refreshToken);
} catch (GoogleJsonResponseException e) {
if (e.getStatusCode() == 401) {
logger.info("Access token expired. Refreshing token...");
String newAccessToken = refreshAccessToken(refreshToken);
return getGmailService(newAccessToken, refreshToken);
}
throw e;
}
}
// Async method to scan all emails in the user's inbox to prevent timeout.
@Async
public void getEmailAsync(String userId, String accessToken, String refreshToken) {
try {
ScannedGmailResponseDto result = getEmail(userId, accessToken, refreshToken);
CompletableFuture.completedFuture(result);
} catch (IOException e) {
logger.error("Error processing Gmail", e);
}
}
// Scan all emails in the user's inbox.
public ScannedGmailResponseDto getEmail(String accessToken) throws IOException, InterruptedException {
Gmail service = getGmailService(accessToken);
public ScannedGmailResponseDto getEmail(String userId, String accessToken, String refreshToken) throws IOException {
Gmail service = refreshAndGetGmailService(accessToken, refreshToken);
logger.info("Gmail service initialized: {}", service);
List<EmailMessage> emailMessagesList = new ArrayList<>();
String userId = "me";
String meUserId = "me";
String nextPageToken = null;
// Fetching email messages with page token and setting max results, Default value is 100.
do {
ListMessagesResponse listResponse = fetchMessages(service, userId, nextPageToken);
// ListHistoryResponse historyResponse = service.users().history().list(meUserId)
// .setStartHistoryId(BigInteger.valueOf(689335))
// .execute();
//
// List<History> historyList = historyResponse.getHistory();
//
// for (History history : historyList) {
// logger.info("History Id: {}, Message Id: {}, Message Snippet: {}", history.getId(), history.getMessages().get(0).getId(), history.getMessages().get(0).getHistoryId());
// }
ListMessagesResponse listResponse = fetchMessages(service, meUserId, nextPageToken);
List<Message> messages = listResponse.getMessages();
nextPageToken = listResponse.getNextPageToken();
// Iterate all the messages and add to emailMessagesList only if it has a valid QR code.
// Iterate all the messages and save to gmail db only if it has a valid QR code.
for (Message message : messages) {
EmailMessage emailMessage = processMessage(service, userId, message);
EmailMessage emailMessage = processMessage(service, meUserId, message);
if (emailMessage != null) {
emailMessagesList.add(emailMessage);
// Save email message to database.
saveEmailMessageAndScanQRCode(userId, emailMessage);
}
}
} while (nextPageToken != null);
// Update user's history id.
// TODO: Update user's history id.
return new ScannedGmailResponseDto(emailMessagesList);
}
// Save email message to database and scan QR code.
private void saveEmailMessageAndScanQRCode(String userId, EmailMessage emailMessage) {
GmailEmailEntity gmailEmailEntity = saveEmailMessage(userId, emailMessage);
if (gmailEmailEntity != null) {
// Save QR codes by content ID
saveQRCodeByContentId(gmailEmailEntity, emailMessage.getQrCodeByContentId());
// Save QR codes by URL
saveQRCodeByURL(gmailEmailEntity, emailMessage.getQrCodeByURL());
} else {
logger.warn("Skipping QR code processing due to failure in saving email message.");
}
}
// Save to gmail_email table
private GmailEmailEntity saveEmailMessage(String userId, EmailMessage emailMessage) {
logger.info("userId: {}", userId);
OffsetDateTime dateReceived = DateParsingUtils.parseDate(emailMessage.getDate());
try {
GmailEmailEntity gmailEmailEntity = GmailEmailEntity.builder()
.userId(userId)
.messageId(emailMessage.getMessageId())
.threadId(emailMessage.getThreadId())
.historyId(Long.valueOf(emailMessage.getHistoryId()))
.subject(emailMessage.getSubject())
.dateReceived(dateReceived)
.build();
return gmailEmailRespository.save(gmailEmailEntity);
} catch (DataIntegrityViolationException e) {
if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
logger.warn("Duplicate entry for userId: {}, messageId: {}", userId, emailMessage.getMessageId());
} else {
logger.error("Error saving to gmail_email table: {}", e.getMessage(), e);
}
} catch (Exception e) {
logger.error("Error saving gmail_email table: {}", e.getMessage(), e);
}
return null;
}
// Iterate through decoded contents found in attachment as content id and save to gmail_cid table
private void saveQRCodeByContentId(GmailEmailEntity gmailEmailEntity, List<QRCodeByContentId> qrCodeByContentIdList) {
qrCodeByContentIdList.forEach(cid -> {
cid.getDecodedContent().forEach(decodedContent -> {
try {
QRCodeModel<?> qrCodeModel = qrCodeTypeService.scanGmailDecodedContents(gmailEmailEntity.getUserId(), decodedContent);
GmailCidEntity gmailCidEntity = GmailCidEntity.builder()
.gmailId(gmailEmailEntity.getId())
.qrCodeId(qrCodeModel.getData().getId())
.cid(cid.getCid())
.attachmentId(cid.getAttachmentId())
.decodedContent(decodedContent)
.build();
gmailCidRespository.save(gmailCidEntity);
} catch (Exception e) {
logger.error("Error saving QR code by content ID to gmail_cid table: {}", e.getMessage(), e);
}
});
});
}
// Iterate through decoded content found in url and save to gmail_url table
private void saveQRCodeByURL(GmailEmailEntity gmailEmailEntity, List<QRCodeByURL> qrCodeByURLList) {
qrCodeByURLList.forEach(imageUrl -> {
imageUrl.getDecodedContent().forEach(decodedContent -> {
try {
QRCodeModel<?> qrCodeModel = qrCodeTypeService.scanGmailDecodedContents(gmailEmailEntity.getUserId(), decodedContent);
GmailUrlEntity gmailUrlEntity = GmailUrlEntity.builder()
.gmailId(gmailEmailEntity.getId())
.qrCodeId(qrCodeModel.getData().getId())
.imageUrl(imageUrl.getUrl())
.decodedContent(decodedContent)
.build();
gmailUrlsRespository.save(gmailUrlEntity);
} catch (Exception e) {
logger.error("Error saving QR code by URL to gmail_urls table: {}", e.getMessage(), e);
}
});
});
}
// Fetching Scanned Gmail from database
public ScannedGmailResponseDto fetchScannedGmail(String userId){
// Fetching all emails from gmail_email table
List<GmailEmailEntity> userEmailsList = gmailEmailRespository.findByUserId(userId);
List<EmailMessage> emailMessageList = new ArrayList<>();
if (userEmailsList != null && !userEmailsList.isEmpty()) {
userEmailsList.forEach(email -> {
EmailMessage emailMessage = new EmailMessage(
email.getMessageId(),
email.getThreadId(),
email.getSubject(),
email.getHistoryId().toString(),
email.getDateReceived().toString()
);
// Fetching all CIDs from gmail_cid table
List<GmailCidEntity> cidList = gmailCidRespository.findByGmailId(email.getId());
Map<String, QRCodeByContentId> qrCodeByContentIdMap = new HashMap<>();
for (GmailCidEntity cid : cidList) {
String key = cid.getCid() + "-" + cid.getAttachmentId();
QRCodeByContentId qrCodeByContentId = qrCodeByContentIdMap.get(key);
if (qrCodeByContentId == null) {
qrCodeByContentId = QRCodeByContentId.builder()
.cid(cid.getCid())
.attachmentId(cid.getAttachmentId())
.decodedContent(new ArrayList<>())
.totalQRCodeFound(0)
.build();
qrCodeByContentIdMap.put(key, qrCodeByContentId);
}
// Append decoded content to the existing list
qrCodeByContentId.getDecodedContent().add(cid.getDecodedContent());
// Fetch scanned QR code from database and add to message object
emailMessage.addQRCodeModel(qrCodeTypeService.getScannedQRCodeDetailsInModel(cid.getQrCodeId()));
qrCodeByContentId.setTotalQRCodeFound(qrCodeByContentId.getTotalQRCodeFound() + 1);
}
emailMessage.setQrCodeByContentId(new ArrayList<>(qrCodeByContentIdMap.values()));
// Fetching all URLs from gmail_urls table
List<GmailUrlEntity> urlList = gmailUrlsRespository.findByGmailId(email.getId());
Map<String, QRCodeByURL> qrCodeByURLMap = new HashMap<>();
for (GmailUrlEntity url : urlList) {
String key = url.getImageUrl();
QRCodeByURL qrCodeByURL = qrCodeByURLMap.get(key);
if (qrCodeByURL == null) {
qrCodeByURL = QRCodeByURL.builder()
.url(url.getImageUrl())
.decodedContent(new ArrayList<>())
.totalQRCodeFound(0)
.build();
qrCodeByURLMap.put(key, qrCodeByURL);
}
// Append decoded content to the existing list
qrCodeByURL.getDecodedContent().add(url.getDecodedContent());
// Fetch scanned QR code from database and add to message object
emailMessage.addQRCodeModel(qrCodeTypeService.getScannedQRCodeDetailsInModel(url.getQrCodeId()));
qrCodeByURL.setTotalQRCodeFound(qrCodeByURL.getTotalQRCodeFound() + 1);
}
emailMessage.setQrCodeByURL(new ArrayList<>(qrCodeByURLMap.values()));
emailMessageList.add(emailMessage);
});
}
return ScannedGmailResponseDto.builder().messages(emailMessageList).build();
}
// Fetching email messages with page token and setting max results
private ListMessagesResponse fetchMessages(Gmail service, String userId, String pageToken) throws IOException {
return service.users().messages().list(userId)
@@ -104,7 +363,7 @@ public class GmailService {
logger.info("Message ID: {}", message.getId());
logger.info("History ID: {}", message.getHistoryId());
EmailMessage emailMessage = new EmailMessage(message.getId(), subject, String.valueOf(message.getHistoryId()), emailDate);
EmailMessage emailMessage = new EmailMessage(message.getId(), message.getThreadId(), subject, String.valueOf(message.getHistoryId()), emailDate);
processAttachments(service, message.getId(), parts, attachmentIds, emailMessage);
processImageUrls(imageUrls, emailMessage);
@@ -290,5 +549,4 @@ public class GmailService {
String lowerUrl = url.toLowerCase();
return lowerUrl.endsWith(".jpg") || lowerUrl.endsWith(".jpeg") || lowerUrl.endsWith(".png") || lowerUrl.endsWith(".gif") || lowerUrl.endsWith(".bmp");
}
}

View File

@@ -1,7 +1,6 @@
package com.safeqr.app.qrcode.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.safeqr.app.qrcode.entity.EmailEntity;
import com.safeqr.app.qrcode.entity.QRCodeEntity;
import com.safeqr.app.qrcode.entity.URLEntity;
import com.safeqr.app.qrcode.service.URLVerificationService;
@@ -12,8 +11,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.net.URISyntaxException;
@EqualsAndHashCode(callSuper = true)
@Data
public final class URLModel extends QRCodeModel<URLEntity> {

View File

@@ -68,13 +68,18 @@ public class QRCodeTypeService {
}
// Get scanned qrcode details
public BaseScanResponse getScannedQRCodeDetails(UUID qrCodeId){
return BaseScanResponse.builder().qrcode(getScannedQRCodeDetailsInModel(qrCodeId)).build();
}
public QRCodeModel<?> getScannedQRCodeDetailsInModel(UUID qrCodeId){
// Find scanned qr code in qr code table
QRCodeEntity qrCodeEntity = qrCodeRepository.findById(qrCodeId)
.orElseThrow(() -> new ResourceNotFoundExceptions("QR Code not found with id: " + qrCodeId));
logger.info("qrCodeEntity: {}", qrCodeEntity);
QRCodeModel<?> qrCodeModel = qrCodeFactoryProvider.createQRCodeInstance(qrCodeEntity);
logger.info("Retrieved details: {}", qrCodeModel.getDetails());
return BaseScanResponse.builder().qrcode(qrCodeModel).build();
return qrCodeModel;
}
// Process Scanned QR Code
@@ -109,6 +114,28 @@ public class QRCodeTypeService {
return BaseScanResponse.builder().qrcode(qrCodeModel).build();
}
@Transactional
public QRCodeModel<?> scanGmailDecodedContents(String userId, String data) {
logger.info("Scan Gmail content: userId={}, data={}", userId, data);
// Get the QR Code Type
QRCodeTypeEntity qrType = getQRCodeType(data);
// Insert the QR Code into main qrcode table
QRCodeEntity scannedQR = qrCodeRepository.save(QRCodeEntity.builder()
.userId(userId)
.contents(data)
.info(qrType)
.createdAt(LocalDateTime.now())
.build());
// Create the QR Code Instance based on the QR Code Type & insert into the respective table
QRCodeModel<?> qrCodeModel = qrCodeFactoryProvider.createQRCodeInstance(scannedQR);
qrCodeModel.setDetails();
return qrCodeModel;
}
// Returns Default type as text if it does not fit into any of the category
private QRCodeTypeEntity getQRCodeType(String data) {
return configs.stream()

View File

@@ -30,8 +30,9 @@ public class URLVerificationService {
public URLEntity getURLEntityByQRCodeId(UUID qrCodeId) {
logger.info("qrCodeId retrieving: {}", qrCodeId);
return urlRepository.findByQrCodeId(qrCodeId)
.orElseThrow(() -> new ResourceNotFoundExceptions("URL not found for QR Code id: " + qrCodeId));
// return urlRepository.findByQrCodeId(qrCodeId)
// .orElseThrow(() -> new ResourceNotFoundExceptions("URL not found for QR Code id: " + qrCodeId));
return urlRepository.findByQrCodeId(qrCodeId).orElse(null);
}
public void insertDB(URLEntity urlEntity) {

View File

@@ -33,4 +33,7 @@ public class UserEntity {
@Column(name = "roles", columnDefinition = "text[]")
private List<String> roles;
private String status;
@Column(name = "gmail_history_id")
private Long gmailHistoryId;
}

View File

@@ -0,0 +1,34 @@
package com.safeqr.app.utils;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// Sets the number of core threads. These threads are always kept alive.
executor.setCorePoolSize(2);
// Sets the maximum number of threads that can be created by the pool.
executor.setMaxPoolSize(2);
// Sets the size of the queue to hold tasks before they are executed.
executor.setQueueCapacity(500);
// Sets the prefix for the names of the threads created by this pool.
executor.setThreadNamePrefix("GmailProcessing-");
// Initializes the executor to apply the configuration and make it ready to use.
executor.initialize();
return executor;
}
}

View File

@@ -0,0 +1,42 @@
package com.safeqr.app.utils;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class DateParsingUtils {
private DateParsingUtils() {
// private constructor to hide the implicit public one
}
private static final DateTimeFormatter INPUT_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("EEE, ")
.appendPattern("[ ]") // This makes a single space optional
.appendPattern("[ ]") // This allows for a second optional space
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, java.time.format.SignStyle.NOT_NEGATIVE)
.appendPattern(" MMM yyyy HH:mm:ss Z")
.toFormatter(Locale.ENGLISH);
private static final DateTimeFormatter OUTPUT_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS Z");
public static String parseAndFormatDate(String inputDate) {
try {
OffsetDateTime dateTime = OffsetDateTime.parse(inputDate, INPUT_FORMATTER);
return dateTime.format(OUTPUT_FORMATTER);
} catch (Exception e) {
throw new RuntimeException("Error parsing date: " + inputDate, e);
}
}
public static OffsetDateTime parseDate(String inputDate) {
try {
return OffsetDateTime.parse(inputDate, INPUT_FORMATTER);
} catch (Exception e) {
throw new RuntimeException("Error parsing date: " + inputDate, e);
}
}
}