Migrated Queries - 4,5 and 70

This commit is contained in:
2026-08-01 23:10:28 +05:30
parent 7e4fb66fc6
commit 452e6189e4
24 changed files with 372 additions and 146 deletions

View File

@@ -13,7 +13,7 @@ public class CasePunching {
private Map<String, String> optionsl;
private Map<String, List<Option>> options;
private List<String> visibleSections;
private Integer portfolioId;
private Short portfolioId;
private Integer formMode;
private String userId;
private String dynamicFields;

View File

@@ -0,0 +1,29 @@
package lib.models;
import java.io.Serial;
import java.io.Serializable;
import lombok.Builder;
import lombok.Value;
/** Immutable, type-safe representation of an authenticated Cygnus user. */
@Value
@Builder
public class UserSession implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
Short companyId;
String companyName;
String companyCode;
Short branchId;
String branchName;
String branchCode;
String branchLocation;
Short userId;
String username;
String userDisplayName;
Short userGroupId;
String userGroupName;
String loginTime;
String menuHtml;
}

Binary file not shown.

View File

@@ -2,3 +2,4 @@
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/CasePunching.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/ErrorDetails.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/Option.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/UserSession.java

View File

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" version="3.0.2" name="lib.models.ErrorDetailsTest" time="0.024" tests="2" errors="0" skipped="0" failures="0">
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" version="3.0.2" name="lib.models.ErrorDetailsTest" time="0.028" tests="2" errors="0" skipped="0" failures="0">
<properties>
<property name="java.specification.version" value="25"/>
<property name="java.specification.version" value="21"/>
<property name="sun.jnu.encoding" value="UTF-8"/>
<property name="java.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/test-classes:/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/classes:/Users/maddy/.m2/repository/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
<property name="java.vm.vendor" value="Homebrew"/>
<property name="java.vm.vendor" value="Microsoft"/>
<property name="sun.arch.data.model" value="64"/>
<property name="java.vendor.url" value="https://github.com/Homebrew/homebrew-core/issues"/>
<property name="java.vendor.url" value="https://www.microsoft.com"/>
<property name="os.name" value="Mac OS X"/>
<property name="java.vm.specification.version" value="25"/>
<property name="java.vm.specification.version" value="21"/>
<property name="sun.java.launcher" value="SUN_STANDARD"/>
<property name="user.country" value="US"/>
<property name="sun.boot.library.path" value="/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib"/>
<property name="sun.java.command" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801204253825_3.jar /Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire 2026-08-01T20-42-53_787-jvmRun1 surefire-20260801204253825_1tmp surefire_0-20260801204253825_2tmp"/>
<property name="sun.boot.library.path" value="/Users/maddy/Library/Java/JavaVirtualMachines/ms-21.0.8/Contents/Home/lib"/>
<property name="sun.java.command" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801225315947_3.jar /Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire 2026-08-01T22-53-15_920-jvmRun1 surefire-20260801225315947_1tmp surefire_0-20260801225315947_2tmp"/>
<property name="http.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="jdk.debug" value="release"/>
<property name="surefire.test.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/test-classes:/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/classes:/Users/maddy/.m2/repository/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
@@ -20,8 +20,8 @@
<property name="user.home" value="/Users/maddy"/>
<property name="user.language" value="en"/>
<property name="java.specification.vendor" value="Oracle Corporation"/>
<property name="java.version.date" value="2026-01-20"/>
<property name="java.home" value="/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home"/>
<property name="java.version.date" value="2025-07-15"/>
<property name="java.home" value="/Users/maddy/Library/Java/JavaVirtualMachines/ms-21.0.8/Contents/Home"/>
<property name="file.separator" value="/"/>
<property name="basedir" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib"/>
<property name="java.vm.compressedOopsMode" value="Zero based"/>
@@ -29,10 +29,10 @@
<property name="java.vm.specification.vendor" value="Oracle Corporation"/>
<property name="java.specification.name" value="Java Platform API Specification"/>
<property name="apple.awt.application.name" value="ForkedBooter"/>
<property name="surefire.real.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801204253825_3.jar"/>
<property name="surefire.real.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801225315947_3.jar"/>
<property name="sun.management.compiler" value="HotSpot 64-Bit Tiered Compilers"/>
<property name="ftp.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.runtime.version" value="25.0.2"/>
<property name="java.runtime.version" value="21.0.8+9-LTS"/>
<property name="user.name" value="maddy"/>
<property name="stdout.encoding" value="UTF-8"/>
<property name="path.separator" value=":"/>
@@ -40,11 +40,11 @@
<property name="java.runtime.name" value="OpenJDK Runtime Environment"/>
<property name="file.encoding" value="UTF-8"/>
<property name="java.vm.name" value="OpenJDK 64-Bit Server VM"/>
<property name="java.vendor.version" value="Homebrew"/>
<property name="java.vendor.version" value="Microsoft-11933201"/>
<property name="localRepository" value="/Users/maddy/.m2/repository"/>
<property name="java.vendor.url.bug" value="https://github.com/Homebrew/homebrew-core/issues"/>
<property name="java.vendor.url.bug" value="https://github.com/microsoft/openjdk/issues"/>
<property name="java.io.tmpdir" value="/var/folders/1l/36214rdn79755j30lcnmgsqh0000gn/T/"/>
<property name="java.version" value="25.0.2"/>
<property name="java.version" value="21.0.8"/>
<property name="user.dir" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib"/>
<property name="os.arch" value="aarch64"/>
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
@@ -52,13 +52,12 @@
<property name="java.library.path" value="/Users/maddy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:."/>
<property name="java.vm.info" value="mixed mode, sharing"/>
<property name="stderr.encoding" value="UTF-8"/>
<property name="java.vendor" value="Homebrew"/>
<property name="java.vm.version" value="25.0.2"/>
<property name="stdin.encoding" value="UTF-8"/>
<property name="java.vendor" value="Microsoft"/>
<property name="java.vm.version" value="21.0.8+9-LTS"/>
<property name="sun.io.unicode.encoding" value="UnicodeBig"/>
<property name="socksNonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.class.version" value="69.0"/>
<property name="java.class.version" value="65.0"/>
</properties>
<testcase name="mapsHttpStatusToCanonicalError" classname="lib.models.ErrorDetailsTest" time="0.008"/>
<testcase name="createsAReusableErrorContract" classname="lib.models.ErrorDetailsTest" time="0.007"/>
<testcase name="createsAReusableErrorContract" classname="lib.models.ErrorDetailsTest" time="0.012"/>
</testsuite>

