Merge branch 'main' into new-admin
This commit is contained in:
commit
6fd4dbb33e
7 changed files with 72 additions and 37 deletions
|
@ -32,7 +32,7 @@ function getNormalizedNodeData(reqData: any): CreateOrUpdateNode {
|
||||||
_.each(nodeFields, function (field) {
|
_.each(nodeFields, function (field) {
|
||||||
let value = normalizeString(reqData[field]);
|
let value = normalizeString(reqData[field]);
|
||||||
if (field === 'mac') {
|
if (field === 'mac') {
|
||||||
value = normalizeMac(value);
|
value = normalizeMac(value as MAC);
|
||||||
}
|
}
|
||||||
node[field] = value;
|
node[field] = value;
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,7 +63,17 @@ export function toNodeTokenResponse(node: StoredNode): NodeTokenResponse {
|
||||||
|
|
||||||
export function toDomainSpecificNodeResponse(node: StoredNode, nodeStateData: NodeStateData): DomainSpecificNodeResponse {
|
export function toDomainSpecificNodeResponse(node: StoredNode, nodeStateData: NodeStateData): DomainSpecificNodeResponse {
|
||||||
return {
|
return {
|
||||||
...toNodeResponse(node),
|
token: node.token,
|
||||||
|
nickname: node.nickname,
|
||||||
|
email: node.email,
|
||||||
|
hostname: node.hostname,
|
||||||
|
coords: node.coords,
|
||||||
|
key: node.key,
|
||||||
|
mac: node.mac,
|
||||||
|
monitoring: node.monitoringState !== MonitoringState.DISABLED,
|
||||||
|
monitoringConfirmed: node.monitoringState === MonitoringState.ACTIVE,
|
||||||
|
monitoringState: node.monitoringState,
|
||||||
|
modifiedAt: node.modifiedAt,
|
||||||
site: nodeStateData.site,
|
site: nodeStateData.site,
|
||||||
domain: nodeStateData.domain,
|
domain: nodeStateData.domain,
|
||||||
onlineState: nodeStateData.state,
|
onlineState: nodeStateData.state,
|
||||||
|
|
|
@ -93,6 +93,14 @@ export function isBoolean(arg: unknown): arg is boolean {
|
||||||
return typeof arg === "boolean"
|
return typeof arg === "boolean"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isUndefined(arg: unknown): arg is undefined {
|
||||||
|
return arg === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNull(arg: unknown): arg is null {
|
||||||
|
return arg === null;
|
||||||
|
}
|
||||||
|
|
||||||
export function toIsArray<T>(isT: TypeGuard<T>): TypeGuard<T[]> {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -473,7 +481,7 @@ export const isDomain = isString;
|
||||||
/**
|
/**
|
||||||
* Represents a node in the context of a Freifunk site and domain.
|
* Represents a node in the context of a Freifunk site and domain.
|
||||||
*/
|
*/
|
||||||
export type DomainSpecificNodeResponse = NodeResponse & {
|
export type DomainSpecificNodeResponse = Record<NodeSortField, any> & NodeResponse & {
|
||||||
site?: Site,
|
site?: Site,
|
||||||
domain?: Domain,
|
domain?: Domain,
|
||||||
onlineState?: OnlineState,
|
onlineState?: OnlineState,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
EnumTypeGuard,
|
EnumTypeGuard,
|
||||||
EnumValue,
|
EnumValue,
|
||||||
type GenericSortField,
|
type GenericSortField,
|
||||||
isJSONObject,
|
isJSONObject, isNumber, isString, isUndefined,
|
||||||
JSONObject,
|
JSONObject,
|
||||||
SortDirection,
|
SortDirection,
|
||||||
TypeGuard
|
TypeGuard
|
||||||
|
@ -91,17 +91,14 @@ function filterCondition(restParams: RestParams, filterFields: string[]): Filter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = _.join(
|
let query = filterFields
|
||||||
_.map(filterFields, function (field) {
|
.map(field => 'LOWER(' + field + ') LIKE ?')
|
||||||
return 'LOWER(' + field + ') LIKE ?';
|
.join(' OR ');
|
||||||
}),
|
|
||||||
' OR '
|
|
||||||
);
|
|
||||||
|
|
||||||
query += ' ESCAPE \'\\\'';
|
query += ' ESCAPE \'\\\'';
|
||||||
|
|
||||||
const search = '%' + (_.isString(restParams.q) ? escapeForLikePattern(_.toLower(restParams.q.trim())) : '') + '%';
|
const search = '%' + (isString(restParams.q) ? escapeForLikePattern(restParams.q.trim().toLowerCase()) : '') + '%';
|
||||||
const params = _.times(filterFields.length, _.constant(search));
|
const params = _.times(filterFields.length, () => search);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: query,
|
query: query,
|
||||||
|
@ -111,13 +108,14 @@ function filterCondition(restParams: RestParams, filterFields: string[]): Filter
|
||||||
|
|
||||||
function getConstrainedValues(data: { [key: string]: any }, constraints: Constraints): { [key: string]: any } {
|
function getConstrainedValues(data: { [key: string]: any }, constraints: Constraints): { [key: string]: any } {
|
||||||
const values: { [key: string]: any } = {};
|
const values: { [key: string]: any } = {};
|
||||||
_.each(_.keys(constraints), (key: string): void => {
|
for (const key of Object.keys(constraints)) {
|
||||||
const value = data[key];
|
const value = data[key];
|
||||||
values[key] =
|
values[key] =
|
||||||
_.isUndefined(value) && key in constraints && !_.isUndefined(constraints[key].default)
|
isUndefined(value) && key in constraints && !isUndefined(constraints[key].default)
|
||||||
? constraints[key].default
|
? constraints[key].default
|
||||||
: value;
|
: value;
|
||||||
});
|
|
||||||
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,10 +174,10 @@ export async function getValidRestParams(
|
||||||
return restParams as RestParams;
|
return restParams as RestParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[], restParams: RestParams): E[] {
|
export function filter<E>(entities: E[], allowedFilterFields: string[], restParams: RestParams): E[] {
|
||||||
let query = restParams.q;
|
let query = restParams.q;
|
||||||
if (query) {
|
if (query) {
|
||||||
query = _.toLower(query.trim());
|
query = query.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryMatches(entity: Entity): boolean {
|
function queryMatches(entity: Entity): boolean {
|
||||||
|
@ -191,15 +189,15 @@ export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[],
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let value = entity[field];
|
let value = entity[field];
|
||||||
if (_.isNumber(value)) {
|
if (isNumber(value)) {
|
||||||
value = value.toString();
|
value = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_.isString(value) || _.isEmpty(value)) {
|
if (!isString(value) || _.isEmpty(value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = _.toLower(value);
|
value = value.toLowerCase();
|
||||||
if (field === 'mac') {
|
if (field === 'mac') {
|
||||||
return _.includes(value.replace(/:/g, ''), query.replace(/:/g, ''));
|
return _.includes(value.replace(/:/g, ''), query.replace(/:/g, ''));
|
||||||
}
|
}
|
||||||
|
@ -216,7 +214,7 @@ export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[],
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.every(filters, (value: any, key: string): boolean => {
|
return _.every(filters, (value: any, key: string): boolean => {
|
||||||
if (_.isUndefined(value)) {
|
if (isUndefined(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_.startsWith(key, 'has')) {
|
if (_.startsWith(key, 'has')) {
|
||||||
|
@ -232,25 +230,43 @@ export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sort<T, S>(entities: ArrayLike<T>, isSortField: TypeGuard<S>, restParams: RestParams): ArrayLike<T> {
|
export function sort<T extends Record<S, any>, S extends string>(entities: T[], isSortField: TypeGuard<S>, restParams: RestParams): T[] {
|
||||||
const sortField: S | undefined = isSortField(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 = entities.slice(0);
|
||||||
if (restParams._sortDir === SortDirection.ASCENDING) {
|
sorted.sort((a, b) => {
|
||||||
return sorted;
|
let as = a[sortField];
|
||||||
} else {
|
let bs = b[sortField];
|
||||||
return _.reverse(sorted);
|
|
||||||
}
|
if (isString(as)) {
|
||||||
|
as = as.toLowerCase();
|
||||||
|
}
|
||||||
|
if (isString(bs)) {
|
||||||
|
bs = bs.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
let order = 0;
|
||||||
|
if (as < bs) {
|
||||||
|
order = -1;
|
||||||
|
}
|
||||||
|
else if (bs > as) {
|
||||||
|
order = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return restParams._sortDir === SortDirection.DESCENDING ? -order : order;
|
||||||
|
});
|
||||||
|
|
||||||
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageEntities(entities: ArrayLike<Entity>, restParams: RestParams) {
|
export function getPageEntities(entities: Entity[], restParams: RestParams) {
|
||||||
const page = restParams._page;
|
const page = restParams._page;
|
||||||
const perPage = restParams._perPage;
|
const perPage = restParams._perPage;
|
||||||
|
|
||||||
return _.slice(entities, (page - 1) * perPage, page * perPage);
|
return entities.slice((page - 1) * perPage, page * perPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {filterCondition as whereCondition};
|
export {filterCondition as whereCondition};
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
|
import {MAC} from "../types";
|
||||||
|
|
||||||
export function normalizeString (str: string): string {
|
export function normalizeString(str: string): string {
|
||||||
return _.isString(str) ? str.trim().replace(/\s+/g, ' ') : str;
|
return _.isString(str) ? str.trim().replace(/\s+/g, ' ') : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeMac (mac: string): string {
|
export function normalizeMac(mac: MAC): MAC {
|
||||||
// parts only contains values at odd indexes
|
// parts only contains values at odd indexes
|
||||||
const parts = mac.toUpperCase().replace(/:/g, '').split(/([A-F0-9]{2})/);
|
const parts = mac.toUpperCase().replace(/[-:]/g, '').split(/([A-F0-9]{2})/);
|
||||||
|
|
||||||
const macParts = [];
|
const macParts = [];
|
||||||
|
|
||||||
|
@ -14,10 +15,10 @@ export function normalizeMac (mac: string): string {
|
||||||
macParts.push(parts[i]);
|
macParts.push(parts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return macParts.join(':');
|
return macParts.join(':') as MAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseInteger (str: string): number {
|
export function parseInteger(str: string): number {
|
||||||
const parsed = _.parseInt(str, 10);
|
const parsed = _.parseInt(str, 10);
|
||||||
if (parsed.toString() === str) {
|
if (parsed.toString() === str) {
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|
|
@ -40,7 +40,7 @@ const CONSTRAINTS = {
|
||||||
},
|
},
|
||||||
coords: {
|
coords: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
regex: /^(-?[0-9]{1,3}(\.[0-9]{1,15})? -?[0-9]{1,3}(\.[0-9]{1,15})?)$/,
|
regex: /^([a-f0-9]{12}|([a-f0-9]{2}:){5}[a-f0-9]{2}|([a-f0-9]{2}-){5}[a-f0-9]{2})$/i,
|
||||||
optional: true
|
optional: true
|
||||||
},
|
},
|
||||||
monitoring: {
|
monitoring: {
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
},
|
},
|
||||||
mac: {
|
mac: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
regex: /^([a-f0-9]{12}|([a-f0-9]{2}:){5}[a-f0-9]{2})$/i,
|
regex: /^([a-f0-9]{12}|([a-f0-9]{2}:){5}[a-f0-9]{2}|([a-f0-9]{2}-){5}[a-f0-9]{2})$/i,
|
||||||
optional: false
|
optional: false
|
||||||
},
|
},
|
||||||
coords: {
|
coords: {
|
||||||
|
|
Loading…
Reference in a new issue