Add sorting parameters and refactor server part to use enums for sorting.
This commit is contained in:
parent
f95829adc6
commit
a3dce0b8a2
9 changed files with 174 additions and 100 deletions
frontend/src
server
resources
services
types
utils
|
@ -1,5 +1,5 @@
|
||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {type EnhancedNode, isEnhancedNode, type NodesFilter} from "@/types";
|
import {type EnhancedNode, isEnhancedNode, type NodesFilter, NodeSortField, SortDirection} from "@/types";
|
||||||
import {internalApi} from "@/utils/Api";
|
import {internalApi} from "@/utils/Api";
|
||||||
|
|
||||||
interface NodesStoreState {
|
interface NodesStoreState {
|
||||||
|
@ -7,6 +7,8 @@ interface NodesStoreState {
|
||||||
page: number;
|
page: number;
|
||||||
nodesPerPage: number;
|
nodesPerPage: number;
|
||||||
totalNodes: number;
|
totalNodes: number;
|
||||||
|
sortDirection: SortDirection;
|
||||||
|
sortField: NodeSortField;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNodesStore = defineStore({
|
export const useNodesStore = defineStore({
|
||||||
|
@ -17,6 +19,8 @@ export const useNodesStore = defineStore({
|
||||||
page: 1,
|
page: 1,
|
||||||
nodesPerPage: 20,
|
nodesPerPage: 20,
|
||||||
totalNodes: 0,
|
totalNodes: 0,
|
||||||
|
sortDirection: SortDirection.ASCENDING,
|
||||||
|
sortField: NodeSortField.HOSTNAME,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
@ -37,18 +41,27 @@ export const useNodesStore = defineStore({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async refresh(page: number, nodesPerPage: number, filter: NodesFilter, searchTerm?: string): Promise<void> {
|
async refresh(
|
||||||
|
page: number,
|
||||||
|
nodesPerPage: number,
|
||||||
|
sortDirection: SortDirection,
|
||||||
|
sortField: NodeSortField,
|
||||||
|
filter: NodesFilter,
|
||||||
|
searchTerm?: string
|
||||||
|
): Promise<void> {
|
||||||
const query: Record<string, any> = {
|
const query: Record<string, any> = {
|
||||||
...filter,
|
...filter,
|
||||||
};
|
};
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
query.q = searchTerm;
|
query.q = searchTerm;
|
||||||
}
|
}
|
||||||
const result = await internalApi.getPagedList<EnhancedNode>(
|
const result = await internalApi.getPagedList<EnhancedNode, NodeSortField>(
|
||||||
"nodes",
|
"nodes",
|
||||||
isEnhancedNode,
|
isEnhancedNode,
|
||||||
page,
|
page,
|
||||||
nodesPerPage,
|
nodesPerPage,
|
||||||
|
sortDirection,
|
||||||
|
sortField,
|
||||||
query,
|
query,
|
||||||
);
|
);
|
||||||
this.nodes = result.entries;
|
this.nodes = result.entries;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {toIsArray, type TypeGuard} from "@/types";
|
import {SortDirection, toIsArray, type TypeGuard} from "@/types";
|
||||||
import type {Headers} from "request";
|
import type {Headers} from "request";
|
||||||
import {parseInteger} from "@/utils/Numbers";
|
import {parseInteger} from "@/utils/Numbers";
|
||||||
|
|
||||||
|
@ -57,16 +57,20 @@ class Api {
|
||||||
return response.result;
|
return response.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPagedList<T>(
|
async getPagedList<Element, SortField>(
|
||||||
path: string,
|
path: string,
|
||||||
isT: TypeGuard<T>,
|
isElement: TypeGuard<Element>,
|
||||||
page: number,
|
page: number,
|
||||||
itemsPerPage: number,
|
itemsPerPage: number,
|
||||||
|
sortDirection?: SortDirection,
|
||||||
|
sortField?: SortField,
|
||||||
filter?: object,
|
filter?: object,
|
||||||
): Promise<PagedListResult<T>> {
|
): Promise<PagedListResult<Element>> {
|
||||||
const response = await this.doGet(path, toIsArray(isT), {
|
const response = await this.doGet(path, toIsArray(isElement), {
|
||||||
_page: page,
|
_page: page,
|
||||||
_perPage: itemsPerPage,
|
_perPage: itemsPerPage,
|
||||||
|
_sortDir: sortDirection,
|
||||||
|
_sortField: sortField,
|
||||||
...filter,
|
...filter,
|
||||||
});
|
});
|
||||||
const totalStr = response.headers.get("x-total-count");
|
const totalStr = response.headers.get("x-total-count");
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import {useNodesStore} from "@/stores/nodes";
|
import {useNodesStore} from "@/stores/nodes";
|
||||||
import {onMounted, ref, watch} from "vue";
|
import {onMounted, ref, watch} from "vue";
|
||||||
import type {EnhancedNode, MAC, NodesFilter} from "@/types";
|
import type {EnhancedNode, MAC, NodesFilter} from "@/types";
|
||||||
|
import {NodeSortField, SortDirection} from "@/types";
|
||||||
import Pager from "@/components/Pager.vue";
|
import Pager from "@/components/Pager.vue";
|
||||||
import LoadingContainer from "@/components/LoadingContainer.vue";
|
import LoadingContainer from "@/components/LoadingContainer.vue";
|
||||||
import NodesFilterPanel from "@/components/nodes/NodesFilterPanel.vue";
|
import NodesFilterPanel from "@/components/nodes/NodesFilterPanel.vue";
|
||||||
|
@ -32,7 +33,14 @@ async function refresh(page: number): Promise<void> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
redactAllFields(true);
|
redactAllFields(true);
|
||||||
try {
|
try {
|
||||||
await nodes.refresh(page, NODE_PER_PAGE, currentFilter.value, currentSearchTerm.value);
|
await nodes.refresh(
|
||||||
|
page,
|
||||||
|
NODE_PER_PAGE,
|
||||||
|
SortDirection.ASCENDING,
|
||||||
|
NodeSortField.HOSTNAME,
|
||||||
|
currentFilter.value,
|
||||||
|
currentSearchTerm.value,
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {forConstraint, forConstraints} from "../validation/validator";
|
||||||
import * as Resources from "../utils/resources";
|
import * as Resources from "../utils/resources";
|
||||||
import {Entity} from "../utils/resources";
|
import {Entity} from "../utils/resources";
|
||||||
import {Request, Response} from "express";
|
import {Request, Response} from "express";
|
||||||
import {EnhancedNode, Node} from "../types";
|
import {EnhancedNode, isNodeSortField, Node} from "../types";
|
||||||
|
|
||||||
const nodeFields = ['hostname', 'key', 'email', 'nickname', 'mac', 'coords', 'monitoring'];
|
const nodeFields = ['hostname', 'key', 'email', 'nickname', 'mac', 'coords', 'monitoring'];
|
||||||
|
|
||||||
|
@ -130,19 +130,7 @@ async function doGetAll(req: Request): Promise<{ total: number; pageNodes: any }
|
||||||
|
|
||||||
const sortedNodes = Resources.sort(
|
const sortedNodes = Resources.sort(
|
||||||
filteredNodes,
|
filteredNodes,
|
||||||
[
|
isNodeSortField,
|
||||||
'hostname',
|
|
||||||
'nickname',
|
|
||||||
'email',
|
|
||||||
'token',
|
|
||||||
'mac',
|
|
||||||
'key',
|
|
||||||
'site',
|
|
||||||
'domain',
|
|
||||||
'coords',
|
|
||||||
'onlineState',
|
|
||||||
'monitoringState'
|
|
||||||
],
|
|
||||||
restParams
|
restParams
|
||||||
);
|
);
|
||||||
const pageNodes = Resources.getPageEntities(sortedNodes, restParams);
|
const pageNodes = Resources.getPageEntities(sortedNodes, restParams);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {getTasks, Task, TaskState} from "../jobs/scheduler";
|
||||||
import {normalizeString} from "../utils/strings";
|
import {normalizeString} from "../utils/strings";
|
||||||
import {forConstraint} from "../validation/validator";
|
import {forConstraint} from "../validation/validator";
|
||||||
import {Request, Response} from "express";
|
import {Request, Response} from "express";
|
||||||
|
import {isTaskSortField} from "../types";
|
||||||
|
|
||||||
const isValidId = forConstraint(CONSTRAINTS.id, false);
|
const isValidId = forConstraint(CONSTRAINTS.id, false);
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ async function doGetAll(req: Request): Promise<{total: number, pageTasks: Entity
|
||||||
|
|
||||||
const tasks = Resources.sort(
|
const tasks = Resources.sort(
|
||||||
_.values(getTasks()),
|
_.values(getTasks()),
|
||||||
['id', 'name', 'schedule', 'state', 'runningSince', 'lastRunStarted'],
|
isTaskSortField,
|
||||||
restParams
|
restParams
|
||||||
);
|
);
|
||||||
const filteredTasks = Resources.filter(
|
const filteredTasks = Resources.filter(
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Logger from "../logger";
|
||||||
import * as MailTemplateService from "./mailTemplateService";
|
import * as MailTemplateService from "./mailTemplateService";
|
||||||
import * as Resources from "../utils/resources";
|
import * as Resources from "../utils/resources";
|
||||||
import {RestParams} from "../utils/resources";
|
import {RestParams} from "../utils/resources";
|
||||||
import {Mail, MailData, MailId, MailType} from "../types";
|
import {isMailSortField, Mail, MailData, MailId, MailSortField, MailType} from "../types";
|
||||||
|
|
||||||
const MAIL_QUEUE_DB_BATCH_SIZE = 50;
|
const MAIL_QUEUE_DB_BATCH_SIZE = 50;
|
||||||
|
|
||||||
|
@ -128,8 +128,8 @@ export async function getPendingMails (restParams: RestParams): Promise<{mails:
|
||||||
|
|
||||||
const filter = Resources.filterClause(
|
const filter = Resources.filterClause(
|
||||||
restParams,
|
restParams,
|
||||||
'id',
|
MailSortField.ID,
|
||||||
['id', 'failures', 'sender', 'recipient', 'email', 'created_at', 'modified_at'],
|
isMailSortField,
|
||||||
['id', 'failures', 'sender', 'recipient', 'email']
|
['id', 'failures', 'sender', 'recipient', 'email']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,17 @@ import {normalizeMac} from "../utils/strings";
|
||||||
import {monitoringDisableUrl} from "../utils/urlBuilder";
|
import {monitoringDisableUrl} from "../utils/urlBuilder";
|
||||||
import CONSTRAINTS from "../validation/constraints";
|
import CONSTRAINTS from "../validation/constraints";
|
||||||
import {forConstraint} from "../validation/validator";
|
import {forConstraint} from "../validation/validator";
|
||||||
import {MAC, MailType, Node, NodeId, OnlineState, NodeStateData, UnixTimestampSeconds} from "../types";
|
import {
|
||||||
|
isMonitoringSortField,
|
||||||
|
MAC,
|
||||||
|
MailType,
|
||||||
|
MonitoringSortField,
|
||||||
|
Node,
|
||||||
|
NodeId,
|
||||||
|
NodeStateData,
|
||||||
|
OnlineState,
|
||||||
|
UnixTimestampSeconds
|
||||||
|
} from "../types";
|
||||||
|
|
||||||
const MONITORING_STATE_MACS_CHUNK_SIZE = 100;
|
const MONITORING_STATE_MACS_CHUNK_SIZE = 100;
|
||||||
const NEVER_ONLINE_NODES_DELETION_CHUNK_SIZE = 20;
|
const NEVER_ONLINE_NODES_DELETION_CHUNK_SIZE = 20;
|
||||||
|
@ -255,8 +265,7 @@ export function parseNodesJson (body: string): NodesParsingResult {
|
||||||
const parsedNode = parseNode(result.importTimestamp, nodeData);
|
const parsedNode = parseNode(result.importTimestamp, nodeData);
|
||||||
Logger.tag('monitoring', 'parsing-nodes-json').debug(`Parsing node successful: ${parsedNode.mac}`);
|
Logger.tag('monitoring', 'parsing-nodes-json').debug(`Parsing node successful: ${parsedNode.mac}`);
|
||||||
result.nodes.push(parsedNode);
|
result.nodes.push(parsedNode);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
result.failedNodesCount += 1;
|
result.failedNodesCount += 1;
|
||||||
Logger.tag('monitoring', 'parsing-nodes-json').error("Could not parse node.", error, nodeData);
|
Logger.tag('monitoring', 'parsing-nodes-json').error("Could not parse node.", error, nodeData);
|
||||||
}
|
}
|
||||||
|
@ -532,21 +541,6 @@ async function retrieveNodeInformationForUrls(urls: string[]): Promise<RetrieveN
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAll(restParams: RestParams): Promise<{ total: number, monitoringStates: any[] }> {
|
export async function getAll(restParams: RestParams): Promise<{ total: number, monitoringStates: any[] }> {
|
||||||
const sortFields = [
|
|
||||||
'id',
|
|
||||||
'hostname',
|
|
||||||
'mac',
|
|
||||||
'site',
|
|
||||||
'domain',
|
|
||||||
'monitoring_state',
|
|
||||||
'state',
|
|
||||||
'last_seen',
|
|
||||||
'import_timestamp',
|
|
||||||
'last_status_mail_type',
|
|
||||||
'last_status_mail_sent',
|
|
||||||
'created_at',
|
|
||||||
'modified_at'
|
|
||||||
];
|
|
||||||
const filterFields = [
|
const filterFields = [
|
||||||
'hostname',
|
'hostname',
|
||||||
'mac',
|
'mac',
|
||||||
|
@ -566,8 +560,8 @@ export async function getAll(restParams: RestParams): Promise<{total: number, mo
|
||||||
|
|
||||||
const filter = Resources.filterClause(
|
const filter = Resources.filterClause(
|
||||||
restParams,
|
restParams,
|
||||||
'id',
|
MonitoringSortField.ID,
|
||||||
sortFields,
|
isMonitoringSortField,
|
||||||
filterFields
|
filterFields
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -648,8 +642,7 @@ export async function sendMonitoringMails(): Promise<void> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendOnlineAgainMails(startTime);
|
await sendOnlineAgainMails(startTime);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
// only logging an continuing with next type
|
// only logging an continuing with next type
|
||||||
Logger
|
Logger
|
||||||
.tag('monitoring', 'mail-sending')
|
.tag('monitoring', 'mail-sending')
|
||||||
|
@ -659,8 +652,7 @@ export async function sendMonitoringMails(): Promise<void> {
|
||||||
for (let mailNumber = 1; mailNumber <= 3; mailNumber++) {
|
for (let mailNumber = 1; mailNumber <= 3; mailNumber++) {
|
||||||
try {
|
try {
|
||||||
await sendOfflineMails(startTime, mailNumber);
|
await sendOfflineMails(startTime, mailNumber);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
// only logging an continuing with next type
|
// only logging an continuing with next type
|
||||||
Logger
|
Logger
|
||||||
.tag('monitoring', 'mail-sending')
|
.tag('monitoring', 'mail-sending')
|
||||||
|
@ -786,8 +778,7 @@ async function deleteNodeByMac(mac: MAC): Promise<void> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
node = await NodeService.getNodeDataByMac(mac);
|
node = await NodeService.getNodeDataByMac(mac);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
// Only log error. We try to delete the nodes state anyways.
|
// Only log error. We try to delete the nodes state anyways.
|
||||||
Logger.tag('nodes', 'delete-offline').error('Could not find node to delete: ' + mac, error);
|
Logger.tag('nodes', 'delete-offline').error('Could not find node to delete: ' + mac, error);
|
||||||
}
|
}
|
||||||
|
@ -801,8 +792,7 @@ async function deleteNodeByMac(mac: MAC): Promise<void> {
|
||||||
'DELETE FROM node_state WHERE mac = ? AND state = ?',
|
'DELETE FROM node_state WHERE mac = ? AND state = ?',
|
||||||
[mac, 'OFFLINE'],
|
[mac, 'OFFLINE'],
|
||||||
);
|
);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
// Only log error and continue with next node.
|
// Only log error and continue with next node.
|
||||||
Logger.tag('nodes', 'delete-offline').error('Could not delete node state: ' + mac, error);
|
Logger.tag('nodes', 'delete-offline').error('Could not delete node state: ' + mac, error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ import {ArrayField, Field, RawJsonField} from "sparkson";
|
||||||
// Types shared with the client.
|
// Types shared with the client.
|
||||||
export type TypeGuard<T> = (arg: unknown) => arg is T;
|
export type TypeGuard<T> = (arg: unknown) => arg is T;
|
||||||
|
|
||||||
|
export type EnumValue<E> = E[keyof E];
|
||||||
|
export type EnumTypeGuard<E> = TypeGuard<EnumValue<E>>;
|
||||||
|
|
||||||
export function isObject(arg: unknown): arg is object {
|
export function isObject(arg: unknown): arg is object {
|
||||||
return arg !== null && typeof arg === "object";
|
return arg !== null && typeof arg === "object";
|
||||||
}
|
}
|
||||||
|
@ -39,8 +42,8 @@ export function toIsArray<T>(isT: TypeGuard<T>): TypeGuard<T[]> {
|
||||||
return (arg): arg is T[] => isArray(arg, isT);
|
return (arg): arg is T[] => isArray(arg, isT);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toIsEnum<E>(enumDef: E): TypeGuard<E> {
|
export function toIsEnum<E>(enumDef: E): EnumTypeGuard<E> {
|
||||||
return (arg): arg is E => Object.values(enumDef).includes(arg as [keyof E]);
|
return (arg): arg is EnumValue<E> => Object.values(enumDef).includes(arg as [keyof E]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isOptional<T>(arg: unknown, isT: TypeGuard<T>): arg is (T | undefined) {
|
export function isOptional<T>(arg: unknown, isT: TypeGuard<T>): arg is (T | undefined) {
|
||||||
|
@ -333,6 +336,22 @@ export function isEnhancedNode(arg: unknown): arg is EnhancedNode {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum NodeSortField {
|
||||||
|
HOSTNAME = 'hostname',
|
||||||
|
NICKNAME = 'nickname',
|
||||||
|
EMAIL = 'email',
|
||||||
|
TOKEN = 'token',
|
||||||
|
MAC = 'mac',
|
||||||
|
KEY = 'key',
|
||||||
|
SITE = 'site',
|
||||||
|
DOMAIN = 'domain',
|
||||||
|
COORDS = 'coords',
|
||||||
|
ONLINE_STATE = 'onlineState',
|
||||||
|
MONITORING_STATE = 'monitoringState',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isNodeSortField = toIsEnum(NodeSortField);
|
||||||
|
|
||||||
export interface NodesFilter {
|
export interface NodesFilter {
|
||||||
hasKey?: boolean;
|
hasKey?: boolean;
|
||||||
hasCoords?: boolean;
|
hasCoords?: boolean;
|
||||||
|
@ -365,3 +384,53 @@ export function isNodesFilter(arg: unknown): arg is NodesFilter {
|
||||||
isOptional(filter.onlineState, isOnlineState)
|
isOptional(filter.onlineState, isOnlineState)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MonitoringSortField {
|
||||||
|
ID = 'id',
|
||||||
|
HOSTNAME = 'hostname',
|
||||||
|
MAC = 'mac',
|
||||||
|
SITE = 'site',
|
||||||
|
DOMAIN = 'domain',
|
||||||
|
MONITORING_STATE = 'monitoring_state',
|
||||||
|
STATE = 'state',
|
||||||
|
LAST_SEEN = 'last_seen',
|
||||||
|
IMPORT_TIMESTAMP = 'import_timestamp',
|
||||||
|
LAST_STATUS_MAIL_TYPE = 'last_status_mail_type',
|
||||||
|
LAST_STATUS_MAIL_SENT = 'last_status_mail_sent',
|
||||||
|
CREATED_AT = 'created_at',
|
||||||
|
MODIFIED_AT = 'modified_at',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isMonitoringSortField = toIsEnum(MonitoringSortField);
|
||||||
|
|
||||||
|
export enum TaskSortField {
|
||||||
|
ID = 'id',
|
||||||
|
NAME = 'name',
|
||||||
|
SCHEDULE = 'schedule',
|
||||||
|
STATE = 'state',
|
||||||
|
RUNNING_SINCE = 'runningSince',
|
||||||
|
LAST_RUN_STARTED = 'lastRunStarted',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isTaskSortField = toIsEnum(TaskSortField);
|
||||||
|
|
||||||
|
export enum MailSortField {
|
||||||
|
ID = 'id',
|
||||||
|
FAILURES = 'failures',
|
||||||
|
SENDER = 'sender',
|
||||||
|
RECIPIENT = 'recipient',
|
||||||
|
EMAIL = 'email',
|
||||||
|
CREATED_AT = 'created_at',
|
||||||
|
MODIFIED_AT = 'modified_at',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isMailSortField = toIsEnum(MailSortField);
|
||||||
|
|
||||||
|
export type GenericSortField = string;
|
||||||
|
|
||||||
|
export enum SortDirection {
|
||||||
|
ASCENDING = "ASC",
|
||||||
|
DESCENDING = "DESC",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isSortDirection = toIsEnum(SortDirection);
|
||||||
|
|
|
@ -5,14 +5,15 @@ import ErrorTypes from "../utils/errorTypes";
|
||||||
import Logger from "../logger";
|
import Logger from "../logger";
|
||||||
import {Constraints, forConstraints, isConstraints} from "../validation/validator";
|
import {Constraints, forConstraints, isConstraints} from "../validation/validator";
|
||||||
import {Request, Response} from "express";
|
import {Request, Response} from "express";
|
||||||
|
import {EnumTypeGuard, EnumValue, type GenericSortField, SortDirection, TypeGuard} from "../types";
|
||||||
|
|
||||||
export type Entity = { [key: string]: any };
|
export type Entity = { [key: string]: any };
|
||||||
|
|
||||||
export type RestParams = {
|
export type RestParams = {
|
||||||
q?: string;
|
q?: string;
|
||||||
|
|
||||||
_sortField?: string;
|
_sortField?: GenericSortField;
|
||||||
_sortDir?: string;
|
_sortDir?: SortDirection;
|
||||||
|
|
||||||
_page: number;
|
_page: number;
|
||||||
_perPage: number;
|
_perPage: number;
|
||||||
|
@ -38,18 +39,18 @@ function respond(res: Response, httpCode: number, data: any, type: string): void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function orderByClause(
|
function orderByClause<S>(
|
||||||
restParams: RestParams,
|
restParams: RestParams,
|
||||||
defaultSortField: string,
|
defaultSortField: EnumValue<S>,
|
||||||
allowedSortFields: string[]
|
isSortField: EnumTypeGuard<S>,
|
||||||
): OrderByClause {
|
): OrderByClause {
|
||||||
let sortField = _.includes(allowedSortFields, restParams._sortField) ? restParams._sortField : undefined;
|
let sortField: EnumValue<S> | undefined = isSortField(restParams._sortField) ? restParams._sortField : undefined;
|
||||||
if (!sortField) {
|
if (!sortField) {
|
||||||
sortField = defaultSortField;
|
sortField = defaultSortField;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: 'ORDER BY ' + sortField + ' ' + (restParams._sortDir === 'ASC' ? 'ASC' : 'DESC'),
|
query: 'ORDER BY LOWER(' + sortField + ') ' + (restParams._sortDir === SortDirection.ASCENDING ? 'ASC' : 'DESC'),
|
||||||
params: []
|
params: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -208,14 +209,14 @@ export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sort<T>(entities: ArrayLike<T>, allowedSortFields: string[], restParams: RestParams): ArrayLike<T> {
|
export function sort<T, S>(entities: ArrayLike<T>, isSortField: TypeGuard<S>, restParams: RestParams): ArrayLike<T> {
|
||||||
const sortField = _.includes(allowedSortFields, restParams._sortField) ? restParams._sortField : undefined;
|
const sortField: S | undefined = isSortField(restParams._sortField) ? restParams._sortField : undefined;
|
||||||
if (!sortField) {
|
if (!sortField) {
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sorted: T[] = _.sortBy(entities, [sortField]);
|
const sorted: T[] = _.sortBy(entities, [sortField]);
|
||||||
if (restParams._sortDir === 'ASC') {
|
if (restParams._sortDir === SortDirection.ASCENDING) {
|
||||||
return sorted;
|
return sorted;
|
||||||
} else {
|
} else {
|
||||||
return _.reverse(sorted);
|
return _.reverse(sorted);
|
||||||
|
@ -231,16 +232,16 @@ export function getPageEntities (entities: ArrayLike<Entity>, restParams: RestPa
|
||||||
|
|
||||||
export {filterCondition as whereCondition};
|
export {filterCondition as whereCondition};
|
||||||
|
|
||||||
export function filterClause (
|
export function filterClause<S>(
|
||||||
restParams: RestParams,
|
restParams: RestParams,
|
||||||
defaultSortField: string,
|
defaultSortField: EnumValue<S>,
|
||||||
allowedSortFields: string[],
|
isSortField: EnumTypeGuard<S>,
|
||||||
filterFields: string[],
|
filterFields: string[],
|
||||||
): FilterClause {
|
): FilterClause {
|
||||||
const orderBy = orderByClause(
|
const orderBy = orderByClause<S>(
|
||||||
restParams,
|
restParams,
|
||||||
defaultSortField,
|
defaultSortField,
|
||||||
allowedSortFields
|
isSortField,
|
||||||
);
|
);
|
||||||
const limitOffset = limitOffsetClause(restParams);
|
const limitOffset = limitOffsetClause(restParams);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue