Skip to content

Commit

Permalink
disallow invalid cql requests on front
Browse files Browse the repository at this point in the history
  • Loading branch information
fakefeik committed Dec 11, 2022
1 parent 027eeab commit 9c78556
Show file tree
Hide file tree
Showing 15 changed files with 404 additions and 47 deletions.
85 changes: 79 additions & 6 deletions DbViewer.Cql/CqlPropertyDescriptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Reflection;

using Cassandra;
using Cassandra.Mapping;
using Cassandra.Mapping.Attributes;

using SkbKontur.DbViewer.Configuration;
Expand All @@ -12,35 +13,107 @@ namespace SkbKontur.DbViewer.Cql
{
public class CqlPropertyDescriptionBuilder : IPropertyDescriptionBuilder
{
private string PrettyPrintPropertyCqlAttributes(PropertyInfo propertyInfo)
{
var columnMeta = $"Тип: {propertyInfo.PropertyType.Name}\n";

var columnAttribute = propertyInfo.GetCustomAttribute<ColumnAttribute>();
if (columnAttribute != null)
{
var type = columnAttribute.Type == null ? string.Empty : $", Type = typeof({columnAttribute.Type.Name})";
columnMeta += $"[Column(\"{columnAttribute.Name}\"{type})]\n";
}

var partitionKeyAttribute = propertyInfo.GetCustomAttribute<PartitionKeyAttribute>();
if (partitionKeyAttribute != null)
{
columnMeta += $"[PartitionKey({partitionKeyAttribute.Index})]";
}

var clusteringKeyAttribute = propertyInfo.GetCustomAttribute<ClusteringKeyAttribute>();
if (clusteringKeyAttribute != null)
{
var sort = clusteringKeyAttribute.ClusteringSortOrder == SortOrder.Unspecified ? null : $", SortOrder.{clusteringKeyAttribute.ClusteringSortOrder}";
var name = string.IsNullOrEmpty(clusteringKeyAttribute.Name) ? null : $", Name = \"{clusteringKeyAttribute.Name}\"";
columnMeta += $"[ClusteringKey({clusteringKeyAttribute.Index}{sort}{name})]";
}

return columnMeta;
}

public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo)
{
var result = new PropertyMetaInformation
{
Name = propertyInfo.Name,
Meta = PrettyPrintPropertyCqlAttributes(propertyInfo),
RequiredForFilter = Array.Empty<FilterRequirement>(),
RequiredForSort = new SortRequirements
{
RequiredFilters = Array.Empty<FilterRequirement>(),
RequiredSorts = Array.Empty<string>(),
},
};

if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(PartitionKeyAttribute)))
var partitionKeys = propertyInfo.ReflectedType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(x => (Property : x, Attribute : x.GetCustomAttribute<PartitionKeyAttribute>()))
.Where(x => x.Attribute != null)
.OrderBy(x => x.Attribute.Index)
.ToArray();
var partitionKeyAttribute = propertyInfo.GetCustomAttribute<PartitionKeyAttribute>();
if (partitionKeyAttribute != null)
{
result.IsSearchable = true;
result.IsIdentity = true;
result.IsRequired = true;
result.AvailableFilters = new[] {ObjectFieldFilterOperator.Equals};
result.AvailableFilters = equals;
}

if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(ClusteringKeyAttribute)))
var clusteringKeys = propertyInfo.ReflectedType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(x => (Property : x, Attribute : x.GetCustomAttribute<ClusteringKeyAttribute>()))
.Where(x => x.Attribute != null)
.OrderBy(x => x.Attribute.Index)
.ToArray();
var clusteringKeyAttribute = propertyInfo.GetCustomAttribute<ClusteringKeyAttribute>();
if (clusteringKeyAttribute != null)
{
result.IsSearchable = true;
result.IsIdentity = true;
result.IsSortable = true;
result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType)
? new[] {ObjectFieldFilterOperator.Equals}
: defaultFilters;
result.AvailableFilters = specialTypes.Contains(propertyInfo.PropertyType) ? equals : defaultFilters;

result.RequiredForFilter =
partitionKeys
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
.Concat(
clusteringKeys
.Where(x => x.Attribute.Index < clusteringKeyAttribute.Index)
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
)
.ToArray();

result.RequiredForSort = new SortRequirements
{
RequiredFilters =
partitionKeys
.Select(x => new FilterRequirement {AvailableFilters = equals, PropertyName = x.Property.Name})
.ToArray(),
OneDirectionSort = true,
RequiredSorts =
clusteringKeys
.Where(x => x.Attribute.Index < clusteringKeyAttribute.Index)
.Select(x => x.Property.Name)
.ToArray(),
};
}

return result;
}

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

private static readonly ObjectFieldFilterOperator[] defaultFilters =
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo)
var result = new PropertyMetaInformation
{
Name = propertyInfo.Name,
RequiredForFilter = Array.Empty<FilterRequirement>(),
RequiredForSort = new SortRequirements
{
RequiredFilters = Array.Empty<FilterRequirement>(),
RequiredSorts = Array.Empty<string>(),
},
};

