summaryrefslogtreecommitdiffstats
path: root/src/app/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/helpers')
-rw-r--r--src/app/helpers/filter-helpers.ts256
-rw-r--r--src/app/helpers/form-validators.ts32
-rw-r--r--src/app/helpers/helpers.ts76
-rw-r--r--src/app/helpers/listing.ts90
-rw-r--r--src/app/helpers/sorting-helpers.ts59
5 files changed, 513 insertions, 0 deletions
diff --git a/src/app/helpers/filter-helpers.ts b/src/app/helpers/filter-helpers.ts
new file mode 100644
index 0000000..cc9f13e
--- /dev/null
+++ b/src/app/helpers/filter-helpers.ts
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Filter } from 'src/app/helpers/listing';
+
+/**
+ * The JSONPath Filter Operators that are used for comparison (subset of [these](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-FILTER-EX-TABLE)).
+ * For instance:
+ * ```
+ * $.id == 1
+ * $.type like_regex "type_(a|b)"
+ * ```
+ */
+export enum FilterOperator {
+ EQUAL = ' == ',
+ NOT_EQUAL = ' != ',
+ LIKE_REGEX = ' like_regex ',
+ OR = ' || ',
+ EQUAL_IN = ' IN '
+}
+
+/**
+ * Turns a Filter that is not formatted into a valid JSONPath one
+ * @param filter the filter that is not in JSONPath format
+ * @param string pass string: true if you want a return type string
+ * @returns a Filter with formatted: true or a string when parameter string: true
+ */
+export function createJsonPathFilter(filter: Filter): Filter;
+export function createJsonPathFilter(filter: Filter, returnsString: true): string;
+export function createJsonPathFilter(filter: Filter, returnsString = false): Filter | string {
+ // like_regex only works with strings
+ if (filter.operator === FilterOperator.LIKE_REGEX) {
+ if (returnsString) {
+ return createFilter(addFieldPrefix(filter.parameter), filter.operator, toSearchRegex(filter.value.toString()));
+ }
+ return {
+ parameter: addFieldPrefix(filter.parameter),
+ operator: filter.operator,
+ value: toSearchRegex(filter.value.toString()),
+ };
+ }
+ if (typeof filter.value === 'number') {
+ if (returnsString) {
+ return createFilter(addFieldPrefix(filter.parameter), filter.operator, filter.value.toString());
+ }
+ return {
+ parameter: addFieldPrefix(filter.parameter),
+ operator: filter.operator,
+ value: filter.value,
+ };
+ }
+ if (returnsString) {
+ return createFilter(addFieldPrefix(filter.parameter), filter.operator, quote(filter.value));
+ }
+ return {
+ parameter: addFieldPrefix(filter.parameter),
+ operator: filter.operator,
+ value: quote(filter.value),
+ };
+}
+
+/**
+ * Concatenates the provided input into a string.
+ * @param parameter the field to filter
+ * @param value the value it should match
+ * @param operator the operator for comparison (`==`,`!=`,`like_regex`,...)
+ * @param forApi
+ * @returns a concatenated string
+ */
+export function createFilter(parameter: string, operator: FilterOperator, value: string | string[], forApi= false): string {
+ if (forApi && operator === FilterOperator.EQUAL_IN) {
+ const valueArray = Array.isArray(value) ? value : value.split(',');
+ return addParentheses(createOrCondition(composeFilterFromArray(valueArray,parameter,FilterOperator.EQUAL)));
+ }
+ return `${parameter}${operator}${value}`;
+}
+/**
+ * Create array of composed filter
+ * @param value array of strings e.g array of IDs for alarms
+ * @param parameter the field to filter e.g $.id
+ * @param operator the operator for comparison
+ * @returns a string[]
+ */
+function composeFilterFromArray(value: string[], parameter: string, operator: FilterOperator): string[] {
+ return value
+ .reduce((acc: string[], curr) => [...acc, `${parameter}${operator}${curr}`], [])
+}
+/**
+ * Join array of string using '||' operator
+ * @param value string[]
+ * @returns a string
+ */
+function createOrCondition(value: string[]) {
+ return value.join(FilterOperator.OR)
+}
+/**
+ * Wraps a string in parentheses `"string"` -> `("string")`
+ * @param value string to wrap in quotes
+ * @returns a string wrapped in quotes
+ */
+function addParentheses(value: string) {
+ return ` ( ${value} ) `
+}
+/**
+ * Wraps a string in quotes `"string"` -> `"'string'"`
+ * @param value string to wrap in quotes
+ * @returns a string wrapped in quotes
+ */
+export function quote(value: string): string {
+ return `"${value}"`;
+}
+
+/**
+ * Add a `$.` prefix to the provided field
+ * @param value the field to match against
+ * @returns the field with a `$.` prefix
+ */
+export function addFieldPrefix(value: string): string {
+ return `$.${value}`;
+}
+
+/**
+ * Concatenate a list of categories into a regex expression for alternative matches.
+ * I.e `[a,b,c]` -> `"^(a|b|c)$"`
+ * @param categories the alternative values that should be matched
+ * @returns a regex expression of alternative matches
+ */
+export function toCategoricalRegex(categories: string[]): string {
+ return `"^(${categories.join('|')})$"`;
+}
+
+/**
+ * Extracts the list of categories from a given regular expression.
+ * I.e `"^(a|b|c)$"` -> `[a,b,c]`
+ * @param regex a regex expression of alternative matches
+ * @returns the categories from the regex
+ */
+export function fromCategoricalRegex(regex: string): string[] {
+ let strippedRegex = regex.slice(3, regex.length - 3);
+ return strippedRegex.split('|');
+}
+
+/**
+ * Turns the given searchTerm into a case insensitive value to be used with like_regex
+ * I.e `term` -> `"term" flag "i"`
+ * @param searchTerm the search term
+ * @returns the term in the format to be used with like_regex
+ */
+export function toSearchRegex(searchTerm: string): string {
+ return `"${searchTerm}" + flag "i"`;
+}
+
+/**
+ * Strips the value of a like_regex filter off its formatting.
+ * I.e `"value" flag "i"` -> `value`
+ * @param regex the value for the `like_regex` operation
+ * @returns the value without quotes and flags
+ */
+export function fromSearchRegex(regex: string): string {
+ const rawValue = regex?.split(' ')[0];
+ if (rawValue.charAt(0) != '"' || rawValue.charAt(rawValue.length - 1) != '"') {
+ throw new Error('Error while extracting the value from the url. Is it in the correct format?');
+ }
+ return rawValue.substring(1, rawValue.length - 1);
+}
+
+/**
+ * Join the individual filters with `&&` into a long expression
+ * @param filters the array of filter strings
+ * @returns a long filter string chained by `&&`'s
+ */
+export function composeFilter(filters: string[]): string {
+ return filters.join('&&');
+}
+
+/**
+ * Turn the provided string of filters into a Map of filters
+ * @param filter the filter string
+ * @returns a Map of filters
+ */
+export function parseFilterFromUrl(filter: string | null): Map<string, Filter> {
+ if (!filter) {
+ return new Map<string, Filter>();
+ }
+ const filters = filter.split('&&');
+ return splitFilterByOperator(filters);
+}
+
+/**
+ * Parse a list of strings in filter format into a Map of Filters
+ * @param filters list of strings in format `['']`
+ * @returns a map of Filters
+ */
+function splitFilterByOperator(filters: string[]): Map<string, Filter> {
+ const mappedFilters = new Map<string, Filter>();
+ filters
+ .map(filter => {
+ if (filter.includes(FilterOperator.EQUAL_IN)) {
+ const [parameter, value] = filter.split(FilterOperator.EQUAL_IN);
+ return { parameter, value, operator: FilterOperator.EQUAL_IN };
+ }
+ if (filter.includes(FilterOperator.EQUAL)) {
+ const [parameter, value] = filter.split(FilterOperator.EQUAL);
+ return { parameter, value, operator: FilterOperator.EQUAL };
+ }
+ if (filter.includes(FilterOperator.NOT_EQUAL)) {
+ const [parameter, value] = filter.split(FilterOperator.NOT_EQUAL);
+ return { parameter, value, operator: FilterOperator.NOT_EQUAL };
+ }
+ if (filter.includes(FilterOperator.LIKE_REGEX)) {
+ const [parameter, value] = filter.split(FilterOperator.LIKE_REGEX);
+ return { parameter, value, operator: FilterOperator.LIKE_REGEX };
+ }
+ throw new Error('Unsupported operator');
+ })
+ .forEach(item => mappedFilters.set(item.parameter, item));
+ return mappedFilters;
+}
+
+/**
+ * Transforms the map of filters back into a JSONPATH filter string
+ * @param filters
+ * @param forApi
+ * @returns a string containing all filter expressions or undefined if size of the Filter Map is 0
+ */
+export function filterBuilder(filters: Map<string, Filter>, forApi=false): string | undefined {
+ if (filters.size === 0) {
+ return undefined;
+ }
+ const filtersString: string[] = [];
+ for (const [, filter] of filters.entries()) {
+ filtersString.push(
+ createFilter(
+ filter.parameter,
+ filter.operator,
+ filter.value.toString(),
+ forApi),
+ );
+ }
+ return composeFilter(filtersString);
+}
diff --git a/src/app/helpers/form-validators.ts b/src/app/helpers/form-validators.ts
new file mode 100644
index 0000000..6cd3acd
--- /dev/null
+++ b/src/app/helpers/form-validators.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { AbstractControl, ValidationErrors } from '@angular/forms';
+
+export class WhiteSpaceValidator {
+ static noWhiteSpace(control: AbstractControl): ValidationErrors | null {
+ if (control.value !== null) {
+ if ((control.value as string).indexOf(' ') >= 0) {
+ return { noWhiteSpace: true };
+ }
+ return null;
+ }
+ return null;
+ }
+}
diff --git a/src/app/helpers/helpers.ts b/src/app/helpers/helpers.ts
new file mode 100644
index 0000000..7c03dbd
--- /dev/null
+++ b/src/app/helpers/helpers.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { FormArray, FormGroup } from '@angular/forms';
+
+export function isNotUndefined<T>(val: T | undefined): val is T {
+ return val !== undefined;
+}
+
+export function isNotNull<T>(val: T | null): val is T {
+ return val !== null;
+}
+
+export function markAsDirtyAndValidate(formGroup: FormGroup): void {
+ Object.values(formGroup.controls).forEach(control => {
+ control.markAsDirty();
+ control.updateValueAndValidity();
+ });
+}
+
+export function isNullOrUndefined(val: any): boolean {
+ return val === null || val === undefined;
+}
+
+export function isNotNullOrUndefined(val: any): boolean {
+ return val !== null && val !== undefined;
+}
+
+export function isNullOrUndefinedOrEmptyString(val: any): boolean {
+ return val === null || val === undefined || val === '';
+}
+
+export function isEmptyArray(array: any[]):boolean {
+ return !array.length
+}
+
+export function getRandomNumber(min: number, max: number) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+export function areFormControlsValid(form: FormGroup): boolean {
+ const formControls = Object.keys(form.controls)
+ .map(key => form.controls[key])
+ .filter(control => !(control instanceof FormArray));
+ return formControls.find(control => control.invalid && (control.dirty || control.touched)) === undefined;
+}
+
+export function isString(value: any): boolean {
+ return typeof value === 'string' || value instanceof String;
+}
+
+export function resetSelectDefaultValue(cssSelector: string): void {
+ setTimeout(() => {
+ const element = document.querySelector(cssSelector);
+ if (element) {
+ //@ts-ignore
+ document.querySelector(cssSelector)?.selectedIndex = -1;
+ }
+ }, 0);
+}
diff --git a/src/app/helpers/listing.ts b/src/app/helpers/listing.ts
new file mode 100644
index 0000000..66d1ae0
--- /dev/null
+++ b/src/app/helpers/listing.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { Router } from '@angular/router';
+import { filterBuilder, FilterOperator } from 'src/app/helpers/filter-helpers';
+
+/**
+ * A JSONPath filter. One or more `Filter`s can be passed to the `filter=` param of the alarm API.
+ */
+export interface Filter {
+ /**
+ * The field to compare against, i.e `$.type`
+ */
+ parameter: string;
+ /**
+ * The JSONPath Filter Operators that are used for comparison (subset of [these](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-SQLJSON-FILTER-EX-TABLE)).
+ * For instance `==` and `like_regex`:
+ * ```
+ * $.id == 1
+ * $.type like_regex "type_(a|b)"
+ * ```
+ */
+ operator: FilterOperator;
+ /**
+ * The value or pattern that the field should have, i.e `"type_(a|b)"`
+ */
+ value: string | number;
+}
+
+/**
+ * This represents one filter expression for one column.
+ * Use `undefined` to signal removal of the filter.
+ *
+ * Note that parameter needs to be the complete param (i.e `$.column`).
+ * That is necessary because this will be the key when parsing the filter expression into a map.
+ */
+export type ColumnFilter = {
+ parameter: string;
+ filter: Filter | undefined;
+};
+
+export function changePage(router: Router, page: number): void {
+ router.navigate([], { queryParams: { page }, queryParamsHandling: 'merge' });
+}
+
+export function changePageSize(router: Router, pageSize: number): void {
+ router.navigate([], {
+ queryParams: { page: 1, pageSize },
+ queryParamsHandling: 'merge',
+ });
+}
+
+export function changeFilter(router: Router, value: string | undefined): void {
+ router.navigate([], {
+ queryParams: { filter: value ? value.trim() : undefined, page: 1 },
+ queryParamsHandling: 'merge',
+ });
+}
+
+/**
+ * Format a JSONPath filter expression into the format required by the angular router.
+ * Add or update this filter param in the router.
+ * @param router the angular router
+ * @param filters the filter expression in JSONPath format
+ * @param path path for new router link
+ * @param params other queryParams
+ */
+export function changeFiltersInRouter(router: Router, filters: Map<string, Filter>, params?: { [key: string]: any }, path?: any[]): void {
+ const composedFilter = filters.size > 0 ? filterBuilder(filters) : undefined;
+ router.navigate(path ?? [], {
+ queryParams: { filter: composedFilter, ...params, page: 1 },
+ queryParamsHandling: 'merge',
+ });
+}
diff --git a/src/app/helpers/sorting-helpers.ts b/src/app/helpers/sorting-helpers.ts
new file mode 100644
index 0000000..94ec9ef
--- /dev/null
+++ b/src/app/helpers/sorting-helpers.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { Sort, SortDirection } from '../components/shared/sort/sort.component';
+import { Router } from '@angular/router';
+
+export function parseSortStringToSort(str: string): Sort {
+ return str.startsWith('-')
+ ? { parameter: str.split('-')[1], direction: SortDirection.DESC }
+ : { parameter: str, direction: SortDirection.ASC };
+}
+export function applyNewSorting(router: Router, sort: Sort) {
+ if (sort.direction === SortDirection.NONE) {
+ router.navigate([], {
+ queryParams: { page: 1, sort: null },
+ queryParamsHandling: 'merge',
+ });
+ } else {
+ router.navigate([], {
+ queryParams: { page: 1, sort: sort.direction === SortDirection.ASC ? sort.parameter : '-' + sort.parameter },
+ queryParamsHandling: 'merge',
+ });
+ }
+}
+export function parseSortToSortString(sort: Sort): string {
+ if (sort.direction === SortDirection.ASC) {
+ return sort.parameter;
+ }
+ if (sort.direction === SortDirection.DESC) {
+ return '-' + sort.parameter;
+ }
+ return '';
+}
+
+export function parseSortToSortJsonString(sort: Sort): string {
+ if (sort.direction === SortDirection.ASC) {
+ return '$.' + sort.parameter;
+ }
+ if (sort.direction === SortDirection.DESC) {
+ return '-$.' + sort.parameter;
+ }
+ return '';
+}