Fixed add case bug
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -71,7 +71,7 @@
|
||||
"CYGNUS_LOGIN_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/login-public.pem",
|
||||
"CYGNUS_PAYLOAD_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/case-save-private.pem",
|
||||
"CYGNUS_PAYLOAD_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/case-save-public.pem",
|
||||
"CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT10S",
|
||||
"CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT30S",
|
||||
"CYGNUS_TOKEN_REFRESH_SKEW": "PT30S"
|
||||
},
|
||||
"vmArgs": "-Dserver.port=8080 -Djava.awt.headless=true -Dmatrix.webapp=${workspaceFolder}/cygnus-onprem-app/build/WebContent -Dmatrix.classes=${workspaceFolder}/cygnus-onprem-app/target/classes",
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -18,6 +19,9 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
public class CloudIdentityClient {
|
||||
|
||||
private static final ParameterizedTypeReference<List<CloudDataItem>> CLOUD_DATA_LIST =
|
||||
new ParameterizedTypeReference<>() { };
|
||||
|
||||
private final WebClient webClient;
|
||||
private final MachineTokenProvider tokenProvider;
|
||||
private final LoginEnvelopeEncryptor encryptor;
|
||||
@@ -87,8 +91,7 @@ public class CloudIdentityClient {
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(new CloudDataRequest(scope, Map.copyOf(data)))
|
||||
.retrieve()
|
||||
.bodyToFlux(CloudDataItem.class)
|
||||
.collectList())
|
||||
.bodyToMono(CLOUD_DATA_LIST))
|
||||
.timeout(properties.requestTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
package com.cygnus.client.model;
|
||||
|
||||
public record CloudDataItem(Object value, String label, String group) {
|
||||
import java.util.Map;
|
||||
|
||||
public record CloudDataItem(Object value, String label, String group, Map<String, Object> data) {
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.cygnus.cloud.platform.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record ScopedDataItem(
|
||||
Object value,
|
||||
String label,
|
||||
String group) {
|
||||
String group,
|
||||
Map<String, Object> data) {
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.cygnus.cloud.platform.api.ScopedDataItem;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -46,6 +47,16 @@ public class ScopedDataRepository {
|
||||
AND o.description = ANY($5::text[])
|
||||
ORDER BY o.description, ov.val_description
|
||||
""";
|
||||
private static final String REPORT_DEFINITION = """
|
||||
SELECT r.report_key, r.report_title, r.hide_leading_zero_columns,
|
||||
c.column_key, c.column_label, c.display_order, c.alignment,
|
||||
c.column_width, c.is_visible, c.is_totalled
|
||||
FROM platform.report_definition r
|
||||
JOIN platform.report_column_definition c ON c.report_key = r.report_key
|
||||
WHERE r.report_key = $1
|
||||
AND r.enabled = true
|
||||
ORDER BY c.display_order
|
||||
""";
|
||||
|
||||
private final ReactiveDatabaseClient database;
|
||||
|
||||
@@ -66,6 +77,22 @@ public class ScopedDataRepository {
|
||||
descriptions.toArray(String[]::new)));
|
||||
}
|
||||
|
||||
public Flux<ScopedDataItem> reportDefinition(String reportKey) {
|
||||
return database.preparedQuery(REPORT_DEFINITION, Tuple.of(reportKey))
|
||||
.flatMapMany(result -> Flux.fromIterable(result))
|
||||
.map(row -> new ScopedDataItem(null, null, null, Map.of(
|
||||
"reportKey", row.getString("report_key"),
|
||||
"reportTitle", row.getString("report_title"),
|
||||
"hideLeadingZeroColumns", row.getBoolean("hide_leading_zero_columns"),
|
||||
"columnKey", row.getString("column_key"),
|
||||
"columnLabel", row.getString("column_label"),
|
||||
"displayOrder", row.getInteger("display_order"),
|
||||
"alignment", row.getString("alignment"),
|
||||
"columnWidth", row.getInteger("column_width"),
|
||||
"visible", row.getBoolean("is_visible"),
|
||||
"totalled", row.getBoolean("is_totalled"))));
|
||||
}
|
||||
|
||||
private Flux<ScopedDataItem> rows(String sql, Tuple parameters) {
|
||||
return database.preparedQuery(sql, parameters)
|
||||
.flatMapMany(result -> Flux.fromIterable(result))
|
||||
@@ -74,6 +101,6 @@ public class ScopedDataRepository {
|
||||
|
||||
private ScopedDataItem item(Row row) {
|
||||
return new ScopedDataItem(
|
||||
row.getValue("value"), row.getString("label"), row.getString("group_name"));
|
||||
row.getValue("value"), row.getString("label"), row.getString("group_name"), Map.of());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import reactor.core.publisher.Flux;
|
||||
public class ScopedDataService {
|
||||
public static final String COMPANY_OPTIONS = "company-options";
|
||||
public static final String PORTFOLIO_OPTIONS = "portfolio-options";
|
||||
public static final String REPORT_DEFINITION = "report-definition";
|
||||
|
||||
private final ScopedDataRepository repository;
|
||||
|
||||
@@ -23,9 +24,13 @@ public class ScopedDataService {
|
||||
|
||||
public Flux<ScopedDataItem> fetch(UUID tenantId, ScopedDataRequest request) {
|
||||
Map<String, Object> data = request.data();
|
||||
String scope = request.scope().trim().toLowerCase(Locale.ROOT);
|
||||
if (REPORT_DEFINITION.equals(scope)) {
|
||||
return repository.reportDefinition(textValue(data, "reportKey"));
|
||||
}
|
||||
short companyId = shortValue(data, "companyId");
|
||||
List<String> descriptions = descriptions(data);
|
||||
return switch (request.scope().trim().toLowerCase(Locale.ROOT)) {
|
||||
return switch (scope) {
|
||||
case COMPANY_OPTIONS -> repository.companyOptions(tenantId, companyId, descriptions);
|
||||
case PORTFOLIO_OPTIONS -> repository.portfolioOptions(
|
||||
tenantId, companyId, shortValue(data, "branchId"),
|
||||
@@ -34,6 +39,14 @@ public class ScopedDataService {
|
||||
};
|
||||
}
|
||||
|
||||
private String textValue(Map<String, Object> data, String name) {
|
||||
String value = String.valueOf(data.getOrDefault(name, "")).trim();
|
||||
if (value.isEmpty() || value.length() > 100 || !value.matches("[a-z0-9-]+")) {
|
||||
throw new ScopedDataException(name + " is invalid");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private UUID uuidValue(Map<String, Object> data, String name) {
|
||||
try {
|
||||
return UUID.fromString(String.valueOf(data.get(name)));
|
||||
|
||||
@@ -34,11 +34,5 @@ SELECT 'hourly-mis', 'hour'||hour, hour::text, hour+1, 'center', 55, true, true
|
||||
FROM generate_series(0,23) hour
|
||||
ON CONFLICT (report_key,column_key) DO NOTHING;
|
||||
|
||||
UPDATE platform.application_query
|
||||
SET query_text='select!C0L!SELECT r.report_title,r.hide_leading_zero_columns,c.column_key,c.column_label,c.display_order,c.alignment,c.column_width,c.is_visible,c.is_totalled FROM platform.report_definition r JOIN platform.report_column_definition c ON c.report_key=r.report_key WHERE r.report_key=? AND r.enabled=true ORDER BY c.display_order',
|
||||
enabled=true, updated_at=current_timestamp
|
||||
DELETE FROM platform.application_query
|
||||
WHERE query_key = 'report-definition';
|
||||
|
||||
INSERT INTO platform.application_query(query_key, query_text, enabled)
|
||||
SELECT 'report-definition', 'select!C0L!SELECT r.report_title,r.hide_leading_zero_columns,c.column_key,c.column_label,c.display_order,c.alignment,c.column_width,c.is_visible,c.is_totalled FROM platform.report_definition r JOIN platform.report_column_definition c ON c.report_key=r.report_key WHERE r.report_key=? AND r.enabled=true ORDER BY c.display_order', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM platform.application_query WHERE query_key='report-definition');
|
||||
@@ -41,11 +41,13 @@
|
||||
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/punch-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/add-cases-v4.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
|
||||
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
|
||||
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/matrix-frame-dialog.js?v=2" defer></script>
|
||||
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=1" defer></script>
|
||||
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
<link href="/matrix/css/matrix-v2.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/mis-filter-v4.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/mis-filter-v4.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
|
||||
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
|
||||
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/matrix-frame-dialog.js?v=2" defer></script>
|
||||
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=2" defer></script>
|
||||
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
.matrix-date-control {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.matrix-date-control > .matrix-date-input {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
padding-right: 38px !important;
|
||||
}
|
||||
|
||||
.matrix-date-control__button {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
display: grid;
|
||||
width: 34px;
|
||||
padding: 0;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-left: 1px solid #c4d0da;
|
||||
border-radius: 0 3px 3px 0;
|
||||
background: #edf3f7;
|
||||
color: #285f8f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.matrix-date-control__button:hover,
|
||||
.matrix-date-control__button:focus-visible {
|
||||
background: #dfeaf3;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.matrix-date-control__picker {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel .matrix-date-field {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@@ -64,25 +64,43 @@
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel {
|
||||
padding: 20px 18px;
|
||||
height: auto !important;
|
||||
min-height: 0 !important;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #c9d4de;
|
||||
border-radius: 0 0 5px 5px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tbody,
|
||||
.matrix-mis-filter .FormPanel table tr,
|
||||
.matrix-mis-filter .FormPanel table tr td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr td {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: end;
|
||||
gap: 14px 18px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr:first-child td {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr:last-child td {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel .widget {
|
||||
display: flex;
|
||||
min-width: 230px;
|
||||
@@ -124,12 +142,15 @@
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel input.button {
|
||||
width: auto !important;
|
||||
min-width: 96px;
|
||||
min-height: 34px;
|
||||
padding: 6px 14px 6px 30px !important;
|
||||
border-color: #7f8b96;
|
||||
border-radius: 4px;
|
||||
background-color: #e5e8eb;
|
||||
float: none !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel br {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const pad = (value) => String(value).padStart(2, "0");
|
||||
|
||||
const parseDisplayValue = (value, mode) => {
|
||||
const pattern = mode === "datetime"
|
||||
? /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}):(\d{2})$/
|
||||
: /^(\d{2})\/(\d{2})\/(\d{4})$/;
|
||||
const match = value.trim().match(pattern);
|
||||
if (!match) return "";
|
||||
|
||||
const [, day, month, year, hour = "00", minute = "00", second = "00"] = match;
|
||||
const date = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
|
||||
if (date.getFullYear() !== Number(year) || date.getMonth() !== Number(month) - 1
|
||||
|| date.getDate() !== Number(day) || date.getHours() !== Number(hour)
|
||||
|| date.getMinutes() !== Number(minute) || date.getSeconds() !== Number(second)) return "";
|
||||
|
||||
const isoDate = `${year}-${month}-${day}`;
|
||||
return mode === "datetime" ? `${isoDate}T${hour}:${minute}:${second}` : isoDate;
|
||||
};
|
||||
|
||||
const formatPickerValue = (value, mode) => {
|
||||
if (!value) return "";
|
||||
const [datePart, timePart = "00:00:00"] = value.split("T");
|
||||
const [year, month, day] = datePart.split("-");
|
||||
if (!year || !month || !day) return "";
|
||||
if (mode === "date") return `${day}/${month}/${year}`;
|
||||
const [hour = "00", minute = "00", second = "00"] = timePart.split(":");
|
||||
return `${day}/${month}/${year} ${pad(hour)}:${pad(minute)}:${pad(second)}`;
|
||||
};
|
||||
|
||||
const initialise = (input) => {
|
||||
if (input.dataset.datePickerReady === "true") return;
|
||||
input.dataset.datePickerReady = "true";
|
||||
|
||||
const mode = input.dataset.dateMode === "date" ? "date" : "datetime";
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.className = "matrix-date-control";
|
||||
input.parentNode.insertBefore(wrapper, input);
|
||||
wrapper.appendChild(input);
|
||||
|
||||
const picker = document.createElement("input");
|
||||
picker.type = mode === "datetime" ? "datetime-local" : "date";
|
||||
picker.className = "matrix-date-control__picker";
|
||||
picker.tabIndex = -1;
|
||||
picker.setAttribute("aria-hidden", "true");
|
||||
if (mode === "datetime") picker.step = "1";
|
||||
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "matrix-date-control__button";
|
||||
button.setAttribute("aria-label", mode === "datetime" ? "Choose date and time" : "Choose date");
|
||||
button.innerHTML = "📅";
|
||||
|
||||
wrapper.append(picker, button);
|
||||
input.closest(".widget")?.classList.add("matrix-date-field");
|
||||
|
||||
const syncPicker = () => {
|
||||
const parsed = parseDisplayValue(input.value, mode);
|
||||
if (parsed) picker.value = parsed;
|
||||
};
|
||||
syncPicker();
|
||||
|
||||
picker.addEventListener("change", () => {
|
||||
input.value = formatPickerValue(picker.value, mode);
|
||||
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
input.focus();
|
||||
});
|
||||
input.addEventListener("change", syncPicker);
|
||||
button.addEventListener("click", () => {
|
||||
syncPicker();
|
||||
if (typeof picker.showPicker === "function") picker.showPicker();
|
||||
else picker.click();
|
||||
});
|
||||
};
|
||||
|
||||
const initialiseAll = (root = document) => root.querySelectorAll(".matrix-date-input").forEach(initialise);
|
||||
document.addEventListener("DOMContentLoaded", () => initialiseAll());
|
||||
window.CygnusDateTimeInputs = { initialise: initialiseAll };
|
||||
})();
|
||||
@@ -8,8 +8,10 @@ import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import matrix.nimble.mis.model.Filters;
|
||||
import matrix.nimble.mis.model.MISHandler;
|
||||
import matrix.nimble.cloud.identity.CloudSessionMapper;
|
||||
import matrix.nimble.model.Session;
|
||||
import matrix.nimble.utilities.GlobalClass;
|
||||
import matrix.services.commons.CommonService;
|
||||
import matrix.services.mis.HourlyMisService;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -27,9 +29,31 @@ import org.springframework.http.ResponseEntity;
|
||||
@SessionAttributes({"Sessvals"})
|
||||
public class MIS {
|
||||
private final HourlyMisService hourlyMisService;
|
||||
private final CommonService commonService;
|
||||
private final CloudSessionMapper cloudSessionMapper;
|
||||
|
||||
public MIS(HourlyMisService hourlyMisService) {
|
||||
public MIS(
|
||||
HourlyMisService hourlyMisService,
|
||||
CommonService commonService,
|
||||
CloudSessionMapper cloudSessionMapper) {
|
||||
this.hourlyMisService = hourlyMisService;
|
||||
this.commonService = commonService;
|
||||
this.cloudSessionMapper = cloudSessionMapper;
|
||||
}
|
||||
|
||||
@ModelAttribute("Sessvals")
|
||||
public Session legacySession(HttpSession httpSession) {
|
||||
Session legacySession = commonService.getSession(httpSession);
|
||||
if (legacySession != null) {
|
||||
return legacySession;
|
||||
}
|
||||
var userSession = commonService.getUserSession(httpSession);
|
||||
if (userSession == null) {
|
||||
return null;
|
||||
}
|
||||
legacySession = cloudSessionMapper.toLegacy(userSession);
|
||||
commonService.storeSession(httpSession, legacySession);
|
||||
return legacySession;
|
||||
}
|
||||
@RequestMapping(value="datetimerange",method=RequestMethod.POST )
|
||||
public String DateRangeFilter(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,HttpSession session)
|
||||
|
||||
@@ -12,18 +12,33 @@ public final class CloudQuerySource implements QuerySource {
|
||||
|
||||
@Override
|
||||
public String fetch(int queryId) {
|
||||
try {
|
||||
return client.fetchQuery(queryId)
|
||||
.map(response -> response.query())
|
||||
.blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException(
|
||||
"Cloud query was empty: " + queryId));
|
||||
} catch (QueryProviderException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw new QueryProviderException(
|
||||
"Cloud query request failed for query " + queryId, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fetch(String queryKey) {
|
||||
try {
|
||||
return client.fetchQuery(queryKey).map(response -> response.query()).blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException("Cloud query was empty: " + queryKey));
|
||||
.orElseThrow(() -> new QueryProviderException(
|
||||
"Cloud query was empty: " + queryKey));
|
||||
} catch (QueryProviderException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw new QueryProviderException(
|
||||
"Cloud query request failed for query " + queryKey, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import lib.models.UserSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -151,6 +152,9 @@ public class CommonService {
|
||||
}
|
||||
return cloudIdentityClient.fetchData(scope, data)
|
||||
.map(items -> items.stream().map(this::option).toList())
|
||||
.onErrorMap(TimeoutException.class, exception -> new IllegalStateException(
|
||||
"Cloud platform-data request timed out for scope: " + scope,
|
||||
exception))
|
||||
.blockOptional()
|
||||
.orElseGet(List::of);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
package matrix.services.mis;
|
||||
|
||||
import com.cygnus.db.CygnusDbExecutor;
|
||||
import com.cygnus.db.RowView;
|
||||
import com.cygnus.client.CloudIdentityClient;
|
||||
import com.cygnus.client.model.CloudDataItem;
|
||||
import lib.models.ReportColumnDefinition;
|
||||
import lib.models.ReportDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Service
|
||||
public class ReportDefinitionService {
|
||||
private static final String DEFINITION_QUERY = "report-definition";
|
||||
private final CygnusDbExecutor dbExecutor;
|
||||
private static final String DEFINITION_SCOPE = "report-definition";
|
||||
private final CloudIdentityClient cloudIdentityClient;
|
||||
|
||||
public ReportDefinitionService(CygnusDbExecutor dbExecutor) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
public ReportDefinitionService(CloudIdentityClient cloudIdentityClient) {
|
||||
this.cloudIdentityClient = cloudIdentityClient;
|
||||
}
|
||||
|
||||
public ReportDefinition hourlyMis(String fallbackTitle) {
|
||||
try {
|
||||
List<RowView> rows = dbExecutor.query(DEFINITION_QUERY, new Object[] { "hourly-mis" });
|
||||
List<CloudDataItem> rows = cloudIdentityClient
|
||||
.fetchData(DEFINITION_SCOPE, Map.of("reportKey", "hourly-mis"))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
exception -> new IllegalStateException("Cloud report definition request timed out", exception))
|
||||
.blockOptional().orElseGet(List::of);
|
||||
if (rows.isEmpty()) return defaults(fallbackTitle);
|
||||
RowView first = rows.getFirst();
|
||||
Map<String, Object> first = rows.getFirst().data();
|
||||
List<ReportColumnDefinition> columns = new ArrayList<>(rows.size());
|
||||
for (RowView row : rows) {
|
||||
for (CloudDataItem item : rows) {
|
||||
Map<String, Object> row = item.data();
|
||||
columns.add(new ReportColumnDefinition(
|
||||
text(row, "column_key"), text(row, "column_label"),
|
||||
number(row, "display_order"), text(row, "alignment"),
|
||||
number(row, "column_width"), bool(row, "is_visible"),
|
||||
bool(row, "is_totalled")));
|
||||
text(row, "columnKey"), text(row, "columnLabel"),
|
||||
number(row, "displayOrder"), text(row, "alignment"),
|
||||
number(row, "columnWidth"), bool(row, "visible"),
|
||||
bool(row, "totalled")));
|
||||
}
|
||||
return new ReportDefinition("hourly-mis", text(first, "report_title"),
|
||||
bool(first, "hide_leading_zero_columns"), List.copyOf(columns));
|
||||
return new ReportDefinition("hourly-mis", text(first, "reportTitle"),
|
||||
bool(first, "hideLeadingZeroColumns"), List.copyOf(columns));
|
||||
} catch (RuntimeException unavailable) {
|
||||
return defaults(fallbackTitle);
|
||||
}
|
||||
@@ -49,7 +56,7 @@ public class ReportDefinitionService {
|
||||
return new ReportDefinition("hourly-mis", title, true, List.copyOf(columns));
|
||||
}
|
||||
|
||||
private String text(RowView row, String column) { return String.valueOf(row.get(column)); }
|
||||
private int number(RowView row, String column) { return ((Number) row.get(column)).intValue(); }
|
||||
private boolean bool(RowView row, String column) { return Boolean.TRUE.equals(row.get(column)); }
|
||||
private String text(Map<String, Object> row, String column) { return String.valueOf(row.get(column)); }
|
||||
private int number(Map<String, Object> row, String column) { return ((Number) row.get(column)).intValue(); }
|
||||
private boolean bool(Map<String, Object> row, String column) { return Boolean.TRUE.equals(row.get(column)); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user