Browse Source

fixed UI sorting to match api logic

pull/15691/head
dashevchenko 4 months ago
parent
commit
22043d1261
  1. 12
      application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
  2. 2
      common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java
  3. 3
      ui-ngx/src/app/core/auth/auth.models.ts
  4. 4
      ui-ngx/src/app/core/auth/auth.reducer.ts
  5. 3
      ui-ngx/src/app/core/auth/auth.service.ts
  6. 36
      ui-ngx/src/app/shared/models/page/page-link.ts

12
application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettings;
import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.common.data.settings.UserSettingsType;
import org.thingsboard.server.common.msg.edqs.EdqsService;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.mobile.QrCodeSettingService; import org.thingsboard.server.dao.mobile.QrCodeSettingService;
import org.thingsboard.server.dao.trendz.TrendzSettingsService; import org.thingsboard.server.dao.trendz.TrendzSettingsService;
@ -52,6 +53,7 @@ import org.thingsboard.server.utils.DebugModeRateLimitsConfig;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Hidden @Hidden
@ -76,6 +78,11 @@ public class SystemInfoController extends BaseController {
@Value("${debug.settings.default_duration:15}") @Value("${debug.settings.default_duration:15}")
private int defaultDebugDurationMinutes; private int defaultDebugDurationMinutes;
@Value("${sql.entity_data_query_nulls_order_strategy:default}")
private String nullsOrderStrategy;
private static final Set<String> ACCEPTED_NULLS_ORDER_STRATEGIES = Set.of("default", "nulls_first", "nulls_last");
@Autowired(required = false) @Autowired(required = false)
private BuildProperties buildProperties; private BuildProperties buildProperties;
@ -91,6 +98,9 @@ public class SystemInfoController extends BaseController {
@Autowired @Autowired
private TrendzSettingsService trendzSettingsService; private TrendzSettingsService trendzSettingsService;
@Autowired
private EdqsService edqsService;
@PostConstruct @PostConstruct
public void init() { public void init() {
JsonNode info = buildInfoObject(); JsonNode info = buildInfoObject();
@ -150,6 +160,8 @@ public class SystemInfoController extends BaseController {
} }
systemParams.setUserSettings(userSettingsNode); systemParams.setUserSettings(userSettingsNode);
systemParams.setMaxDatapointsLimit(maxDatapointsLimit); systemParams.setMaxDatapointsLimit(maxDatapointsLimit);
systemParams.setNullsOrderStrategy(ACCEPTED_NULLS_ORDER_STRATEGIES.contains(nullsOrderStrategy) ? nullsOrderStrategy : "default");
systemParams.setEdqsEnabled(edqsService.isApiEnabled());
if (!currentUser.isSystemAdmin()) { if (!currentUser.isSystemAdmin()) {
DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration();
systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize()); systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize());

2
common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java

@ -39,4 +39,6 @@ public class SystemParams {
long maxArgumentsPerCF; long maxArgumentsPerCF;
long maxDataPointsPerRollingArg; long maxDataPointsPerRollingArg;
TrendzSettings trendzSettings; TrendzSettings trendzSettings;
String nullsOrderStrategy;
boolean edqsEnabled;
} }

3
ui-ngx/src/app/core/auth/auth.models.ts

@ -17,6 +17,7 @@
import { AuthUser, User } from '@shared/models/user.model'; import { AuthUser, User } from '@shared/models/user.model';
import { UserSettings } from '@shared/models/user-settings.models'; import { UserSettings } from '@shared/models/user-settings.models';
import { TrendzSettings } from '@shared/models/trendz-settings.models'; import { TrendzSettings } from '@shared/models/trendz-settings.models';
import { NullsOrderStrategy } from '@shared/models/page/page-link';
export interface SysParamsState { export interface SysParamsState {
userTokenAccessEnabled: boolean; userTokenAccessEnabled: boolean;
@ -34,6 +35,8 @@ export interface SysParamsState {
ruleChainDebugPerTenantLimitsConfiguration?: string; ruleChainDebugPerTenantLimitsConfiguration?: string;
calculatedFieldDebugPerTenantLimitsConfiguration?: string; calculatedFieldDebugPerTenantLimitsConfiguration?: string;
trendzSettings: TrendzSettings; trendzSettings: TrendzSettings;
nullsOrderStrategy: NullsOrderStrategy;
edqsEnabled: boolean;
} }
export interface SysParams extends SysParamsState { export interface SysParams extends SysParamsState {

4
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -36,7 +36,9 @@ const emptyUserAuthState: AuthPayload = {
maxDataPointsPerRollingArg: 0, maxDataPointsPerRollingArg: 0,
maxDebugModeDurationMinutes: 0, maxDebugModeDurationMinutes: 0,
userSettings: initialUserSettings, userSettings: initialUserSettings,
trendzSettings: initialTrendzSettings trendzSettings: initialTrendzSettings,
nullsOrderStrategy: 'default',
edqsEnabled: false
}; };
export const initialState: AuthState = { export const initialState: AuthState = {

3
ui-ngx/src/app/core/auth/auth.service.ts

@ -20,6 +20,7 @@ import { HttpClient } from '@angular/common/http';
import { Observable, of, ReplaySubject, throwError } from 'rxjs'; import { Observable, of, ReplaySubject, throwError } from 'rxjs';
import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { catchError, map, mergeMap, tap } from 'rxjs/operators';
import { setEdqsEnabled, setNullsOrderStrategy } from '@shared/models/page/page-link';
import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models'; import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models';
import { Router, UrlTree } from '@angular/router'; import { Router, UrlTree } from '@angular/router';
@ -441,6 +442,8 @@ export class AuthService {
return this.http.get<SysParams>('/api/system/params', defaultHttpOptions()).pipe( return this.http.get<SysParams>('/api/system/params', defaultHttpOptions()).pipe(
map((sysParams) => { map((sysParams) => {
this.timeService.setMaxDatapointsLimit(sysParams.maxDatapointsLimit); this.timeService.setMaxDatapointsLimit(sysParams.maxDatapointsLimit);
setNullsOrderStrategy(sysParams.nullsOrderStrategy);
setEdqsEnabled(sysParams.edqsEnabled);
return sysParams; return sysParams;
}), }),
catchError(() => of({} as SysParamsState)) catchError(() => of({} as SysParamsState))

36
ui-ngx/src/app/shared/models/page/page-link.ts

@ -22,6 +22,19 @@ import { EntitiesTableAction } from '@home/models/entity/entity-table-component.
export const MAX_SAFE_PAGE_SIZE = 2147483647; export const MAX_SAFE_PAGE_SIZE = 2147483647;
export type NullsOrderStrategy = 'default' | 'nulls_first' | 'nulls_last';
let nullsOrderStrategy: NullsOrderStrategy = 'default';
let edqsEnabled = false;
export function setNullsOrderStrategy(value: NullsOrderStrategy): void {
nullsOrderStrategy = value ?? 'default';
}
export function setEdqsEnabled(value: boolean): void {
edqsEnabled = !!value;
}
export type PageLinkSearchFunction<T> = (entity: T, textSearch: string, searchProperty?: string) => boolean; export type PageLinkSearchFunction<T> = (entity: T, textSearch: string, searchProperty?: string) => boolean;
export interface PageQueryParam extends Partial<SortOrder>{ export interface PageQueryParam extends Partial<SortOrder>{
@ -76,6 +89,29 @@ const defaultPageLinkSearch: PageLinkSearchFunction<any> =
export function sortItems(item1: any, item2: any, property: string, asc: boolean): number { export function sortItems(item1: any, item2: any, property: string, asc: boolean): number {
const item1Value = getDescendantProp(item1, property); const item1Value = getDescendantProp(item1, property);
const item2Value = getDescendantProp(item2, property); const item2Value = getDescendantProp(item2, property);
const item1Empty = item1Value === null || item1Value === undefined || item1Value === '';
const item2Empty = item2Value === null || item2Value === undefined || item2Value === '';
// Mirror backend's nulls ordering only for column types where the SQL ORDER BY sees real NULLs
// (boolean/numeric attribute values and entity-field columns). String attribute/telemetry
// values are coalesced to '' in SQL, so the backend ignores the strategy there and naive
// compare below already matches its order. EDQS has its own fixed null handling (ASC=NULLS
// FIRST, DESC=NULLS LAST) that doesn't honor the strategy at all, so skip this branch then
// — naive compare already matches EDQS's behavior for the values we care about.
if (!edqsEnabled && (item1Empty || item2Empty) && !(item1Empty && item2Empty)) {
const other = item1Empty ? item2Value : item1Value;
const otherIsBoolOrNum =
typeof other === 'boolean' || other === 'true' || other === 'false' ||
(typeof other === 'number' && isFinite(other)) ||
(typeof other === 'string' && other.trim() !== '' && !isNaN(Number(other)));
if (otherIsBoolOrNum) {
const nullsFirst = nullsOrderStrategy === 'nulls_first'
|| (nullsOrderStrategy === 'default' && !asc);
if (item1Empty) {
return nullsFirst ? -1 : 1;
}
return nullsFirst ? 1 : -1;
}
}
let result = 0; let result = 0;
if (item1Value !== item2Value) { if (item1Value !== item2Value) {
const item1Type = typeof item1Value; const item1Type = typeof item1Value;

Loading…
Cancel
Save