Skip to content

Commit 947df57

Browse files
authored
Merge pull request #66 from skbkontur/remove-qs
remove qs & decimal.js from dependencies
2 parents 60725ca + e7d2c29 commit 947df57

17 files changed

+219
-497
lines changed

db-viewer-ui/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,8 @@
5151
"@skbkontur/react-ui-validations": "^1.8.3",
5252
"copy-to-clipboard": "^3.3.1",
5353
"date-fns": "^2.29.1",
54-
"decimal.js": "^10.2.1",
5554
"lodash": "^4.17.20",
56-
"qs": "^6.9.6",
57-
"tslib": "^2.3.0",
58-
"whatwg-fetch": "^3.5.0"
55+
"tslib": "^2.3.0"
5956
},
6057
"devDependencies": {
6158
"@babel/core": "^7.14.8",

db-viewer-ui/src/Containers/ObjectDetailsContainer.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import TrashIcon from "@skbkontur/react-icons/Trash";
33
import { ColumnStack, Fit, RowStack } from "@skbkontur/react-stack-layout";
44
import { Link } from "@skbkontur/react-ui";
55
import get from "lodash/get";
6-
import qs from "qs";
76
import React from "react";
87
import { RouteComponentProps, withRouter } from "react-router";
98

@@ -76,10 +75,10 @@ class ObjectDetailsContainerInternal extends React.Component<ObjectDetailsProps,
7675
}
7776