View File

@@ -1,4 +1,4 @@
-------------------------------------------------------------------------------
Test set: lib.models.ErrorDetailsTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 s -- in lib.models.ErrorDetailsTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.028 s -- in lib.models.ErrorDetailsTest

View File

@@ -53,7 +53,7 @@
<c:if test="${pageFormMode!=2}">
<!-- Dynamic Menu -->
<nav class="matrix-shell__navigation" aria-label="Primary navigation">
<c:out value="${Sessvals.menuHtml}" escapeXml="false" />
<c:out value="${sessionScope.userSession.menuHtml}" escapeXml="false" />
</nav>
<!-- -->
</c:if>
@@ -133,9 +133,9 @@
<option value="-1" selected >SELECT</option>
<c:choose>
<c:when test="${not empty model}">
<c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'branch.')}">
<option value="${fn:substringAfter(option.key, 'branch.')}"><c:out value="${option.value}" /></option>
</c:if></c:forEach>
<c:forEach items="${model.options['branch.']}" var="option">
<option value="${option.value}"><c:out value="${option.label}" /></option>
</c:forEach>
</c:when>
<c:otherwise><c:forEach items="${branchlist}" var="brnch"><option value="${brnch[0]}"><c:out value="${brnch[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
@@ -152,7 +152,7 @@
<select id="product" name="product" style="width:150px" onblur="validate(this,'t','');">
<option value="-1" selected >SELECT</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'product.')}"><option value="${fn:substringAfter(option.key, 'product.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['product.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${prodlist}" var="prod"><option value="${prod[0]}"><c:out value="${prod[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -184,7 +184,7 @@
<select id="apptype" name="apptype" style="width:153px" onchange="ToggleChks((this.value.toUpperCase()!='APPLICANT' && this.value.toUpperCase()!='-1'),'sradd!C0L!soadd!C0L!spadd!C0L!','!C0L!')" onblur="validate(this,'t','');">
<option value="-1" selected>SELECT</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'applicationType.')}"><option value="${fn:substringAfter(option.key, 'applicationType.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['applicationType.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${typelist}" var="type"><option value="${type[0]}"><c:out value="${type[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -200,7 +200,7 @@
<select id="category" name="category" style="width:133px">
<option value="-1" selected>SELECT</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'category.')}"><option value="${fn:substringAfter(option.key, 'category.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['category.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${catlist}" var="cat"><option value="${cat[0]}"><c:out value="${cat[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -311,7 +311,7 @@
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['city.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -429,7 +429,7 @@
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['city.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -556,7 +556,7 @@
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:when test="${not empty model}"><c:forEach items="${model.options['city.']}" var="option"><option value="${option.value}"><c:out value="${option.label}" /></option></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
@@ -689,8 +689,8 @@
</c:if>
<input type="hidden" id="uuid" name="uuid" value="${pageDocumentCaseId}" />
<input type="hidden" id="case_id" name="case_id" value="" />
<input type="hidden" id="company_id" name="company_id" value="${Sessvals.getCompanyID()}" />
<input type="hidden" id="branch_id" name="branch_id" value="${Sessvals.getBranchID()}" />
<input type="hidden" id="company_id" name="company_id" value="${sessionScope.userSession.companyId}" />
<input type="hidden" id="branch_id" name="branch_id" value="${sessionScope.userSession.branchId}" />
<input type="hidden" id="status" name="status" value="" />
<!-- -->
<input type="hidden" id="invalidfields" value="0" />

View File

@@ -6,8 +6,8 @@
<span>Cygnus 1.0</span>
</div>
<div class="matrix-shell__context" aria-label="Current session">
<span class="matrix-shell__company"><c:out value="${Sessvals.getCompanyCode()}" /> (<c:out value="${Sessvals.getBranchLocation()}" />)</span>
<span class="matrix-shell__user"><c:out value="${Sessvals.getUserDisplayName()}" /> (<c:out value="${Sessvals.getUserGroupName()}" />)</span>
<span class="matrix-shell__company"><c:out value="${sessionScope.userSession.companyCode}" /> (<c:out value="${sessionScope.userSession.branchLocation}" />)</span>
<span class="matrix-shell__user"><c:out value="${sessionScope.userSession.userDisplayName}" /> (<c:out value="${sessionScope.userSession.userGroupName}" />)</span>
</div>
</div>
</header>

View File

@@ -5,7 +5,7 @@ import com.cygnus.client.CloudIdentityClient;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClientResponseException;
@@ -28,7 +28,7 @@ public class CloudAuthenticationGateway {
this.sessionMapper = sessionMapper;
}
public Session authenticate(String loginId, String password) {
public UserSession authenticate(String loginId, String password) {
try {
Duration blockingTimeout = properties.requestTimeout().plusSeconds(1);
return sessionMapper.map(client.authenticate(loginId, password).block(blockingTimeout));

View File

@@ -5,6 +5,7 @@ import com.cygnus.client.model.CloudMenuItem;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import lib.models.UserSession;
import matrix.nimble.model.Session;
import org.springframework.stereotype.Component;
@@ -20,22 +21,42 @@ public class CloudSessionMapper {
this.menuRenderer = menuRenderer;
}
public Session map(CloudIdentitySession source) {
public UserSession map(CloudIdentitySession source) {
return UserSession.builder()
.userId(source.userId())
.username(source.loginId())
.userDisplayName(source.displayName())
.userGroupId(source.groupId())
.userGroupName(source.groupName())
.branchId(source.branchId())
.branchName(source.branchName())
.branchCode(source.branchCode())
.branchLocation(source.branchLocation())
.companyId(source.companyId())
.companyName(source.companyName())
.companyCode(source.companyCode())
.loginTime(LEGACY_LOGIN_TIME.format(source.loginTime()))
.menuHtml(menuRenderer.render(toLegacyMenu(source.menu())))
.build();
}
/** Temporary projection used by controllers that have not migrated to UserSession yet. */
public Session toLegacy(UserSession source) {
Session target = new Session();
target.setUserID(Short.toString(source.userId()));
target.setUsername(source.loginId());
target.setUserDisplayName(source.displayName());
target.setUserGroupID(Short.toString(source.groupId()));
target.setUserGroupName(source.groupName());
target.setBranchID(Short.toString(source.branchId()));
target.setBranchName(source.branchName());
target.setBranchCode(source.branchCode());
target.setBranchLocation(source.branchLocation());
target.setCompanyID(Short.toString(source.companyId()));
target.setCompanyName(source.companyName());
target.setCompanyCode(source.companyCode());
target.setLoginTime(LEGACY_LOGIN_TIME.format(source.loginTime()));
target.setMenuHtml(menuRenderer.render(toLegacyMenu(source.menu())));
target.setUserID(Short.toString(source.getUserId()));
target.setUsername(source.getUsername());
target.setUserDisplayName(source.getUserDisplayName());
target.setUserGroupID(Short.toString(source.getUserGroupId()));
target.setUserGroupName(source.getUserGroupName());
target.setBranchID(Short.toString(source.getBranchId()));
target.setBranchName(source.getBranchName());
target.setBranchCode(source.getBranchCode());
target.setBranchLocation(source.getBranchLocation());
target.setCompanyID(Short.toString(source.getCompanyId()));
target.setCompanyName(source.getCompanyName());
target.setCompanyCode(source.getCompanyCode());
target.setLoginTime(source.getLoginTime());
target.setMenuHtml(source.getMenuHtml());
return target;
}

View File

@@ -4,7 +4,7 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lib.constants.ApplicationError;
import lib.models.ErrorDetails;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import matrix.services.commons.CommonErrorService;
import matrix.services.commons.CommonService;
import org.springframework.ui.ModelMap;
@@ -26,14 +26,14 @@ public abstract class AbstractAuthenticatedController {
ModelMap model,
HttpSession httpSession,
HttpServletResponse response) {
Session session = commonService.getSession(httpSession);
UserSession session = commonService.getUserSession(httpSession);
if (session == null) {
return denied(model, response, httpSession, ApplicationError.SESSION_REQUIRED);
}
if (!commonService.hasPageAccess(session, pageRoute)) {
return denied(model, response, httpSession, ApplicationError.ACCESS_DENIED);
}
model.addAttribute(CommonService.SESSION_ATTRIBUTE, session);
model.addAttribute(CommonService.USER_SESSION_ATTRIBUTE, session);
return PageAuthorization.granted(session);
}
@@ -47,8 +47,8 @@ public abstract class AbstractAuthenticatedController {
return PageAuthorization.denied(viewName);
}
protected record PageAuthorization(Session session, String viewName) {
private static PageAuthorization granted(Session session) {
protected record PageAuthorization(UserSession session, String viewName) {
private static PageAuthorization granted(UserSession session) {
return new PageAuthorization(session, null);
}

View File

@@ -2,9 +2,11 @@ package matrix.nimble.controller;
import matrix.nimble.cloud.identity.CloudAuthenticationException;
import matrix.nimble.cloud.identity.CloudAuthenticationGateway;
import matrix.nimble.cloud.identity.CloudSessionMapper;
import matrix.nimble.model.Login;
import matrix.nimble.model.Session;
import matrix.services.commons.CommonService;
import lib.models.UserSession;
//servlet libraries
import jakarta.servlet.http.HttpServletRequest;
@@ -23,12 +25,15 @@ import org.springframework.web.bind.annotation.SessionAttributes;
public class SessionController {
private final CloudAuthenticationGateway cloudAuthenticationGateway;
private final CommonService commonService;
private final CloudSessionMapper cloudSessionMapper;
public SessionController(
CloudAuthenticationGateway cloudAuthenticationGateway,
CommonService commonService) {
CommonService commonService,
CloudSessionMapper cloudSessionMapper) {
this.cloudAuthenticationGateway = cloudAuthenticationGateway;
this.commonService = commonService;
this.cloudSessionMapper = cloudSessionMapper;
}
@RequestMapping(value="login", method={RequestMethod.GET, RequestMethod.POST})
public String LoginPage(ModelMap model)
@@ -53,10 +58,12 @@ public class SessionController {
HttpSession httpSession)
{
try {
Session cloudSession = cloudAuthenticationGateway.authenticate(
UserSession userSession = cloudAuthenticationGateway.authenticate(
login.getLoginid(), login.getPassword());
Session cloudSession = cloudSessionMapper.toLegacy(userSession);
model.remove("login");
model.addAttribute("Sessvals", cloudSession);
commonService.storeUserSession(httpSession, userSession);
commonService.storeSession(httpSession, cloudSession);
return "home";
} catch (CloudAuthenticationException exception) {

View File

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpServletResponse;
import matrix.nimble.controller.AbstractAuthenticatedController;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import matrix.services.commons.CommonService;
import matrix.services.edp.PunchingService;
import matrix.services.commons.CommonErrorService;
@@ -38,11 +38,10 @@ public class PunchingController extends AbstractAuthenticatedController {
if (!authorization.isGranted()) {
return authorization.viewName();
}
Session Sessvals = authorization.session();
UserSession userSession = authorization.session();
model.addAttribute("model", punchingService.init(
Sessvals.getBranchID(), Sessvals.getUserID(), null, "xxx"));
model.addAttribute("Sessvals", Sessvals);
userSession.getBranchId(), userSession.getUserId(), null, null));
return "edp/punching/initcase";
}
@@ -58,16 +57,15 @@ public class PunchingController extends AbstractAuthenticatedController {
return authorization.viewName();
}
Session session = authorization.session();
UserSession session = authorization.session();
model.addAttribute("model", punchingService.addCase(
submitted, session.getBranchID(), session.getUserID()));
model.addAttribute("Sessvals", session);
submitted, session.getBranchId(), session.getCompanyId(), session.getUserId()));
return "edp/punching/initcase";
}
@RequestMapping(value = "caseedit", method = RequestMethod.POST)
public String editCase(
@RequestParam("PortfolioId") Integer portfolioId,
@RequestParam("PortfolioId") Short portfolioId,
@RequestParam("uuid") String documentCaseId,
ModelMap model,
HttpSession httpSession,
@@ -78,10 +76,10 @@ public class PunchingController extends AbstractAuthenticatedController {
return authorization.viewName();
}
Session session = authorization.session();
UserSession session = authorization.session();
model.addAttribute("model", punchingService.editCase(
portfolioId, documentCaseId, session.getBranchID(), session.getUserID()));
model.addAttribute("Sessvals", session);
portfolioId, documentCaseId, session.getBranchId(),
session.getCompanyId(), session.getUserId()));
return "edp/punching/initcase";
}
}

View File

@@ -3,6 +3,7 @@ package matrix.services.commons;
import com.cygnus.db.CygnusDbExecutor;
import jakarta.servlet.http.HttpSession;
import lib.models.Option;
import lib.models.UserSession;
import java.util.List;
import java.util.Objects;
@@ -15,6 +16,7 @@ import org.springframework.stereotype.Service;
@Service
public class CommonService {
public static final String SESSION_ATTRIBUTE = "Sessvals";
public static final String USER_SESSION_ATTRIBUTE = "userSession";
private static final Pattern MENU_COMMAND = Pattern.compile(
"SubmitMenuCommand\\(\\s*'([^']+)'\\s*,",
@@ -46,6 +48,20 @@ public class CommonService {
httpSession.setAttribute(SESSION_ATTRIBUTE, Objects.requireNonNull(session, "session"));
}
public UserSession getUserSession(HttpSession httpSession) {
if (httpSession == null) {
return null;
}
Object value = httpSession.getAttribute(USER_SESSION_ATTRIBUTE);
return value instanceof UserSession session ? session : null;
}
public void storeUserSession(HttpSession httpSession, UserSession session) {
Objects.requireNonNull(httpSession, "httpSession");
httpSession.setAttribute(
USER_SESSION_ATTRIBUTE, Objects.requireNonNull(session, "session"));
}
public boolean hasPageAccess(HttpSession httpSession, String pageRoute) {
return hasPageAccess(getSession(httpSession), pageRoute);
}
@@ -69,6 +85,29 @@ public class CommonService {
return false;
}
public boolean hasPageAccess(UserSession session, String pageRoute) {
return session != null && hasPageAccess(session.getMenuHtml(), pageRoute);
}
private boolean hasPageAccess(String menuHtml, String pageRoute) {
if (menuHtml == null) {
return false;
}
String requiredRoute = normalizeRoute(pageRoute);
if (requiredRoute.isEmpty()) {
return false;
}
Matcher matcher = MENU_COMMAND.matcher(menuHtml);
while (matcher.find()) {
if (normalizeRoute(matcher.group(1)).equals(requiredRoute)) {
return true;
}
}
return false;
}
private String normalizeRoute(String route) {
if (route == null) {
return "";

View File

@@ -1,5 +1,6 @@
package matrix.services.edp;
import com.cygnus.db.SqlArrayParameter;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -13,7 +14,6 @@ import lib.models.ErrorDetails;
import lib.models.Option;
import lib.constants.ApplicationError;
import matrix.nimble.edp.punching.model.PunchingHandler;
import matrix.nimble.utilities.CommonFunctions;
import matrix.nimble.utilities.GlobalClass;
import matrix.services.commons.CommonService;
@@ -25,8 +25,15 @@ public class PunchingService {
private static final String PRODUCT = "product.";
private static final String CITY = "city.";
private static final String APPLICATION_TYPE = "applicationType.";
private static final String DATABASE_APPLICATION_TYPE = "apptype.";
private static final String CATEGORY = "category.";
private static final Short ACTIVE = Short.valueOf((short) 1);
private static final SqlArrayParameter GENERAL_OPTION_TYPES =
SqlArrayParameter.text("CITY", "APPTYPE");
private static final SqlArrayParameter PORTFOLIO_OPTION_TYPES =
SqlArrayParameter.text("CATEGORY", "PRODUCT");
private final CommonService commonService;
public PunchingService(CommonService commonService) {
@@ -34,8 +41,8 @@ public class PunchingService {
}
public CasePunching init(
String branchId,
String userId,
Short branchId,
Short userId,
String verificationCaseId,
String documentCaseId) {
PunchingHandler punchingHandler = new PunchingHandler();
@@ -43,16 +50,15 @@ public class PunchingService {
CasePunching casePunching = new CasePunching();
Map<String, List<Option>> options = new LinkedHashMap<>();
Short databaseBranchId = Short.valueOf(branchId.trim());
List<Option> portfolios = commonService.getOptions(
3, new Object[] { databaseBranchId, Short.valueOf((short) 1) });
3, new Object[] { branchId, ACTIVE });
addOptions(options, PORTFOLIO, portfolios);
casePunching.setOptions(Collections.unmodifiableMap(options));
casePunching.setOptions(freezeOptions(options));
casePunching.setVisibleSections(List.of());
casePunching.setPortfolioId(-1);
casePunching.setPortfolioId((short) -1);
casePunching.setFormMode(0);
casePunching.setUserId(userId);
casePunching.setUserId(Short.toString(userId));
casePunching.setDynamicFields("");
casePunching.setDynamicHtml("");
casePunching.setVerificationCaseId(verificationCaseId);
@@ -62,17 +68,18 @@ public class PunchingService {
public CasePunching addCase(
CasePunching submitted,
String branchIds,
String userId) {
Short branchId,
Short companyId,
Short userId) {
CasePunching result = new CasePunching();
result.setPortfolioId(submitted.getPortfolioId());
result.setFormMode(1);
result.setUserId(userId);
result.setUserId(Short.toString(userId));
result.setVerificationCaseId(submitted.getVerificationCaseId());
result.setDocumentCaseId("xxx");
if (submitted.getPortfolioId() == null || submitted.getPortfolioId() <= 0) {
result.setOptions(Collections.emptyMap());
result.setOptions(Map.of());
result.setVisibleSections(List.of());
result.setDynamicFields("");
result.setDynamicHtml("");
@@ -80,11 +87,10 @@ public class PunchingService {
return result;
}
String portfolioId = submitted.getPortfolioId().toString();
Short portfolioId = submitted.getPortfolioId();
PunchingHandler punchingHandler = new PunchingHandler();
CommonFunctions commonFunctions = new CommonFunctions();
punchingHandler.setErrCode("1001");
punchingHandler.setPortfolioid(portfolioId);
punchingHandler.setPortfolioid(Short.toString(portfolioId));
punchingHandler.setProcessFlag(true);
punchingHandler.DynamicHtml("10");
@@ -101,65 +107,40 @@ public class PunchingService {
result.setErrorDetails(new ErrorDetails(ApplicationError.INTERNAL_SERVER_ERROR));
}
Map<String, String> options = new LinkedHashMap<>();
punchingHandler.GetDDValues(3, branchIds.split(GlobalClass.ColDelim));
addOptions(options, PORTFOLIO, punchingHandler.getOptionvals());
punchingHandler.GetDDValues(4, new String[] { portfolioId });
addOptions(options, BRANCH, punchingHandler.getOptionvals());
punchingHandler.GetDDValues(5,
(branchIds + GlobalClass.ColDelim
+ "op.description='CITY' or op.description='APPTYPE'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
String[][] generalOptions = punchingHandler.getOptionvals();
if (generalOptions != null) {
addOptions(options, CITY,
commonFunctions.GetSubArray("CITY", 2, generalOptions));
addOptions(options, APPLICATION_TYPE,
commonFunctions.GetSubArray("APPTYPE", 2, generalOptions));
}
punchingHandler.GetDDValues(70,
(portfolioId + GlobalClass.ColDelim
+ "op.description='CATEGORY' or op.description='PRODUCT'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
String[][] portfolioOptions = punchingHandler.getOptionvals();
if (portfolioOptions != null) {
addOptions(options, PRODUCT,
commonFunctions.GetSubArray("PRODUCT", 2, portfolioOptions));
addOptions(options, CATEGORY,
commonFunctions.GetSubArray("CATEGORY", 2, portfolioOptions));
}
result.setOptionsl(Collections.unmodifiableMap(options));
result.setOptions(loadOptions(branchId, companyId, portfolioId));
return result;
}
Map<String, List<Option>> loadOptions(
Short branchId, Short companyId, Short portfolioId) {
Map<String, List<Option>> options = new LinkedHashMap<>();
addOptions(options, PORTFOLIO, commonService.getOptions(
3, new Object[] {branchId, ACTIVE}));
addOptions(options, BRANCH, commonService.getOptions(
4, new Object[] {portfolioId, ACTIVE}));
addGroupedOptions(options, commonService.getOptions(
5, new Object[] {companyId, GENERAL_OPTION_TYPES}));
addGroupedOptions(options, commonService.getOptions(
70, new Object[] {portfolioId, PORTFOLIO_OPTION_TYPES}));
return freezeOptions(options);
}
public CasePunching editCase(
Integer portfolioId,
Short portfolioId,
String documentCaseId,
String branchIds,
String userId) {
Short branchId,
Short companyId,
Short userId) {
CasePunching submitted = new CasePunching();
submitted.setPortfolioId(portfolioId);
submitted.setDocumentCaseId(documentCaseId);
CasePunching result = addCase(submitted, branchIds, userId);
CasePunching result = addCase(submitted, branchId, companyId, userId);
result.setFormMode(2);
result.setDocumentCaseId(documentCaseId);
return result;
}
private void addOptions(Map<String, String> options, String prefix, String[][] values) {
if (values == null) {
return;
}
for (String[] value : values) {
if (value != null && value.length >= 2 && value[0] != null) {
options.put(prefix + value[0], value[1] == null ? "" : value[1]);
}
}
}
private void addOptions(Map<String, List<Option>> options, String prefix, List<Option> values) {
if (values == null || values.isEmpty()) {
return;
@@ -178,6 +159,45 @@ public class PunchingService {
}
}
private void addGroupedOptions(Map<String, List<Option>> options, List<Option> values) {
if (values == null || values.isEmpty()) {
return;
}
for (Option value : values) {
if (value == null || value.getValue() == null) {
continue;
}
String group = normalizeGroup(value.getGroup());
if (group == null) {
continue;
}
options.computeIfAbsent(group, ignored -> new java.util.ArrayList<>()).add(value);
}
}
private String normalizeGroup(String group) {
if (group == null || group.isBlank()) {
return null;
}
String normalized = group.trim();
if (DATABASE_APPLICATION_TYPE.equalsIgnoreCase(normalized)) {
return APPLICATION_TYPE;
}
if (CITY.equalsIgnoreCase(normalized)) return CITY;
if (CATEGORY.equalsIgnoreCase(normalized)) return CATEGORY;
if (PRODUCT.equalsIgnoreCase(normalized)) return PRODUCT;
return null;
}
private Map<String, List<Option>> freezeOptions(Map<String, List<Option>> options) {
if (options.isEmpty()) {
return Map.of();
}
Map<String, List<Option>> immutable = new LinkedHashMap<>(options.size());
options.forEach((group, values) -> immutable.put(group, List.copyOf(values)));
return Collections.unmodifiableMap(immutable);
}
private List<String> toVisibleSections(String sections) {
if (sections == null || sections.isBlank()) {
return List.of();

View File

@@ -310,7 +310,8 @@ class RoleVisibleBatchMigrationTest {
Path.of("build/WebContent/WEB-INF/app/operation/tools").resolve(page));
assertTrue(content.contains("matrix-shell matrix-tool-workspace"), page);
assertTrue(content.contains("tool-workspace-v3.css"), page);
assertTrue(content.contains("Sessvals.menuHtml"), page);
assertTrue(content.contains("Sessvals.menuHtml")
|| content.contains("sessionScope.userSession.menuHtml"), page);
assertFalse(content.contains("z-index:-1"), page);
assertFalse(content.contains("centerDivBoth(\"formcontainer\""), page);
}
@@ -505,7 +506,8 @@ class RoleVisibleBatchMigrationTest {
String content = Files.readString(punching.resolve(page));
assertTrue(content.contains("matrix-tool-workspace matrix-punch-workspace"), page);
assertTrue(content.contains("punch-workspace-v3.css"), page);
assertTrue(content.contains("Sessvals.menuHtml"), page);
assertTrue(content.contains("Sessvals.menuHtml")
|| content.contains("sessionScope.userSession.menuHtml"), page);
assertFalse(content.contains("z-index:-1"), page);
assertFalse(content.contains("centerDivBoth(\"formcontainer\""), page);
}

View File

@@ -8,13 +8,13 @@ import com.cygnus.client.model.CloudIdentitySession;
import com.cygnus.client.model.CloudMenuItem;
import java.time.Instant;
import java.util.List;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import org.junit.jupiter.api.Test;
class CloudSessionMapperTest {
@Test
void mapsCloudIdentityAndStructuredMenuToLegacySession() {
void mapsCloudIdentityAndStructuredMenuToTypedSession() {
CloudIdentitySession identity = new CloudIdentitySession(
(short) 17,
"maddy",
@@ -37,9 +37,9 @@ class CloudSessionMapperTest {
(short) 11, "Scanning", "scanreporting", (short) 10,
(short) 1, "111", "_parent", "")));
Session session = new CloudSessionMapper(new SessionMenuRenderer()).map(identity);
UserSession session = new CloudSessionMapper(new SessionMenuRenderer()).map(identity);
assertEquals("17", session.getUserID());
assertEquals((short) 17, session.getUserId());
assertEquals("maddy", session.getUsername());
assertEquals("Operations", session.getUserGroupName());
assertEquals("Mayapuri", session.getBranchName());
@@ -49,4 +49,19 @@ class CloudSessionMapperTest {
assertTrue(session.getMenuHtml().contains("Scanning"));
assertTrue(session.getMenuHtml().contains("scanreporting"));
}
@Test
void createsLegacyProjectionWithoutLosingIdentityValues() {
CloudIdentitySession identity = new CloudIdentitySession(
(short) 17, "maddy", "Maddy", (short) 5, "Operations",
(short) 9, "Mayapuri", "MYP", "Delhi", (short) 2,
"Matrix", "MCR", Instant.parse("2026-07-23T06:30:00Z"), List.of());
CloudSessionMapper mapper = new CloudSessionMapper(new SessionMenuRenderer());
matrix.nimble.model.Session legacy = mapper.toLegacy(mapper.map(identity));
assertEquals("17", legacy.getUserID());
assertEquals("9", legacy.getBranchID());
assertEquals("2", legacy.getCompanyID());
}
}

View File

@@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import matrix.services.commons.CommonErrorService;
import matrix.services.commons.CommonService;
import org.junit.jupiter.api.Test;
@@ -20,8 +20,8 @@ class AbstractAuthenticatedControllerTest {
@Test
void grantsAConfiguredMenuRoute() {
MockHttpSession httpSession = new MockHttpSession();
Session session = sessionWithRoute("initview");
commonService.storeSession(httpSession, session);
UserSession session = sessionWithRoute("initview");
commonService.storeUserSession(httpSession, session);
AbstractAuthenticatedController.PageAuthorization result = controller.authorize(
"initview", new ModelMap(), httpSession, new MockHttpServletResponse());
@@ -39,7 +39,7 @@ class AbstractAuthenticatedControllerTest {
assertEquals(401, noSessionResponse.getStatus());
MockHttpSession httpSession = new MockHttpSession();
commonService.storeSession(httpSession, sessionWithRoute("dashboard"));
commonService.storeUserSession(httpSession, sessionWithRoute("dashboard"));
MockHttpServletResponse deniedResponse = new MockHttpServletResponse();
AbstractAuthenticatedController.PageAuthorization denied = controller.authorize(
"initview", new ModelMap(), httpSession, deniedResponse);
@@ -47,11 +47,11 @@ class AbstractAuthenticatedControllerTest {
assertEquals(403, deniedResponse.getStatus());
}
private Session sessionWithRoute(String route) {
Session session = new Session();
session.setMenuHtml("<a onclick=\"SubmitMenuCommand('" + route
+ "','_parent','MenuForm','')\">Page</a>");
return session;
private UserSession sessionWithRoute(String route) {
return UserSession.builder()
.menuHtml("<a onclick=\"SubmitMenuCommand('" + route
+ "','_parent','MenuForm','')\">Page</a>")
.build();
}
private static final class TestController extends AbstractAuthenticatedController {

View File

@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import matrix.nimble.model.Session;
import lib.models.UserSession;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpSession;
@@ -21,6 +22,17 @@ class CommonServiceTest {
assertSame(session, service.getSession(httpSession));
}
@Test
void storesAndReadsTypedUserSession() {
MockHttpSession httpSession = new MockHttpSession();
UserSession session = typedSessionWithMenu("initview");
service.storeUserSession(httpSession, session);
assertSame(session, service.getUserSession(httpSession));
assertTrue(service.hasPageAccess(session, "initview"));
}
@Test
void grantsOnlyAnExactMenuCommandRoute() {
Session session = sessionWithMenu("/matrix/ver/initview?source=menu");
@@ -42,4 +54,11 @@ class CommonServiceTest {
+ "','_parent','MenuForm','')\">Punching</a>");
return session;
}
private UserSession typedSessionWithMenu(String route) {
return UserSession.builder()
.menuHtml("<a onclick=\"SubmitMenuCommand('" + route
+ "','_parent','MenuForm','')\">Punching</a>")
.build();
}
}

View File

@@ -0,0 +1,76 @@
package matrix.services.edp;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.cygnus.db.SqlArrayParameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lib.models.Option;
import matrix.services.commons.CommonService;
import org.junit.jupiter.api.Test;
class PunchingServiceTest {
@Test
void loadsParameterizedOptionsAndNormalizesApplicationTypeGroup() {
RecordingCommonService commonService = new RecordingCommonService();
PunchingService service = new PunchingService(commonService);
Map<String, List<Option>> options = service.loadOptions((short) 5, (short) 1, (short) 38);
assertEquals(List.of(3, 4, 5, 70),
commonService.calls.stream().map(Call::queryId).toList());
assertArrayEquals(new Object[] {(short) 5, (short) 1},
commonService.calls.get(0).parameters());
assertArrayEquals(new Object[] {(short) 38, (short) 1},
commonService.calls.get(1).parameters());
assertEquals((short) 1, commonService.calls.get(2).parameters()[0]);
assertArrayEquals(new Object[] {"CITY", "APPTYPE"},
((SqlArrayParameter) commonService.calls.get(2).parameters()[1]).values());
assertEquals((short) 38, commonService.calls.get(3).parameters()[0]);
assertArrayEquals(new Object[] {"CATEGORY", "PRODUCT"},
((SqlArrayParameter) commonService.calls.get(3).parameters()[1]).values());
assertEquals("Portfolio", options.get("portfolio.").getFirst().getLabel());
assertEquals("Branch", options.get("branch.").getFirst().getLabel());
assertEquals("City", options.get("city.").getFirst().getLabel());
assertEquals("Applicant", options.get("applicationType.").getFirst().getLabel());
assertEquals("Category", options.get("category.").getFirst().getLabel());
assertEquals("Product", options.get("product.").getFirst().getLabel());
assertThrows(UnsupportedOperationException.class,
() -> options.put("other.", List.of()));
}
private static final class RecordingCommonService extends CommonService {
private final List<Call> calls = new ArrayList<>();
@Override
public List<Option> getOptions(Integer queryId, Object[] queryParams) {
calls.add(new Call(queryId, queryParams.clone()));
return switch (queryId) {
case 3 -> List.of(option(38, "Portfolio", "portfolio."));
case 4 -> List.of(option(7, "Branch", "branch."));
case 5 -> List.of(
option("DEL", "City", "city."),
option("APPLICANT", "Applicant", "apptype."));
case 70 -> List.of(
option("DIRECTOR", "Category", "category."),
option("AUTO", "Product", "product."));
default -> List.of();
};
}
}
private static Option option(Object value, String label, String group) {
Option option = new Option();
option.setValue(value);
option.setLabel(label);
option.setGroup(group);
return option;
}
private record Call(int queryId, Object[] parameters) {}
}