Skip to content

Commit

Permalink
Merge pull request #66 from skbkontur/remove-qs
Browse files Browse the repository at this point in the history
remove qs & decimal.js from dependencies
  • Loading branch information
fakefeik authored Jan 8, 2023
2 parents 60725ca + e7d2c29 commit 947df57
Show file tree
Hide file tree
Showing 17 changed files with 219 additions and 497 deletions.
5 changes: 1 addition & 4 deletions db-viewer-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@
"@skbkontur/react-ui-validations": "^1.8.3",
"copy-to-clipboard": "^3.3.1",
"date-fns": "^2.29.1",
"decimal.js": "^10.2.1",
"lodash": "^4.17.20",
"qs": "^6.9.6",
"tslib": "^2.3.0",
"whatwg-fetch": "^3.5.0"
"tslib": "^2.3.0"
},
"devDependencies": {
"@babel/core": "^7.14.8",
Expand Down
9 changes: 4 additions & 5 deletions db-viewer-ui/src/Containers/ObjectDetailsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import TrashIcon from "@skbkontur/react-icons/Trash";
import { ColumnStack, Fit, RowStack } from "@skbkontur/react-stack-layout";
import { Link } from "@skbkontur/react-ui";
import get from "lodash/get";
import qs from "qs";
import React from "react";
import { RouteComponentProps, withRouter } from "react-router";

Expand Down Expand Up @@ -76,10 +75,10 @@ class ObjectDetailsContainerInternal extends React.Component<ObjectDetailsProps,
}