if (propertyInfo.CustomAttributes.Any(x => x.AttributeType == typeof(TPrimaryKey)))
Expand Down
6 changes: 6 additions & 0 deletions DbViewer.TestApi/Impl/SamplePropertyDescriptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public PropertyMetaInformation Build(PropertyInfo propertyInfo, Type typeInfo)
var result = new PropertyMetaInformation
{
Name = propertyInfo.Name,
RequiredForFilter = Array.Empty<FilterRequirement>(),
RequiredForSort = new SortRequirements
{
RequiredFilters = Array.Empty<FilterRequirement>(),
RequiredSorts = Array.Empty<string>(),
}
};
var indexed = propertyInfo.GetCustomAttribute(typeof(IndexedAttribute)) != null;
var required = propertyInfo.GetCustomAttribute(typeof(RequiredAttribute)) != null;
Expand Down
13 changes: 13 additions & 0 deletions DbViewer/DataTypes/FilterRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;

namespace SkbKontur.DbViewer.DataTypes
{
public class FilterRequirement
{
[JsonProperty("availableFilters")]
public ObjectFieldFilterOperator[] AvailableFilters { get; set; }

[JsonProperty("propertyName")]
public string PropertyName { get; set; }
}
}
9 changes: 9 additions & 0 deletions DbViewer/DataTypes/PropertyMetaInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ public class PropertyMetaInformation
[JsonProperty("availableValues")]
public string[] AvailableValues { get; set; }

[JsonProperty("requiredForFilter")]
public FilterRequirement[] RequiredForFilter { get; set; }

[JsonProperty("requiredForSort")]
public SortRequirements RequiredForSort { get; set; }

[JsonProperty("meta")]
public string? Meta { get; set; }

[JsonProperty("type")]
public TypeMetaInformation Type { get; set; }
}
Expand Down
16 changes: 16 additions & 0 deletions DbViewer/DataTypes/SortRequirements.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Newtonsoft.Json;

namespace SkbKontur.DbViewer.DataTypes
{
public class SortRequirements
{
[JsonProperty("requiredFilters")]
public FilterRequirement[] RequiredFilters { get; set; }

[JsonProperty("oneDirectionSort")]
public bool OneDirectionSort { get; set; }

[JsonProperty("requiredSorts")]
public string[] RequiredSorts { get; set; }
}
}
9 changes: 8 additions & 1 deletion DbViewer/Helpers/PropertyHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,19 @@ private static PropertyMetaInformation BuildPropertyInfo(object? @object, Proper
{
Name = propertyInfo.Name,
AvailableFilters = propertyDescription.AvailableFilters,
AvailableValues = underlyingType.IsEnum ? Enum.GetNames(underlyingType) : new string[0],
AvailableValues = underlyingType.IsEnum ? Enum.GetNames(underlyingType) : Array.Empty<string>(),
IsEditable = propertyInfo.SetMethod != null,
IsIdentity = propertyDescription.IsIdentity,
IsRequired = propertyDescription.IsRequired,
IsSearchable = propertyDescription.IsSearchable,
IsSortable = propertyDescription.IsSortable,
RequiredForFilter = propertyDescription.RequiredForFilter ?? Array.Empty<FilterRequirement>(),
RequiredForSort = propertyDescription.RequiredForSort ?? new SortRequirements
{
RequiredFilters = Array.Empty<FilterRequirement>(),
RequiredSorts = Array.Empty<string>(),
},
Meta = propertyDescription.Meta,
Type = BuildTypeMetaInformation(objectProperty, propertyType, originalPropertyType, propertyDescriptionBuilder, @object == null ? null : propertyConfigurationProvider, types),
};
}
Expand Down
22 changes: 19 additions & 3 deletions db-viewer-ui/src/Components/FormRow/FormRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Fit, Fixed, RowStack } from "@skbkontur/react-stack-layout";
import { ThemeContext } from "@skbkontur/react-ui";
import { Hint, ThemeContext } from "@skbkontur/react-ui";
import React from "react";

import { jsStyles } from "./FormRow.styles";
Expand All @@ -8,14 +8,30 @@ export interface FormRowProps {
caption?: string | JSX.Element;
captionWidth?: number;
children?: React.ReactNode;
hint?: null | string;
}

export function FormRow({ caption, captionWidth, children }: FormRowProps) {
export function FormRow({ caption, captionWidth, hint, children }: FormRowProps): JSX.Element {
const theme = React.useContext(ThemeContext);

const hintElement = hint ? (
<div style={{ textAlign: "start" }}>
{hint.split("\n").map(x => (
<div key={x}>{x}</div>
))}
</div>
) : null;

return (
<RowStack gap={2}>
<Fixed data-tid="FormCaption" className={jsStyles.caption(theme)} width={captionWidth || 240}>
{caption}
{hintElement ? (
<Hint maxWidth={400} text={hintElement}>
{caption}
</Hint>
) : (
caption
)}
</Fixed>
<Fit>{children}</Fit>
</RowStack>
Expand Down
Loading

0 comments on commit 9c78556

Please sign in to comment.