Skip to content

Commit

Permalink
Time format fix for last login times (mosip#692)
Browse files Browse the repository at this point in the history
* Minor correction to the id prop

* minor refactoring

* Fix missing code in refactoring

* Added timezone logger

* Minor correction

* Pom correction

* Added timezone based timestamp responses for related APIs

* Test fixes

* Test fix

* Fix to filename pattern

* Added missing file

* Refactored to fix utility class name

* Refactored to fix utility class name

* Fixed last login time offset; filename timestamp; vid generated on time;

Co-authored-by: Loganathan Sekar <[email protected]>
Signed-off-by: kameshsr <[email protected]>
  • Loading branch information
2 people authored and kameshsr committed Oct 17, 2023
1 parent 27a5d06 commit a83e4ea
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -633,11 +633,11 @@ public ResponseEntity<Object> downLoadServiceHistory(
@ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(schema = @Schema(hidden = true))) })

public ResponseWrapper<UserInfoDto> userinfo()
public ResponseWrapper<UserInfoDto> userinfo(@RequestHeader(name = "time-zone-offset", required = false, defaultValue = "0") int timeZoneOffset)
throws ResidentServiceCheckedException, ApisResourceAccessException {
logger.debug("ResidentController::getuserinfo()::entry");
String Id = identityServiceImpl.getResidentIdaToken();
ResponseWrapper<UserInfoDto> userInfoDto = residentService.getUserinfo(Id);
ResponseWrapper<UserInfoDto> userInfoDto = residentService.getUserinfo(Id, timeZoneOffset);
logger.debug("ResidentController::getuserinfo()::exit");
return userInfoDto;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.mosip.resident.dto;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import lombok.Data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ byte[] downLoadServiceHistory(ResponseWrapper<PageDto<ServiceHistoryResponseDto>
String languageCode, LocalDateTime eventReqDateTime, LocalDate fromDateTime, LocalDate toDateTime,
String serviceType, String statusFilter) throws ResidentServiceCheckedException, IOException;

public ResponseWrapper<UserInfoDto> getUserinfo(String Id) throws ApisResourceAccessException;
public ResponseWrapper<UserInfoDto> getUserinfo(String Id, int timeZoneOffset) throws ApisResourceAccessException;

public String getFileName(String eventId, int timeZoneOffset);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,7 @@ public PageDto<ServiceHistoryResponseDto> getServiceHistoryResponse(String sortT
LocalDate toDateTime, String serviceType, String langCode, int timeZoneOffset)
throws ResidentServiceCheckedException {
String nativeQueryString = getDynamicNativeQueryString(sortType, idaToken, pageStart, pageFetch, statusFilter,
searchText, fromDateTime, toDateTime, serviceType);
searchText, fromDateTime, toDateTime, serviceType, timeZoneOffset);
Query nativeQuery = entityManager.createNativeQuery(nativeQueryString, ResidentTransactionEntity.class);
List<ResidentTransactionEntity> residentTransactionEntityList = (List<ResidentTransactionEntity>) nativeQuery
.getResultList();
Expand All @@ -1700,24 +1700,24 @@ public PageDto<ServiceHistoryResponseDto> getServiceHistoryResponse(String sortT

public String getDynamicNativeQueryString(String sortType, String idaToken, Integer pageStart, Integer pageFetch,
String statusFilter, String searchText, LocalDate fromDateTime, LocalDate toDateTime,
String serviceType) {
String serviceType, int timeZoneOffset) {
String query = "SELECT * FROM resident_transaction where token_id = '"
+ idaToken+"'";
String dynamicQuery = "";
if (fromDateTime != null && toDateTime != null && serviceType != null && !serviceType.equalsIgnoreCase("ALL")
&& statusFilter != null && searchText != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime) + getServiceQuery(serviceType)
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getServiceQuery(serviceType)
+ getStatusFilterQuery(statusFilter) + getSearchQuery(searchText);
} else if (fromDateTime != null && toDateTime != null && serviceType != null
&& !serviceType.equalsIgnoreCase("ALL") && statusFilter != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime) + getServiceQuery(serviceType )
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getServiceQuery(serviceType )
+ getStatusFilterQuery(statusFilter );
} else if (fromDateTime != null && toDateTime != null && serviceType != null
&& !serviceType.equalsIgnoreCase("ALL") && searchText != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime ) + getServiceQuery(serviceType )
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getServiceQuery(serviceType )
+ getSearchQuery(searchText );
} else if (fromDateTime != null && toDateTime != null && statusFilter != null && searchText != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime ) + getStatusFilterQuery(statusFilter )
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getStatusFilterQuery(statusFilter )
+ getSearchQuery(searchText );
} else if (serviceType != null && !serviceType.equalsIgnoreCase("ALL") && statusFilter != null
&& searchText != null) {
Expand All @@ -1730,14 +1730,14 @@ public String getDynamicNativeQueryString(String sortType, String idaToken, Inte
} else if (statusFilter != null && searchText != null) {
dynamicQuery = getStatusFilterQuery(statusFilter ) + getSearchQuery(searchText );
} else if (fromDateTime != null && toDateTime != null && searchText != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime ) + getSearchQuery(searchText );
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getSearchQuery(searchText );
} else if (fromDateTime != null && toDateTime != null && statusFilter != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime ) + getStatusFilterQuery(statusFilter );
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getStatusFilterQuery(statusFilter );
} else if (fromDateTime != null && toDateTime != null && serviceType != null
&& !serviceType.equalsIgnoreCase("ALL")) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime ) + getServiceQuery(serviceType );
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset) + getServiceQuery(serviceType );
} else if (fromDateTime != null && toDateTime != null) {
dynamicQuery = getDateQuery(fromDateTime, toDateTime );
dynamicQuery = getDateQuery(fromDateTime, toDateTime, timeZoneOffset);
} else if (serviceType != null && !serviceType.equalsIgnoreCase("ALL")) {
dynamicQuery = getServiceQuery(serviceType );
} else if (statusFilter != null) {
Expand Down Expand Up @@ -1768,9 +1768,10 @@ private String getServiceQuery(String serviceType) {
return " and request_type_code in (" + serviceTypeListString + ")";
}

private String getDateQuery(LocalDate fromDate, LocalDate toDate) {
LocalDateTime fromDateTime = fromDate.atStartOfDay();
LocalDateTime toDateTime = toDate.plusDays(1).atStartOfDay();
private String getDateQuery(LocalDate fromDate, LocalDate toDate, int timeZoneOffset) {
//Converting local time to UTC before using in db query
LocalDateTime fromDateTime = fromDate.atStartOfDay().plusMinutes(timeZoneOffset);
LocalDateTime toDateTime = toDate.plusDays(1).atStartOfDay().plusMinutes(timeZoneOffset);
return " and cr_dtimes between '" + fromDateTime + "' and '" +
toDateTime+ "'";
}
Expand Down Expand Up @@ -2171,7 +2172,7 @@ public byte[] downLoadServiceHistory(ResponseWrapper<PageDto<ServiceHistoryRespo
if(serviceType == null){
serviceType = ALL;
}
servHistoryMap.put("eventReqTimeStamp", eventReqDateTime);
servHistoryMap.put("eventReqTimeStamp", utility.formatWithOffsetForUI(0, eventReqDateTime));//No change to the offset - keep the incoming timestamp with same offset
servHistoryMap.put("fromDate", fromDate);
servHistoryMap.put("toDate", toDate);
servHistoryMap.put("statusFilter", statusFilter);
Expand All @@ -2187,7 +2188,7 @@ public byte[] downLoadServiceHistory(ResponseWrapper<PageDto<ServiceHistoryRespo
}

@Override
public ResponseWrapper<UserInfoDto> getUserinfo(String Id) throws ApisResourceAccessException {
public ResponseWrapper<UserInfoDto> getUserinfo(String Id, int timeZoneOffset) throws ApisResourceAccessException {
String name = identityServiceImpl.getAvailableclaimValue(env.getProperty(ResidentConstants.NAME_FROM_PROFILE));
String photo = identityServiceImpl.getAvailableclaimValue(env.getProperty(IMAGE));
String email = identityServiceImpl.getAvailableclaimValue(env.getProperty(ResidentConstants.EMAIL_FROM_PROFILE));
Expand All @@ -2207,7 +2208,7 @@ public ResponseWrapper<UserInfoDto> getUserinfo(String Id) throws ApisResourceAc
user.setIp(response.get().getIpAddress());
user.setMachineType(response.get().getMachineType());
user.setHost(response.get().getHost());
user.setLastLogin(response.get().getLastloginDtime());
user.setLastLogin(utility.applyTimeZoneOffsetOnDateTime(timeZoneOffset, response.get().getLastloginDtime()));
user.setPhoto(data);
responseWrapper.setResponse(user);
return responseWrapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
@Component
public class ResidentVidServiceImpl implements ResidentVidService {

private static final String GENRATED_ON_TIMESTAMP = "genratedOnTimestamp";

private static final String EXPIRY_TIMESTAMP = "expiryTimestamp";

private static final String TRANSACTIONS_LEFT_COUNT = "transactionsLeftCount";
Expand Down Expand Up @@ -735,7 +737,8 @@ public ResponseWrapper<List<Map<String,?>>> retrieveVidsfromUin(String uin, int
LinkedHashMap<String, Object> lhm = new LinkedHashMap<String, Object>(map);
getMaskedVid(lhm);
getRefIdHash(lhm);
normalizeExpiryTime(lhm, timeZoneOffset);
normalizeTime(EXPIRY_TIMESTAMP, lhm, timeZoneOffset);
normalizeTime(GENRATED_ON_TIMESTAMP, lhm, timeZoneOffset);
return lhm;
})
.collect(Collectors.toList());
Expand All @@ -748,16 +751,16 @@ public ResponseWrapper<List<Map<String,?>>> retrieveVidsfromUin(String uin, int

}

private void normalizeExpiryTime(LinkedHashMap<String, Object> lhm, int timeZoneOffset) {
Object expiryTimeObj = lhm.get(EXPIRY_TIMESTAMP);
if(expiryTimeObj instanceof String) {
String expiryTime = String.valueOf(expiryTimeObj);
LocalDateTime expiryLocalDateTime = mapper.convertValue(expiryTime, LocalDateTime.class);
private void normalizeTime(String attributeName, LinkedHashMap<String, Object> lhm, int timeZoneOffset) {
Object timeObject = lhm.get(attributeName);
if(timeObject instanceof String) {
String timeStr = String.valueOf(timeObject);
LocalDateTime localDateTime = mapper.convertValue(timeStr, LocalDateTime.class);
//For the big expiry time, assume no expiry time, so set to null
if(expiryLocalDateTime.getYear() >= 9999) {
lhm.put(EXPIRY_TIMESTAMP, null);
if(localDateTime.getYear() >= 9999) {
lhm.put(attributeName, null);
} else {
lhm.put(EXPIRY_TIMESTAMP, utility.formatWithOffsetForUI(timeZoneOffset, expiryLocalDateTime)) ;
lhm.put(attributeName, utility.formatWithOffsetForUI(timeZoneOffset, localDateTime)) ;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,58 @@
package io.mosip.resident.util;

import static io.mosip.resident.constant.MappingJsonConstants.EMAIL;
import static io.mosip.resident.constant.MappingJsonConstants.PHONE;
import static io.mosip.resident.constant.RegistrationConstants.DATETIME_PATTERN;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.assertj.core.util.Lists;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.mvel2.MVEL;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.integration.impl.MapVariableResolverFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.util.IOUtils;

import io.mosip.kernel.core.exception.ServiceError;
import io.mosip.kernel.core.http.RequestWrapper;
import io.mosip.kernel.core.http.ResponseWrapper;
Expand Down Expand Up @@ -30,55 +81,6 @@
import io.mosip.resident.exception.ResidentServiceException;
import io.mosip.resident.repository.ResidentTransactionRepository;
import io.mosip.resident.service.impl.IdentityServiceImpl;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.assertj.core.util.Lists;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.mvel2.MVEL;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.integration.impl.MapVariableResolverFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import static io.mosip.resident.constant.MappingJsonConstants.EMAIL;
import static io.mosip.resident.constant.MappingJsonConstants.PHONE;
import static io.mosip.resident.constant.RegistrationConstants.DATETIME_PATTERN;

/**
* @author Girish Yarru
Expand Down Expand Up @@ -522,16 +524,19 @@ public String getRefIdHash(String individualId) throws NoSuchAlgorithmException
}

private String formatDateTimeForPattern(LocalDateTime localDateTime, String dateTimePattern) {
return localDateTime.format(
DateTimeFormatter.ofPattern(dateTimePattern));
return localDateTime == null ? null : localDateTime.format(DateTimeFormatter.ofPattern(dateTimePattern));
}

public String formatWithOffsetForUI(int timeZoneOffset, LocalDateTime localDateTime) {
return formatDateTimeForPattern(localDateTime.minusMinutes(timeZoneOffset), Objects.requireNonNull(env.getProperty(ResidentConstants.UI_DATE_TIME_PATTERN)));
return formatDateTimeForPattern(applyTimeZoneOffsetOnDateTime(timeZoneOffset, localDateTime), Objects.requireNonNull(env.getProperty(ResidentConstants.UI_DATE_TIME_PATTERN)));
}

public LocalDateTime applyTimeZoneOffsetOnDateTime(int timeZoneOffset, LocalDateTime localDateTime) {
return localDateTime == null ? null : localDateTime.minusMinutes(timeZoneOffset); //Converting UTC to local time zone
}

public String formatWithOffsetForFileName(int timeZoneOffset, LocalDateTime localDateTime) {
return formatDateTimeForPattern(localDateTime.minusMinutes(timeZoneOffset), Objects.requireNonNull(env.getProperty(ResidentConstants.FILENAME_DATETIME_PATTERN)));
return formatDateTimeForPattern(applyTimeZoneOffsetOnDateTime(timeZoneOffset, localDateTime), Objects.requireNonNull(env.getProperty(ResidentConstants.FILENAME_DATETIME_PATTERN)));
}

public String getClientIp(HttpServletRequest req) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,8 @@ public void testGetUserInfo() throws Exception {
user.setFullName("name");
ResponseWrapper<UserInfoDto> response = new ResponseWrapper<>();
response.setResponse(user);
residentController.userinfo();
Mockito.when(residentService.getUserinfo(Mockito.any())).thenReturn(response);
residentController.userinfo(0);
Mockito.when(residentService.getUserinfo(Mockito.any(), Mockito.anyInt())).thenReturn(response);
this.mockMvc.perform(get("/profile"))
.andExpect(status().isOk());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public void testGetUserInfo() throws ApisResourceAccessException{
residentUserEntity.setIpAddress("http");
Optional<ResidentUserEntity> response = Optional.of(residentUserEntity);
Mockito.when(residentUserRepository.findById(Mockito.anyString())).thenReturn(response);
ResponseWrapper<UserInfoDto> responseWrapper = residentServiceImpl.getUserinfo("123");
ResponseWrapper<UserInfoDto> responseWrapper = residentServiceImpl.getUserinfo("123", 0);
assertEquals(responseWrapper.getResponse().getFullName(), responseWrapper.getResponse().getFullName());
}

Expand All @@ -204,7 +204,7 @@ public void testGetUserInfoFailed() throws ApisResourceAccessException {
Mockito.when(identityServiceImpl.getClaimFromIdToken(Mockito.anyString())).thenReturn("claim");
Optional<ResidentUserEntity> response = Optional.empty();
Mockito.when(residentUserRepository.findById(Mockito.anyString())).thenReturn(response);
ResponseWrapper<UserInfoDto> responseWrapper = residentServiceImpl.getUserinfo("123");
ResponseWrapper<UserInfoDto> responseWrapper = residentServiceImpl.getUserinfo("123", 0);
assertEquals(responseWrapper.getResponse().getFullName(), responseWrapper.getResponse().getFullName());
}

Expand All @@ -224,7 +224,7 @@ public void testDownloadServiceHistory() throws ResidentServiceCheckedException,
.thenReturn(responseWrapper1);
Mockito.when(templateManager.merge(any(), any())).thenReturn(new ByteArrayInputStream("pdf".getBytes()));
Mockito.when(utility.signPdf(any(), any())).thenReturn("pdf".getBytes(StandardCharsets.UTF_8));
byte[] pdfDocument = residentServiceImpl.downLoadServiceHistory(responseWrapper, "eng",
byte[] pdfDocument = residentServiceImpl.downLoadServiceHistory(responseWrapper, "eng",
LocalDateTime.now(), LocalDate.now(), LocalDate.now(),
String.valueOf(RequestType.DOWNLOAD_PERSONALIZED_CARD), "SUCCESS");
assertNotNull(pdfDocument);
Expand Down

0 comments on commit a83e4ea

Please sign in to comment.