public getConditions(): Condition[] {
const query = qs.parse(this.props.objectQuery.replace(/^\?/, ""));
return Object.keys(query).map(x => ({
path: x,
value: String(query[x]),
const query = new URLSearchParams(this.props.objectQuery);
return [...query.entries()].map(x => ({
path: x[0],
value: x[1],
operator: ObjectFieldFilterOperator.Equals,
}));
}
Expand Down
49 changes: 28 additions & 21 deletions db-viewer-ui/src/Containers/ObjectTableContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ColumnStack, Fit, RowStack } from "@skbkontur/react-stack-layout";
import { Link, Loader, Paging } from "@skbkontur/react-ui";
import isEqual from "lodash/isEqual";
import qs from "qs";
import React from "react";
import { RouteComponentProps, withRouter } from "react-router";

Expand All @@ -19,10 +18,8 @@ import { SearchResult } from "../Domain/Api/DataTypes/SearchResult";
import { IDbViewerApi } from "../Domain/Api/DbViewerApi";
import { ICustomRenderer } from "../Domain/Objects/CustomRenderer";
import { ObjectSearchQuery } from "../Domain/Objects/ObjectSearchQuery";
import { ConditionsMapper, ObjectSearchQueryUtils, SortMapper } from "../Domain/Objects/ObjectSearchQueryUtils";
import { ObjectSearchQueryMapping } from "../Domain/Objects/ObjectSearchQueryMapping";
import { PropertyMetaInformationUtils } from "../Domain/Objects/PropertyMetaInformationUtils";
import { QueryStringMapping } from "../Domain/QueryStringMapping/QueryStringMapping";
import { QueryStringMappingBuilder } from "../Domain/QueryStringMapping/QueryStringMappingBuilder";
import { RouteUtils } from "../Domain/Utils/RouteUtils";

interface ObjectTableProps extends RouteComponentProps {
Expand All @@ -48,14 +45,6 @@ interface ObjectTableState {
downloadCount?: CountResult;
}

const objectsQueryMapping: QueryStringMapping<ObjectSearchQuery> = new QueryStringMappingBuilder<ObjectSearchQuery>()
.mapToInteger(x => x.count, "count", 20)
.mapToInteger(x => x.offset, "offset", 0)
.mapToStringArray(x => x.hiddenColumns, "hiddenColumns", [])
.mapTo(x => x.sorts, new SortMapper("sort"))
.mapTo(x => x.conditions, new ConditionsMapper(["sort", "count", "offset", "hiddenColumns", "countLimit"]))
.build();

function getDefaultQuery(): ObjectSearchQuery {
return {
conditions: [],
Expand Down Expand Up @@ -197,8 +186,11 @@ class ObjectTableContainerInternal extends React.Component<ObjectTableProps, Obj
private checkForNecessityLoad(prevQuery: string): boolean {
const { urlQuery } = this.props;
if (prevQuery !== urlQuery) {
const prevState = ObjectSearchQueryUtils.normalize(this.parseQuery(prevQuery), !this.state.objects?.count);
const nextState = ObjectSearchQueryUtils.normalize(this.parseQuery(urlQuery), !this.state.objects?.count);
const prevState = ObjectSearchQueryMapping.normalize(
this.parseQuery(prevQuery),
!this.state.objects?.count
);
const nextState = ObjectSearchQueryMapping.normalize(this.parseQuery(urlQuery), !this.state.objects?.count);
return !isEqual(nextState, prevState);
}
return false;
Expand Down Expand Up @@ -295,7 +287,7 @@ class ObjectTableContainerInternal extends React.Component<ObjectTableProps, Obj
query[prop.name] = item[prop.name];
}
}
return RouteUtils.goTo(this.props.path, `details?${qs.stringify(query)}`);
return RouteUtils.goTo(this.props.path, `details?${new URLSearchParams(query)}`);
};

private readonly handleOpenFilter = () => {
Expand Down Expand Up @@ -436,16 +428,31 @@ class ObjectTableContainerInternal extends React.Component<ObjectTableProps, Obj
}

private getQuery(overrides: Partial<ObjectSearchQuery> = {}): string {
const { query } = this.state;
const { query, metaInformation } = this.state;
const { path } = this.props;
return path + objectsQueryMapping.stringify({ ...query, ...overrides });
let properties: PropertyMetaInformation[] = [];
if (metaInformation?.typeMetaInformation?.properties) {
properties = PropertyMetaInformationUtils.getProperties(metaInformation.typeMetaInformation.properties);
}
return (
path +
ObjectSearchQueryMapping.stringify(
{ ...query, ...overrides },
properties.map(x => x.name)
)
);
}

private parseQuery(urlQuery: string): ObjectSearchQuery {
const query = objectsQueryMapping.parse(urlQuery);
query.conditions = query.conditions || [];
query.sorts = query.sorts || [];
return query;
const { metaInformation } = this.state;
let properties: PropertyMetaInformation[] = [];
if (metaInformation?.typeMetaInformation?.properties) {
properties = PropertyMetaInformationUtils.getProperties(metaInformation.typeMetaInformation.properties);
}
return ObjectSearchQueryMapping.parse(
urlQuery,
properties.map(x => x.name)
);
}

private readonly goToPage = (page: number) => {
Expand Down
1 change: 0 additions & 1 deletion db-viewer-ui/src/Domain/ApiBase/ApiBase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ApiError } from "./ApiError";
import "whatwg-fetch";

interface ParamsMap {
[key: string]: null | undefined | number | string | any[] | boolean;
Expand Down
152 changes: 152 additions & 0 deletions db-viewer-ui/src/Domain/Objects/ObjectSearchQueryMapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Condition } from "../Api/DataTypes/Condition";
import { Sort } from "../Api/DataTypes/Sort";
import { StringUtils } from "../Utils/StringUtils";

import { ObjectSearchQuery } from "./ObjectSearchQuery";
import {
convertOperationToString,
convertSortToString,
convertStringToOperation,
convertStringToSort,
} from "./OperationsConverter";

interface SearchQueryStringified {
conditions: string;
sorts: string;
count: string;
offset: string;
hiddenColumns?: string;
shownColumns?: string;
}

export class ObjectSearchQueryMapping {
public static normalize(query: ObjectSearchQuery, removeCounts: boolean): ObjectSearchQuery {
return {
...query,
count: removeCounts ? 0 : query.count,
offset: removeCounts ? 0 : query.offset,
hiddenColumns: [],
};
}

public static parse(query: string, columns: string[]): ObjectSearchQuery {
const url = new URLSearchParams(query);
const conditions = url.get("conditions");
const sorts = url.get("sorts");
const count = url.get("count");
const offset = url.get("offset");
const hiddenColumns = url.get("hiddenColumns");
const shownColumns = url.get("shownColumns");

return {
conditions: parseConditions(conditions, columns),
sorts: parseSorts(sorts),
count: count ? parseInt(count) : 20,
offset: offset ? parseInt(offset) : 0,
hiddenColumns: parseHiddenColumns(shownColumns, hiddenColumns, columns),
};
}

public static stringify(query: ObjectSearchQuery, columns: string[]): string {
let params: Partial<SearchQueryStringified> = {};
if (query.conditions.length !== 0) {
params.conditions = stringifyConditions(query.conditions);
}
if (query.sorts.length !== 0) {
params.sorts = stringifySorts(query.sorts);
}
if (query.count !== 20) {
params.count = query.count.toString();
}
if (query.offset !== 0) {
params.offset = query.offset.toString();
}
if (query.hiddenColumns.length !== 0) {
params = { ...params, ...stringifyHiddenColumns(query.hiddenColumns, columns) };
}
return `?${new URLSearchParams(params)}`;
}
}

const outerSep = ";";
const innerSep = ":";

/*
* note: Single condition has format path:operator:value;
* complicated parse function to handle edge cases
* eg. conditions = `Id:=:ab:cd:e;f;g;ScopeId:=:a;b`, columns = ["Id", "ScopeId"]
* should return [
* { path: "Id", operator: Equals, value: "ab:cd:e;f;g" },
* { path: "ScopeId", operator: Equals, value: "a;b" },
* ]
*/
function parseConditions(conditions: string | null, columns: string[]): Condition[] {
if (StringUtils.isNullOrWhitespace(conditions)) {
return [];
}
const result: Condition[] = [];
let current = 0;
let next = 0;
while ((next = conditions.indexOf(outerSep, next + 1)) !== -1) {
const nextPath = conditions.substring(next + 1, conditions.indexOf(innerSep, next + 1));
if (columns.indexOf(nextPath) !== -1) {
result.push(parseCondition(conditions, current, next));
current = next + 1;
}
}
if (current >= 0 && current < conditions.length) {
result.push(parseCondition(conditions, current));
}
return result;
}

function parseCondition(conditions: string, start: number, end?: number): Condition {
const sep1 = conditions.indexOf(innerSep, start);
const sep2 = conditions.indexOf(innerSep, sep1 + 1);
return {
path: conditions.substring(start, sep1),
operator: convertOperationToString(conditions.substring(sep1 + 1, sep2)),
value: conditions.substring(sep2 + 1, end),
};
}

function parseSorts(sorts: string | null): Sort[] {
if (StringUtils.isNullOrWhitespace(sorts)) {
return [];
}
return sorts.split(outerSep).map(s => {
const [path, order] = s.split(innerSep);
return {
path: path,
sortOrder: convertStringToSort(order),
};
});
}

function parseHiddenColumns(shownColumns: string | null, hiddenColumns: string | null, columns: string[]): string[] {
if (shownColumns != null) {
const cols = shownColumns.split(outerSep);
return columns.filter(c => cols.indexOf(c) === -1);
} else if (hiddenColumns != null) {
return hiddenColumns.split(outerSep);
}
return [];
}

function stringifyConditions(conditions: Condition[]): string {
return conditions.map(c => [c.path, convertStringToOperation(c.operator), c.value].join(innerSep)).join(outerSep);
}

function stringifySorts(sorts: Sort[]): string {
return sorts.map(s => [s.path, convertSortToString(s.sortOrder)].join(innerSep)).join(outerSep);
}

function stringifyHiddenColumns(hiddenColumns: string[], columns: string[]): Partial<SearchQueryStringified> {
const result: Partial<SearchQueryStringified> = {};
if (hiddenColumns.length >= columns.length - hiddenColumns.length) {
result.shownColumns = columns.filter(c => hiddenColumns.indexOf(c) === -1).join(outerSep);
} else {
result.hiddenColumns = hiddenColumns.join(outerSep);
}
return result;
}
98 changes: 0 additions & 98 deletions db-viewer-ui/src/Domain/Objects/ObjectSearchQueryUtils.tsx

This file was deleted.

Loading

0 comments on commit 947df57

Please sign in to comment.