Skip to content

Commit f842364

Browse files
committed
disallow invalid cql requests on front
1 parent 027eeab commit f842364

File tree

14 files changed

+393
-46
lines changed

14 files changed

+393
-46
lines changed

DbViewer.Cql/CqlPropertyDescriptionBuilder.cs

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Reflection;
44

55
using Cassandra;
6+
using Cassandra.Mapping;
67
using Cassandra.Mapping.Attributes;
78

89
using SkbKontur.DbViewer.Configuration;
@@ -12,35 +13,107 @@ namespace SkbKontur.DbViewer.Cql
1213
{
1314
public class CqlPropertyDescriptionBuilder : IPropertyDescriptionBuilder
1415
{
16+
private string PrettyPrintPropertyCqlAttributes(PropertyInfo propertyInfo)
17+
{
18+
var columnMeta = $"Тип: {propertyInfo.PropertyType.Name}\n";
19+
20+
var columnAttribute = propertyInfo.GetCustomAttribute<ColumnAttribute>();
21+
if (columnAttribute != null)
22+
{
23+
var type = columnAttribute.Type == null ? string.Empty : $", Type = typeof({columnAttribute.Type.Name})";
24+
columnMeta += $"[Column(\"{columnAttribute.Name}\"{type})]\n";
25+
}
26+
27+
var partitionKeyAttribute = propertyInfo.GetCustomAttribute<PartitionKeyAttribute>();
28+
if (partitionKeyAttribute != null)
29+
{
30+
columnMeta += $"[PartitionKey({partitionKeyAttribute.Index})]";
31+
}
32+
33+
var clusteringKeyAttribute = propertyInfo.GetCustomAttribute<ClusteringKeyAttribute>();
34+
if (clusteringKeyAttribute != null)
35+
{
36+
var sort = clusteringKeyAttribute.ClusteringSortOrder == SortOrder.Unspecified ? null : $", SortOrder.{clusteringKeyAttribute.ClusteringSortOrder}";
37+
var name = string.IsNullOrEmpty(clusteringKeyAttribute.Name) ? null : $", Name = \"{clusteringKeyAttribute.Name}\"";
38+
columnMeta += $"[ClusteringKey({clusteringKeyAttribute.Index}{sort}{name})]";
39+
}
40+
41+
return columnMeta;
42+
}
43+
1544
public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo)
1645
{
1746
var result = new PropertyMetaInformation
1847
{
1948
Name = propertyInfo.Name,
49+
Meta = PrettyPrintPropertyCqlAttributes(propertyInfo),
50+
RequiredForFilter = Array.Empty<FilterRequirement>(),
51+
RequiredForSort = new SortRequirements
52+
{
53+
RequiredFilters = Array.Empty<FilterRequirement>(),
54+
RequiredSorts = Array.Empty<string>(),
55+
},
2056
};
2157

22-
if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(PartitionKeyAttribute)))
58+
var partitionKeys = propertyInfo.ReflectedType
59+
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
60+
.Select(x => (Property : x, Attribute : x.GetCustomAttribute<PartitionKeyAttribute>()))
61+
.Where(x => x.Attribute != null)
62+
.OrderBy(x => x.Attribute.Index)
63+
.ToArray();
64+
var partitionKeyAttribute = propertyInfo.GetCustomAttribute<PartitionKeyAttribute>();
65+
if (partitionKeyAttribute != null)
2366
{
2467
result.IsSearchable = true;
2568
result.IsIdentity = true;
2669
result.IsRequired = true;
27-
result.AvailableFilters = new[] {ObjectFieldFilterOperator.Equals};
70+
result.AvailableFilters = equals;
2871
}
2972

30-
if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(ClusteringKeyAttribute)))
73+
var clusteringKeys = propertyInfo.ReflectedType
74+
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
75+
.Select(x => (Property : x, Attribute : x.GetCustomAttribute<ClusteringKeyAttribute>()))
76+
.Where(x => x.Attribute != null)
77+
.OrderBy(x => x.Attribute.Index)
78+
.ToArray();
79+
var clusteringKeyAttribute = propertyInfo.GetCustomAttribute<ClusteringKeyAttribute>();
80+
if (clusteringKeyAttribute != null)
3181
{
3282
result.IsSearchable = true;
3383
result.IsIdentity = true;
3484
result.IsSortable = true;
35-
result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType)
36-
? new[] {ObjectFieldFilterOperator.Equals}
37-
: defaultFilters;
85+
result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType) ? equals : defaultFilters;
86+
87+
result.RequiredForFilter =
88+
partitionKeys
89+
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
90+
.Concat(
91+
clusteringKeys
92+
.Where(x => x.Attribute.Index < clusteringKeyAttribute.Index)
93+
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
94+
)
95+
.ToArray();
96+
97+
result.RequiredForSort = new SortRequirements
98+
{
99+
RequiredFilters =
100+
partitionKeys
101+
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
102+
.ToArray(),
103+
OneDirectionSort = true,
104+
RequiredSorts =
105+
clusteringKeys
106+
.Where(x => x.Attribute.Index < clusteringKeyAttribute.Index)
107+
.Select(x => x.Property.Name)
108+
.ToArray(),
109+
};
38110
}
39111