7877
public getConditions(): Condition[] {
79-
const query = qs.parse(this.props.objectQuery.replace(/^\?/, ""));
80-
return Object.keys(query).map(x => ({
81-
path: x,
82-
value: String(query[x]),
78+
const query = new URLSearchParams(this.props.objectQuery);
79+
return [...query.entries()].map(x => ({
80+
path: x[0],
81+
value: x[1],
8382
operator: ObjectFieldFilterOperator.Equals,
8483
}));
8584
}

db-viewer-ui/src/Containers/ObjectTableContainer.tsx

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ColumnStack, Fit, RowStack } from "@skbkontur/react-stack-layout";
22
import { Link, Loader, Paging } from "@skbkontur/react-ui";
33
import isEqual from "lodash/isEqual";
4-
import qs from "qs";
54
import React from "react";
65
import { RouteComponentProps, withRouter } from "react-router";
76

@@ -19,10 +18,8 @@ import { SearchResult } from "../Domain/Api/DataTypes/SearchResult";
1918
import { IDbViewerApi } from "../Domain/Api/DbViewerApi";
2019
import { ICustomRenderer } from "../Domain/Objects/CustomRenderer";
2120
import { ObjectSearchQuery } from "../Domain/Objects/ObjectSearchQuery";
22-
import { ConditionsMapper, ObjectSearchQueryUtils, SortMapper } from "../Domain/Objects/ObjectSearchQueryUtils";
21+
import { ObjectSearchQueryMapping } from "../Domain/Objects/ObjectSearchQueryMapping";
2322
import { PropertyMetaInformationUtils } from "../Domain/Objects/PropertyMetaInformationUtils";
24-
import { QueryStringMapping } from "../Domain/QueryStringMapping/QueryStringMapping";
25-
import { QueryStringMappingBuilder } from "../Domain/QueryStringMapping/QueryStringMappingBuilder";
2623
import { RouteUtils } from "../Domain/Utils/RouteUtils";
2724

2825
interface ObjectTableProps extends RouteComponentProps {
@@ -48,14 +45,6 @@ interface ObjectTableState {
4845
downloadCount?: CountResult;
4946
}
5047

51-
const objectsQueryMapping: QueryStringMapping<ObjectSearchQuery> = new QueryStringMappingBuilder<ObjectSearchQuery>()
52-
.mapToInteger(x => x.count, "count", 20)
53-
.mapToInteger(x => x.offset, "offset", 0)
54-
.mapToStringArray(x => x.hiddenColumns, "hiddenColumns", [])
55-
.mapTo(x => x.sorts, new SortMapper("sort"))
56-
.mapTo(x => x.conditions, new ConditionsMapper(["sort", "count", "offset", "hiddenColumns", "countLimit"]))
57-
.build();
58-
5948
function getDefaultQuery(): ObjectSearchQuery {
6049
return {
6150
conditions: [],
@@ -197,8 +186,11 @@ class ObjectTableContainerInternal extends React.Component<ObjectTableProps, Obj
197186
private checkForNecessityLoad(prevQuery: string): boolean {
198187
const { urlQuery } = this.props;
199188
if (prevQuery !== urlQuery) {
200-
const prevState = ObjectSearchQueryUtils.normalize(this.parseQuery(prevQuery), !this.state.objects?.count);
201-
const nextState = ObjectSearchQueryUtils.normalize(this.parseQuery(urlQuery), !this.state.objects?.count);
189+
const prevState = ObjectSearchQueryMapping.normalize(
190+
this.parseQuery(prevQuery),
191+
!this.state.objects?.count
192+
);
193+
const nextState = ObjectSearchQueryMapping.normalize(this.parseQuery(urlQuery), !this.state.objects?.count);
202194
return !isEqual(nextState, prevState);
203195
}
204196
return false;
@@ -295,7 +287,7 @@ class ObjectTableContainerInternal extends React.Component<ObjectTableProps, Obj
295287
query[prop.name] = item[prop.name];
296288
}
297289
}
298-
return RouteUtils.goTo(this.props.path, `details?${qs.stringify(query)}`);
290+
return RouteUtils.goTo(this.props.path, `details?${new URLSearchParams(query)}`);
299291
};
300292

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

438430
private getQuery(overrides: Partial<ObjectSearchQuery> = {}): string {
439-
const { query } = this.state;
431+
const { query, metaInformation } = this.state;
440432
const { path } = this.props;
441-
return path + objectsQueryMapping.stringify({ ...query, ...overrides });
433+
let properties: PropertyMetaInformation[] = [];
434+
if (metaInformation?.typeMetaInformation?.properties) {
435+
properties = PropertyMetaInformationUtils.getProperties(metaInformation.typeMetaInformation.properties);
436+
}
437+
return (
438+
path +
439+
ObjectSearchQueryMapping.stringify(
440+
{ ...query, ...overrides },
441+
properties.map(x => x.name)
442+
)
443+
);
442444
}
443445

444446
private parseQuery(urlQuery: string): ObjectSearchQuery {
445-
const query = objectsQueryMapping.parse(urlQuery);
446-
query.conditions = query.conditions || [];
447-
query.sorts = query.sorts || [];
448-
return query;
447+
const { metaInformation } = this.state;
448+
let properties: PropertyMetaInformation[] = [];
449+
if (metaInformation?.typeMetaInformation?.properties) {
450+
properties = PropertyMetaInformationUtils.getProperties(metaInformation.typeMetaInformation.properties);
451+
}
452+
return ObjectSearchQueryMapping.parse(
453+
urlQuery,
454+
properties.map(x => x.name)
455+
);
449456
}
450457

451458
private readonly goToPage = (page: number) => {

db-viewer-ui/src/Domain/ApiBase/ApiBase.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { ApiError } from "./ApiError";
2-
import "whatwg-fetch";
32

43
interface ParamsMap {
54
[key: string]: null | undefined | number | string | any[] | boolean;
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { Condition } from "../Api/DataTypes/Condition";
2+
import { Sort } from "../Api/DataTypes/Sort";
3+
import { StringUtils } from "../Utils/StringUtils";
4+
5+
import { ObjectSearchQuery } from "./ObjectSearchQuery";
6+
import {
7+
convertOperationToString,
8+
convertSortToString,
9+
convertStringToOperation,
10+
convertStringToSort,
11+
} from "./OperationsConverter";
12+
13+
interface SearchQueryStringified {
14+
conditions: string;
15+
sorts: string;
16+
count: string;
17+
offset: string;
18+
hiddenColumns?: string;
19+
shownColumns?: string;
20+
}
21+
22+
export class ObjectSearchQueryMapping {
23+
public static normalize(query: ObjectSearchQuery, removeCounts: boolean): ObjectSearchQuery {
24+
return {
25+
...query,
26+
count: removeCounts ? 0 : query.count,
27+
offset: removeCounts ? 0 : query.offset,
28+
hiddenColumns: [],
29+
};
30+
}
31+
32+
public static parse(query: string, columns: string[]): ObjectSearchQuery {
33+
const url = new URLSearchParams(query);
34+
const conditions = url.get("conditions");
35+
const sorts = url.get("sorts");
36+
const count = url.get("count");
37+
const offset = url.get("offset");
38+
const hiddenColumns = url.get("hiddenColumns");
39+
const shownColumns = url.get("shownColumns");
40+
41+
return {
42+
conditions: parseConditions(conditions, columns),
43+
sorts: parseSorts(sorts),
44+
count: count ? parseInt(count) : 20,
45+
offset: offset ? parseInt(offset) : 0,
46+
hiddenColumns: parseHiddenColumns(shownColumns, hiddenColumns, columns),
47+
};
48+
}
49+
50+
public static stringify(query: ObjectSearchQuery, columns: string[]): string {
51+
let params: Partial<SearchQueryStringified> = {};
52+
if (query.conditions.length !== 0) {
53+
params.conditions = stringifyConditions(query.conditions);
54+
}
55+
if (query.sorts.length !== 0) {
56+
params.sorts = stringifySorts(query.sorts);
57+
}
58+
if (query.count !== 20) {
59+
params.count = query.count.toString();
60+
}
61+
if (query.offset !== 0) {
62+
params.offset = query.offset.toString();
63+
}
64+
if (query.hiddenColumns.length !== 0) {
65+
params = { ...params, ...stringifyHiddenColumns(query.hiddenColumns, columns) };
66+
}
67+
return `?${new URLSearchParams(params)}`;
68+
}
69+
}
70+
71+
const outerSep = ";";
72+
const innerSep = ":";
73+
74+
/*
75+
* note: Single condition has format path:operator:value;
76+
* complicated parse function to handle edge cases
77+
* eg. conditions = `Id:=:ab:cd:e;f;g;ScopeId:=:a;b`, columns = ["Id", "ScopeId"]
78+
* should return [
79+
* { path: "Id", operator: Equals, value: "ab:cd:e;f;g" },
80+
* { path: "ScopeId", operator: Equals, value: "a;b" },
81+
* ]
82+
*/
83+
function parseConditions(conditions: string | null, columns: string[]): Condition[] {
84+
if (StringUtils.isNullOrWhitespace(conditions)) {
85+
return [];
86+
}
87+
const result: Condition[] = [];
88+
let current = 0;
89+
let next = 0;
90+
while ((next = conditions.indexOf(outerSep, next + 1)) !== -1) {
91+
const nextPath = conditions.substring(next + 1, conditions.indexOf(innerSep, next + 1));
92+
if (columns.indexOf(nextPath) !== -1) {
93+
result.push(parseCondition(conditions, current, next));
94+
current = next + 1;
95+
}
96+
}
97+
if (current >= 0 && current < conditions.length) {
98+
result.push(parseCondition(conditions, current));
99+
}
100+
return result;
101+
}
102+
103+
function parseCondition(conditions: string, start: number, end?: number): Condition {
104+
const sep1 = conditions.indexOf(innerSep, start);
105+
const sep2 = conditions.indexOf(innerSep, sep1 + 1);
106+
return {
107+
path: conditions.substring(start, sep1),
108+
operator: convertOperationToString(conditions.substring(sep1 + 1, sep2)),
109+
value: conditions.substring(sep2 + 1, end),
110+
};
111+
}
112+
113+
function parseSorts(sorts: string | null): Sort[] {
114+
if (StringUtils.isNullOrWhitespace(sorts)) {
115+
return [];
116+
}
117+
return sorts.split(outerSep).map(s => {
118+
const [path, order] = s.split(innerSep);
119+
return {
120+
path: path,
121+
sortOrder: convertStringToSort(order),
122+
};
123+
});
124+
}
125+
126+
function parseHiddenColumns(shownColumns: string | null, hiddenColumns: string | null, columns: string[]): string[] {
127+
if (shownColumns != null) {
128+
const cols = shownColumns.split(outerSep);
129+
return columns.filter(c => cols.indexOf(c) === -1);
130+
} else if (hiddenColumns != null) {
131+
return hiddenColumns.split(outerSep);
132+
}
133+
return [];
134+
}
135+
136+
function stringifyConditions(conditions: Condition[]): string {
137+
return conditions.map(c => [c.path, convertStringToOperation(c.operator), c.value].join(innerSep)).join(outerSep);
138+
}
139+
140+
function stringifySorts(sorts: Sort[]): string {
141+
return sorts.map(s => [s.path, convertSortToString(s.sortOrder)].join(innerSep)).join(outerSep);
142+
}
143+
144+
function stringifyHiddenColumns(hiddenColumns: string[], columns: string[]): Partial<SearchQueryStringified> {
145+
const result: Partial<SearchQueryStringified> = {};
146+
if (hiddenColumns.length >= columns.length - hiddenColumns.length) {
147+
result.shownColumns = columns.filter(c => hiddenColumns.indexOf(c) === -1).join(outerSep);
148+
} else {
149+
result.hiddenColumns = hiddenColumns.join(outerSep);
150+
}
151+
return result;
152+
}

db-viewer-ui/src/Domain/Objects/ObjectSearchQueryUtils.tsx

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)