Merge branch 'main' into new-admin

This commit is contained in:
baldo 2022-07-22 17:19:13 +02:00
commit 6fd4dbb33e
7 changed files with 72 additions and 37 deletions

View file

@ -32,7 +32,7 @@ function getNormalizedNodeData(reqData: any): CreateOrUpdateNode {
_.each(nodeFields, function (field) {
let value = normalizeString(reqData[field]);
if (field === 'mac') {
value = normalizeMac(value);
value = normalizeMac(value as MAC);
}
node[field] = value;
});

View file

@ -63,7 +63,17 @@ export function toNodeTokenResponse(node: StoredNode): NodeTokenResponse {
export function toDomainSpecificNodeResponse(node: StoredNode, nodeStateData: NodeStateData): DomainSpecificNodeResponse {
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,
domain: nodeStateData.domain,
onlineState: nodeStateData.state,

View file

@ -93,6 +93,14 @@ export function isBoolean(arg: unknown): arg is 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[]> {
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.
*/
export type DomainSpecificNodeResponse = NodeResponse & {
export type DomainSpecificNodeResponse = Record<NodeSortField, any> & NodeResponse & {
site?: Site,
domain?: Domain,
onlineState?: OnlineState,

View file

@ -9,7 +9,7 @@ import {
EnumTypeGuard,
EnumValue,
type GenericSortField,
isJSONObject,
isJSONObject, isNumber, isString, isUndefined,
JSONObject,
SortDirection,
TypeGuard
@ -91,17 +91,14 @@ function filterCondition(restParams: RestParams, filterFields: string[]): Filter
};
}
let query = _.join(
_.map(filterFields, function (field) {
return 'LOWER(' + field + ') LIKE ?';
}),
' OR '
);
let query = filterFields
.map(field => 'LOWER(' + field + ') LIKE ?')
.join(' OR ');
query += ' ESCAPE \'\\\'';
const search = '%' + (_.isString(restParams.q) ? escapeForLikePattern(_.toLower(restParams.q.trim())) : '') + '%';
const params = _.times(filterFields.length, _.constant(search));
const search = '%' + (isString(restParams.q) ? escapeForLikePattern(restParams.q.trim().toLowerCase()) : '') + '%';
const params = _.times(filterFields.length, () => search);
return {
query: query,
@ -111,13 +108,14 @@ function filterCondition(restParams: RestParams, filterFields: string[]): Filter
function getConstrainedValues(data: { [key: string]: any }, constraints: Constraints): { [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];
values[key] =
_.isUndefined(value) && key in constraints && !_.isUndefined(constraints[key].default)
isUndefined(value) && key in constraints && !isUndefined(constraints[key].default)
? constraints[key].default
: value;
});
}
return values;
}
@ -176,10 +174,10 @@ export async function getValidRestParams(
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;
if (query) {
query = _.toLower(query.trim());
query = query.trim().toLowerCase();
}
function queryMatches(entity: Entity): boolean {
@ -191,15 +189,15 @@ export function filter<E>(entities: ArrayLike<E>, allowedFilterFields: string[],
return true;
}
let value = entity[field];
if (_.isNumber(value)) {
if (isNumber(value)) {
value = value.toString();
}
if (!_.isString(value) || _.isEmpty(value)) {
if (!isString(value) || _.isEmpty(value)) {
return false;
}
value = _.toLower(value);
value = value.toLowerCase();
if (field === 'mac') {
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 => {
if (_.isUndefined(value)) {
if (isUndefined(value)) {
return true;
}
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;
if (!sortField) {
return entities;
}
const sorted: T[] = _.sortBy(entities, [sortField]);
if (restParams._sortDir === SortDirection.ASCENDING) {
return sorted;
} else {
return _.reverse(sorted);
}
const sorted = entities.slice(0);
sorted.sort((a, b) => {
let as = a[sortField];
let bs = b[sortField];
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 perPage = restParams._perPage;
return _.slice(entities, (page - 1) * perPage, page * perPage);
return entities.slice((page - 1) * perPage, page * perPage);
}
export {filterCondition as whereCondition};

View file

@ -1,12 +1,13 @@
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;
}
export function normalizeMac (mac: string): string {
export function normalizeMac(mac: MAC): MAC {
// 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 = [];
@ -14,10 +15,10 @@ export function normalizeMac (mac: string): string {
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);
if (parsed.toString() === str) {
return parsed;

View file

@ -40,7 +40,7 @@ const CONSTRAINTS = {
},
coords: {
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
},
monitoring: {

View file

@ -38,7 +38,7 @@
},
mac: {
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
},
coords: {