40112
return result;
41113
}
42114

43115
private static readonly Type[] specialTypes = {typeof(TimeUuid), typeof(bool)};
116+
private static readonly ObjectFieldFilterOperator[] equals = {ObjectFieldFilterOperator.Equals};
44117

45118
private static readonly ObjectFieldFilterOperator[] defaultFilters =
46119
{

DbViewer.EntityFramework/EntityFrameworkPropertyDescriptionBuilder.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo)
1616
var result = new PropertyMetaInformation
1717
{
1818
Name = propertyInfo.Name,
19+
RequiredForFilter = Array.Empty<FilterRequirement>(),
20+
RequiredForSort = new SortRequirements
21+
{
22+
RequiredFilters = Array.Empty<FilterRequirement>(),
23+
RequiredSorts = Array.Empty<string>(),
24+
},
1925
};
2026

2127
if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(TPrimaryKey)))
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Newtonsoft.Json;
2+
3+
namespace SkbKontur.DbViewer.DataTypes
4+
{
5+
public class FilterRequirement
6+
{
7+
[JsonProperty("availableFilters")]
8+
public ObjectFieldFilterOperator[] AvailableFilters { get; set; }
9+
10+
[JsonProperty("propertyName")]
11+
public string PropertyName { get; set; }
12+
}
13+
}

DbViewer/DataTypes/PropertyMetaInformation.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ public class PropertyMetaInformation
2828
[JsonProperty("availableValues")]
2929
public string[] AvailableValues { get; set; }
3030

31+
[JsonProperty("requiredForFilter")]
32+
public FilterRequirement[] RequiredForFilter { get; set; }
33+
34+
[JsonProperty("requiredForSort")]
35+
public SortRequirements RequiredForSort { get; set; }
36+
37+
[JsonProperty("meta")]
38+
public string? Meta { get; set; }
39+
3140
[JsonProperty("type")]
3241
public TypeMetaInformation Type { get; set; }
3342
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Newtonsoft.Json;
2+
3+
namespace SkbKontur.DbViewer.DataTypes
4+
{
5+
public class SortRequirements
6+
{
7+
[JsonProperty("requiredFilters")]
8+
public FilterRequirement[] RequiredFilters { get; set; }
9+
10+
[JsonProperty("oneDirectionSort")]
11+
public bool OneDirectionSort { get; set; }
12+
13+
[JsonProperty("requiredSorts")]
14+
public string[] RequiredSorts { get; set; }
15+
}
16+
}

DbViewer/Helpers/PropertyHelpers.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ private static PropertyMetaInformation BuildPropertyInfo(object? @object, Proper
148148
IsRequired = propertyDescription.IsRequired,
149149
IsSearchable = propertyDescription.IsSearchable,
150150
IsSortable = propertyDescription.IsSortable,
151+
RequiredForFilter = propertyDescription.RequiredForFilter,
152+
RequiredForSort = propertyDescription.RequiredForSort,
153+
Meta = propertyDescription.Meta,
151154
Type = BuildTypeMetaInformation(objectProperty, propertyType, originalPropertyType, propertyDescriptionBuilder, @object == null ? null : propertyConfigurationProvider, types),
152155
};
153156
}

db-viewer-ui/src/Components/FormRow/FormRow.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Fit, Fixed, RowStack } from "@skbkontur/react-stack-layout";
2-
import { ThemeContext } from "@skbkontur/react-ui";
2+
import { Hint, ThemeContext } from "@skbkontur/react-ui";
33
import React from "react";
44

55
import { jsStyles } from "./FormRow.styles";
@@ -8,14 +8,30 @@ export interface FormRowProps {
88
caption?: string | JSX.Element;
99
captionWidth?: number;
1010
children?: React.ReactNode;
11+
hint?: null | string;
1112
}
1213

13-
export function FormRow({ caption, captionWidth, children }: FormRowProps) {
14+
export function FormRow({ caption, captionWidth, hint, children }: FormRowProps): JSX.Element {
1415
const theme = React.useContext(ThemeContext);
16+
17+
const hintElement = hint ? (
18+
<div style={{ textAlign: "start" }}>
19+
{hint.split("\n").map(x => (
20+
<div key={x}>{x}</div>
21+
))}
22+
</div>
23+
) : null;
24+
1525
return (
1626
<RowStack gap={2}>
1727
<Fixed data-tid="FormCaption" className={jsStyles.caption(theme)} width={captionWidth || 240}>
18-
{caption}
28+
{hintElement ? (
29+
<Hint maxWidth={400} text={hintElement}>
30+
{caption}
31+
</Hint>
32+
) : (
33+
caption
34+
)}
1935
</Fixed>
2036
<Fit>{children}</Fit>
2137
</RowStack>

0 commit comments

Comments
 (0)