Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b0133640f | ||
|
|
0600d3deaa | ||
|
|
90f8aedc3b |
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -493,7 +493,7 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "canvas"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -585,7 +585,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -641,7 +641,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"prost 0.13.5",
|
||||
"prost-build 0.14.1",
|
||||
@@ -3116,7 +3116,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "search"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
@@ -3215,7 +3215,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "server"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
@@ -3252,6 +3252,7 @@ dependencies = [
|
||||
"tonic-reflection",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"unicode-width 0.2.0",
|
||||
"uuid",
|
||||
"validator",
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@ resolver = "2"
|
||||
[workspace.package]
|
||||
# TODO: idk how to do the name, fix later
|
||||
# name = "komp_ac"
|
||||
version = "0.6.3"
|
||||
version = "0.6.7"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Filip Priečinský <filippriec@gmail.com>"]
|
||||
|
||||
2
canvas
2
canvas
Submodule canvas updated: abbda5b7a9...d6e8ff58d5
2
client
2
client
Submodule client updated: ab990ac128...14a8b4ffbe
@@ -24,6 +24,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
".komp_ac.table_validation.PatternRule",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.PatternPosition",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.CharacterConstraint",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.PatternRules",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
@@ -32,6 +40,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
".komp_ac.table_validation.CustomFormatter",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.FormatterOption",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.AllowedValues",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.UpdateFieldValidationRequest",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
@@ -40,11 +56,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
".komp_ac.table_validation.UpdateFieldValidationResponse",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.ReplaceTableValidationRequest",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.ReplaceTableValidationResponse",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
)
|
||||
// Enum -> readable strings in JSON ("BYTES", "DISPLAY_WIDTH")
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.CountMode",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)] #[serde(rename_all = \"SCREAMING_SNAKE_CASE\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.PatternPositionKind",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)] #[serde(rename_all = \"SCREAMING_SNAKE_CASE\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_validation.CharacterConstraintKind",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)] #[serde(rename_all = \"SCREAMING_SNAKE_CASE\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_definition.ColumnDefinition",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]",
|
||||
@@ -61,6 +93,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
".komp_ac.table_definition.TableDefinitionResponse",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]"
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_definition.GetColumnAliasRenameHistoryRequest",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]"
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_definition.ColumnAliasRenameHistoryEntry",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]"
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_definition.GetColumnAliasRenameHistoryResponse",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]"
|
||||
)
|
||||
.type_attribute(
|
||||
".komp_ac.table_definition.RenameColumnAliasRequest",
|
||||
"#[derive(serde::Serialize, serde::Deserialize)]"
|
||||
|
||||
@@ -22,6 +22,9 @@ service TableDefinition {
|
||||
// Pure data retrieval - no business logic.
|
||||
rpc GetProfileDetails(GetProfileDetailsRequest) returns (GetProfileDetailsResponse);
|
||||
|
||||
// Returns the stored rename history for column aliases in one profile.
|
||||
rpc GetColumnAliasRenameHistory(GetColumnAliasRenameHistoryRequest) returns (GetColumnAliasRenameHistoryResponse);
|
||||
|
||||
// Renames a user-visible column alias while keeping the physical column unchanged.
|
||||
rpc RenameColumnAlias(RenameColumnAliasRequest) returns (RenameColumnAliasResponse);
|
||||
|
||||
@@ -138,6 +141,31 @@ message GetProfileDetailsResponse {
|
||||
repeated TableDetail tables = 2;
|
||||
}
|
||||
|
||||
// Request to fetch recorded column alias rename history for one profile.
|
||||
message GetColumnAliasRenameHistoryRequest {
|
||||
string profile_name = 1;
|
||||
|
||||
// Optional filter. When omitted, returns all tables in the profile.
|
||||
optional int64 table_definition_id = 2;
|
||||
}
|
||||
|
||||
// One recorded column alias rename.
|
||||
message ColumnAliasRenameHistoryEntry {
|
||||
int64 id = 1;
|
||||
string profile_name = 2;
|
||||
int64 table_definition_id = 3;
|
||||
string table_name = 4;
|
||||
string old_column_name = 5;
|
||||
string new_column_name = 6;
|
||||
string created_at = 7;
|
||||
}
|
||||
|
||||
// Response with stored column alias rename history rows.
|
||||
message GetColumnAliasRenameHistoryResponse {
|
||||
string profile_name = 1;
|
||||
repeated ColumnAliasRenameHistoryEntry entries = 2;
|
||||
}
|
||||
|
||||
// Describes a table with its columns and associated scripts.
|
||||
message TableDetail {
|
||||
string name = 1;
|
||||
|
||||
@@ -2,31 +2,55 @@
|
||||
syntax = "proto3";
|
||||
package komp_ac.table_validation;
|
||||
|
||||
// This proto is the canonical server-side storage and distribution contract for
|
||||
// client validation configuration.
|
||||
//
|
||||
// Design goals:
|
||||
// - The server stores the entire field validation definition in one structured payload.
|
||||
// - Clients fetch the validation rules for a table in one batch and map them to
|
||||
// their local validation/runtime system (for example canvas).
|
||||
// - Common validation must be represented as typed data, not as string mini-languages.
|
||||
//
|
||||
// Important split:
|
||||
// - limits / pattern / allowed_values / required are validation rules.
|
||||
// - mask / formatter are presentation and input-shaping metadata for clients.
|
||||
|
||||
// Request validation rules for a table
|
||||
message GetTableValidationRequest {
|
||||
string profileName = 1;
|
||||
string tableName = 2;
|
||||
}
|
||||
|
||||
// Response with field-level validations; if a field is omitted,
|
||||
// no validation is applied (default unspecified).
|
||||
// Response with field-level validations for the whole table.
|
||||
// If a field is omitted, no validation configuration exists for that field.
|
||||
message TableValidationResponse {
|
||||
repeated FieldValidation fields = 1;
|
||||
}
|
||||
|
||||
// Field-level validation (extensible for future kinds)
|
||||
// Field-level validation definition stored on the server and distributed to clients.
|
||||
message FieldValidation {
|
||||
// MUST match your frontend FormState.dataKey for the column
|
||||
string dataKey = 1;
|
||||
|
||||
// Current: only CharacterLimits. More rules can be added later.
|
||||
// Validation 1: length and counting rules.
|
||||
CharacterLimits limits = 10;
|
||||
// Future expansion:
|
||||
PatternRules pattern = 11; // Validation 2
|
||||
optional CustomFormatter formatter = 14; // Validation 4 – custom formatting logic
|
||||
|
||||
// Validation 2: position-based character constraints.
|
||||
PatternRules pattern = 11;
|
||||
|
||||
// Exact-value whitelist.
|
||||
AllowedValues allowed_values = 12;
|
||||
|
||||
// Client-side hint that this field participates in external/asynchronous validation UI.
|
||||
bool external_validation_enabled = 13;
|
||||
|
||||
// Client-side formatter metadata. This is intentionally data-only, not executable code.
|
||||
optional CustomFormatter formatter = 14;
|
||||
|
||||
// Client-side display mask metadata. The server stores raw data without mask literals.
|
||||
DisplayMask mask = 3;
|
||||
// ExternalValidation external = 13;
|
||||
// CustomFormatter formatter = 14;
|
||||
|
||||
// Field must be provided / treated as required by clients and server enforcement layers.
|
||||
bool required = 4;
|
||||
}
|
||||
|
||||
@@ -38,7 +62,8 @@ enum CountMode {
|
||||
DISPLAY_WIDTH = 3;
|
||||
}
|
||||
|
||||
// Character limit validation (Validation 1)
|
||||
// Character limit validation (Validation 1).
|
||||
// These rules map directly to canvas CharacterLimits.
|
||||
message CharacterLimits {
|
||||
// When zero, the field is considered "not set". If both min/max are zero,
|
||||
// the server should avoid sending this FieldValidation (no validation).
|
||||
@@ -51,39 +76,91 @@ message CharacterLimits {
|
||||
CountMode countMode = 4; // defaults to CHARS if unspecified
|
||||
}
|
||||
|
||||
// Mask for pretty display
|
||||
// Mask for pretty display only.
|
||||
//
|
||||
// This is not a validation rule by itself. It exists so clients can render and
|
||||
// navigate masked input while still storing raw values server-side.
|
||||
message DisplayMask {
|
||||
string pattern = 1; // e.g., "(###) ###-####" or "####-##-##"
|
||||
string input_char = 2; // e.g., "#"
|
||||
optional string template_char = 3; // e.g., "_"
|
||||
}
|
||||
|
||||
// One position‑based validation rule, similar to CharacterFilter + PositionRange
|
||||
message PatternRule {
|
||||
// Range descriptor: how far the rule applies
|
||||
// Examples:
|
||||
// - "0" → Single position 0
|
||||
// - "0-3" → Range 0..3 inclusive
|
||||
// - "from:5" → From position 5 onward
|
||||
// - "0,2,5" → Multiple discrete positions
|
||||
string range = 1;
|
||||
|
||||
// Character filter type, case‑insensitive keywords:
|
||||
// "ALPHABETIC", "NUMERIC", "ALPHANUMERIC",
|
||||
// "ONEOF(<chars>)", "EXACT(:)", "CUSTOM(<name>)"
|
||||
string filter = 2;
|
||||
// Which positions a pattern rule applies to.
|
||||
// This exists instead of a string syntax like "0-3" so the server can validate
|
||||
// the structure directly and clients do not need to parse a DSL.
|
||||
message PatternPosition {
|
||||
PatternPositionKind kind = 1;
|
||||
uint32 single = 2;
|
||||
uint32 start = 3;
|
||||
uint32 end = 4;
|
||||
repeated uint32 positions = 5;
|
||||
}
|
||||
|
||||
enum PatternPositionKind {
|
||||
PATTERN_POSITION_KIND_UNSPECIFIED = 0;
|
||||
PATTERN_POSITION_SINGLE = 1;
|
||||
PATTERN_POSITION_RANGE = 2;
|
||||
PATTERN_POSITION_FROM = 3;
|
||||
PATTERN_POSITION_MULTIPLE = 4;
|
||||
}
|
||||
|
||||
// What type of character constraint a pattern rule applies.
|
||||
// This mirrors the typed character filters used by canvas.
|
||||
message CharacterConstraint {
|
||||
CharacterConstraintKind kind = 1;
|
||||
|
||||
// Used when kind == CHARACTER_CONSTRAINT_EXACT.
|
||||
optional string exact = 2;
|
||||
|
||||
// Used when kind == CHARACTER_CONSTRAINT_ONE_OF.
|
||||
repeated string one_of = 3;
|
||||
|
||||
// Used when kind == CHARACTER_CONSTRAINT_REGEX.
|
||||
optional string regex = 4;
|
||||
}
|
||||
|
||||
enum CharacterConstraintKind {
|
||||
CHARACTER_CONSTRAINT_KIND_UNSPECIFIED = 0;
|
||||
CHARACTER_CONSTRAINT_ALPHABETIC = 1;
|
||||
CHARACTER_CONSTRAINT_NUMERIC = 2;
|
||||
CHARACTER_CONSTRAINT_ALPHANUMERIC = 3;
|
||||
CHARACTER_CONSTRAINT_EXACT = 4;
|
||||
CHARACTER_CONSTRAINT_ONE_OF = 5;
|
||||
CHARACTER_CONSTRAINT_REGEX = 6;
|
||||
}
|
||||
|
||||
// One position-based validation rule, similar to canvas PositionFilter.
|
||||
message PatternRule {
|
||||
PatternPosition position = 1;
|
||||
CharacterConstraint constraint = 2;
|
||||
}
|
||||
|
||||
// Client-side formatter metadata.
|
||||
// The formatter "type" is intended to be resolved by a client-side formatter registry.
|
||||
message CustomFormatter {
|
||||
// Formatter type identifier; handled client‑side.
|
||||
// Examples: "PSCFormatter", "PhoneFormatter", "CreditCardFormatter", "DateFormatter"
|
||||
string type = 1;
|
||||
|
||||
// Optional free‑text note or parameters (e.g. locale, pattern)
|
||||
optional string description = 2;
|
||||
repeated FormatterOption options = 2;
|
||||
optional string description = 3;
|
||||
}
|
||||
|
||||
// Collection of pattern rules for one field
|
||||
message FormatterOption {
|
||||
string key = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
// Exact-value whitelist configuration.
|
||||
// This maps to canvas AllowedValues semantics.
|
||||
message AllowedValues {
|
||||
repeated string values = 1;
|
||||
bool allow_empty = 2;
|
||||
bool case_insensitive = 3;
|
||||
}
|
||||
|
||||
// Collection of pattern rules for one field.
|
||||
message PatternRules {
|
||||
// All rules that make up the validation logic
|
||||
repeated PatternRule rules = 1;
|
||||
@@ -92,11 +169,15 @@ message PatternRules {
|
||||
optional string description = 2;
|
||||
}
|
||||
|
||||
// Service to fetch validations for a table
|
||||
// Service for storing and fetching field-validation definitions.
|
||||
service TableValidationService {
|
||||
rpc GetTableValidation(GetTableValidationRequest) returns (TableValidationResponse);
|
||||
|
||||
// Upsert a single field validation definition.
|
||||
rpc UpdateFieldValidation(UpdateFieldValidationRequest) returns (UpdateFieldValidationResponse);
|
||||
|
||||
// Replace the full validation definition set for a table in one transaction.
|
||||
rpc ReplaceTableValidation(ReplaceTableValidationRequest) returns (ReplaceTableValidationResponse);
|
||||
}
|
||||
|
||||
message UpdateFieldValidationRequest {
|
||||
@@ -110,3 +191,16 @@ message UpdateFieldValidationResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message ReplaceTableValidationRequest {
|
||||
string profileName = 1;
|
||||
string tableName = 2;
|
||||
|
||||
// Full replacement set. Fields omitted here are removed from the stored config.
|
||||
repeated FieldValidation fields = 3;
|
||||
}
|
||||
|
||||
message ReplaceTableValidationResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -125,6 +125,44 @@ pub struct GetProfileDetailsResponse {
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub tables: ::prost::alloc::vec::Vec<TableDetail>,
|
||||
}
|
||||
/// Request to fetch recorded column alias rename history for one profile.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetColumnAliasRenameHistoryRequest {
|
||||
#[prost(string, tag = "1")]
|
||||
pub profile_name: ::prost::alloc::string::String,
|
||||
/// Optional filter. When omitted, returns all tables in the profile.
|
||||
#[prost(int64, optional, tag = "2")]
|
||||
pub table_definition_id: ::core::option::Option<i64>,
|
||||
}
|
||||
/// One recorded column alias rename.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ColumnAliasRenameHistoryEntry {
|
||||
#[prost(int64, tag = "1")]
|
||||
pub id: i64,
|
||||
#[prost(string, tag = "2")]
|
||||
pub profile_name: ::prost::alloc::string::String,
|
||||
#[prost(int64, tag = "3")]
|
||||
pub table_definition_id: i64,
|
||||
#[prost(string, tag = "4")]
|
||||
pub table_name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "5")]
|
||||
pub old_column_name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "6")]
|
||||
pub new_column_name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "7")]
|
||||
pub created_at: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Response with stored column alias rename history rows.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetColumnAliasRenameHistoryResponse {
|
||||
#[prost(string, tag = "1")]
|
||||
pub profile_name: ::prost::alloc::string::String,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub entries: ::prost::alloc::vec::Vec<ColumnAliasRenameHistoryEntry>,
|
||||
}
|
||||
/// Describes a table with its columns and associated scripts.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TableDetail {
|
||||
@@ -383,6 +421,36 @@ pub mod table_definition_client {
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns the stored rename history for column aliases in one profile.
|
||||
pub async fn get_column_alias_rename_history(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GetColumnAliasRenameHistoryRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetColumnAliasRenameHistoryResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/komp_ac.table_definition.TableDefinition/GetColumnAliasRenameHistory",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"komp_ac.table_definition.TableDefinition",
|
||||
"GetColumnAliasRenameHistory",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Renames a user-visible column alias while keeping the physical column unchanged.
|
||||
pub async fn rename_column_alias(
|
||||
&mut self,
|
||||
@@ -486,6 +554,14 @@ pub mod table_definition_server {
|
||||
tonic::Response<super::GetProfileDetailsResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
/// Returns the stored rename history for column aliases in one profile.
|
||||
async fn get_column_alias_rename_history(
|
||||
&self,
|
||||
request: tonic::Request<super::GetColumnAliasRenameHistoryRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetColumnAliasRenameHistoryResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
/// Renames a user-visible column alias while keeping the physical column unchanged.
|
||||
async fn rename_column_alias(
|
||||
&self,
|
||||
@@ -724,6 +800,60 @@ pub mod table_definition_server {
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
"/komp_ac.table_definition.TableDefinition/GetColumnAliasRenameHistory" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct GetColumnAliasRenameHistorySvc<T: TableDefinition>(
|
||||
pub Arc<T>,
|
||||
);
|
||||
impl<
|
||||
T: TableDefinition,
|
||||
> tonic::server::UnaryService<
|
||||
super::GetColumnAliasRenameHistoryRequest,
|
||||
> for GetColumnAliasRenameHistorySvc<T> {
|
||||
type Response = super::GetColumnAliasRenameHistoryResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<
|
||||
super::GetColumnAliasRenameHistoryRequest,
|
||||
>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as TableDefinition>::get_column_alias_rename_history(
|
||||
&inner,
|
||||
request,
|
||||
)
|
||||
.await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = GetColumnAliasRenameHistorySvc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
"/komp_ac.table_definition.TableDefinition/RenameColumnAlias" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct RenameColumnAliasSvc<T: TableDefinition>(pub Arc<T>);
|
||||
|
||||
@@ -7,40 +7,45 @@ pub struct GetTableValidationRequest {
|
||||
#[prost(string, tag = "2")]
|
||||
pub table_name: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Response with field-level validations; if a field is omitted,
|
||||
/// no validation is applied (default unspecified).
|
||||
/// Response with field-level validations for the whole table.
|
||||
/// If a field is omitted, no validation configuration exists for that field.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TableValidationResponse {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub fields: ::prost::alloc::vec::Vec<FieldValidation>,
|
||||
}
|
||||
/// Field-level validation (extensible for future kinds)
|
||||
/// Field-level validation definition stored on the server and distributed to clients.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct FieldValidation {
|
||||
/// MUST match your frontend FormState.dataKey for the column
|
||||
#[prost(string, tag = "1")]
|
||||
pub data_key: ::prost::alloc::string::String,
|
||||
/// Current: only CharacterLimits. More rules can be added later.
|
||||
/// Validation 1: length and counting rules.
|
||||
#[prost(message, optional, tag = "10")]
|
||||
pub limits: ::core::option::Option<CharacterLimits>,
|
||||
/// Future expansion:
|
||||
///
|
||||
/// Validation 2
|
||||
/// Validation 2: position-based character constraints.
|
||||
#[prost(message, optional, tag = "11")]
|
||||
pub pattern: ::core::option::Option<PatternRules>,
|
||||
/// Validation 4 – custom formatting logic
|
||||
/// Exact-value whitelist.
|
||||
#[prost(message, optional, tag = "12")]
|
||||
pub allowed_values: ::core::option::Option<AllowedValues>,
|
||||
/// Client-side hint that this field participates in external/asynchronous validation UI.
|
||||
#[prost(bool, tag = "13")]
|
||||
pub external_validation_enabled: bool,
|
||||
/// Client-side formatter metadata. This is intentionally data-only, not executable code.
|
||||
#[prost(message, optional, tag = "14")]
|
||||
pub formatter: ::core::option::Option<CustomFormatter>,
|
||||
/// Client-side display mask metadata. The server stores raw data without mask literals.
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub mask: ::core::option::Option<DisplayMask>,
|
||||
/// ExternalValidation external = 13;
|
||||
/// CustomFormatter formatter = 14;
|
||||
/// Field must be provided / treated as required by clients and server enforcement layers.
|
||||
#[prost(bool, tag = "4")]
|
||||
pub required: bool,
|
||||
}
|
||||
/// Character limit validation (Validation 1)
|
||||
/// Character limit validation (Validation 1).
|
||||
/// These rules map directly to canvas CharacterLimits.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct CharacterLimits {
|
||||
@@ -57,7 +62,10 @@ pub struct CharacterLimits {
|
||||
#[prost(enumeration = "CountMode", tag = "4")]
|
||||
pub count_mode: i32,
|
||||
}
|
||||
/// Mask for pretty display
|
||||
/// Mask for pretty display only.
|
||||
///
|
||||
/// This is not a validation rule by itself. It exists so clients can render and
|
||||
/// navigate masked input while still storing raw values server-side.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct DisplayMask {
|
||||
@@ -71,24 +79,51 @@ pub struct DisplayMask {
|
||||
#[prost(string, optional, tag = "3")]
|
||||
pub template_char: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// One position‑based validation rule, similar to CharacterFilter + PositionRange
|
||||
/// Which positions a pattern rule applies to.
|
||||
/// This exists instead of a string syntax like "0-3" so the server can validate
|
||||
/// the structure directly and clients do not need to parse a DSL.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PatternPosition {
|
||||
#[prost(enumeration = "PatternPositionKind", tag = "1")]
|
||||
pub kind: i32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub single: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub start: u32,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub end: u32,
|
||||
#[prost(uint32, repeated, tag = "5")]
|
||||
pub positions: ::prost::alloc::vec::Vec<u32>,
|
||||
}
|
||||
/// What type of character constraint a pattern rule applies.
|
||||
/// This mirrors the typed character filters used by canvas.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CharacterConstraint {
|
||||
#[prost(enumeration = "CharacterConstraintKind", tag = "1")]
|
||||
pub kind: i32,
|
||||
/// Used when kind == CHARACTER_CONSTRAINT_EXACT.
|
||||
#[prost(string, optional, tag = "2")]
|
||||
pub exact: ::core::option::Option<::prost::alloc::string::String>,
|
||||
/// Used when kind == CHARACTER_CONSTRAINT_ONE_OF.
|
||||
#[prost(string, repeated, tag = "3")]
|
||||
pub one_of: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
/// Used when kind == CHARACTER_CONSTRAINT_REGEX.
|
||||
#[prost(string, optional, tag = "4")]
|
||||
pub regex: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// One position-based validation rule, similar to canvas PositionFilter.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PatternRule {
|
||||
/// Range descriptor: how far the rule applies
|
||||
/// Examples:
|
||||
/// - "0" → Single position 0
|
||||
/// - "0-3" → Range 0..3 inclusive
|
||||
/// - "from:5" → From position 5 onward
|
||||
/// - "0,2,5" → Multiple discrete positions
|
||||
#[prost(string, tag = "1")]
|
||||
pub range: ::prost::alloc::string::String,
|
||||
/// Character filter type, case‑insensitive keywords:
|
||||
/// "ALPHABETIC", "NUMERIC", "ALPHANUMERIC",
|
||||
/// "ONEOF(<chars>)", "EXACT(:)", "CUSTOM(<name>)"
|
||||
#[prost(string, tag = "2")]
|
||||
pub filter: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub position: ::core::option::Option<PatternPosition>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub constraint: ::core::option::Option<CharacterConstraint>,
|
||||
}
|
||||
/// Client-side formatter metadata.
|
||||
/// The formatter "type" is intended to be resolved by a client-side formatter registry.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CustomFormatter {
|
||||
@@ -96,11 +131,32 @@ pub struct CustomFormatter {
|
||||
/// Examples: "PSCFormatter", "PhoneFormatter", "CreditCardFormatter", "DateFormatter"
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
/// Optional free‑text note or parameters (e.g. locale, pattern)
|
||||
#[prost(string, optional, tag = "2")]
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub options: ::prost::alloc::vec::Vec<FormatterOption>,
|
||||
#[prost(string, optional, tag = "3")]
|
||||
pub description: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// Collection of pattern rules for one field
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct FormatterOption {
|
||||
#[prost(string, tag = "1")]
|
||||
pub key: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub value: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Exact-value whitelist configuration.
|
||||
/// This maps to canvas AllowedValues semantics.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct AllowedValues {
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub values: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub allow_empty: bool,
|
||||
#[prost(bool, tag = "3")]
|
||||
pub case_insensitive: bool,
|
||||
}
|
||||
/// Collection of pattern rules for one field.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PatternRules {
|
||||
@@ -131,6 +187,25 @@ pub struct UpdateFieldValidationResponse {
|
||||
#[prost(string, tag = "2")]
|
||||
pub message: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ReplaceTableValidationRequest {
|
||||
#[prost(string, tag = "1")]
|
||||
pub profile_name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub table_name: ::prost::alloc::string::String,
|
||||
/// Full replacement set. Fields omitted here are removed from the stored config.
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub fields: ::prost::alloc::vec::Vec<FieldValidation>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ReplaceTableValidationResponse {
|
||||
#[prost(bool, tag = "1")]
|
||||
pub success: bool,
|
||||
#[prost(string, tag = "2")]
|
||||
pub message: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Character length counting mode
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
@@ -167,6 +242,90 @@ impl CountMode {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum PatternPositionKind {
|
||||
Unspecified = 0,
|
||||
PatternPositionSingle = 1,
|
||||
PatternPositionRange = 2,
|
||||
PatternPositionFrom = 3,
|
||||
PatternPositionMultiple = 4,
|
||||
}
|
||||
impl PatternPositionKind {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Unspecified => "PATTERN_POSITION_KIND_UNSPECIFIED",
|
||||
Self::PatternPositionSingle => "PATTERN_POSITION_SINGLE",
|
||||
Self::PatternPositionRange => "PATTERN_POSITION_RANGE",
|
||||
Self::PatternPositionFrom => "PATTERN_POSITION_FROM",
|
||||
Self::PatternPositionMultiple => "PATTERN_POSITION_MULTIPLE",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"PATTERN_POSITION_KIND_UNSPECIFIED" => Some(Self::Unspecified),
|
||||
"PATTERN_POSITION_SINGLE" => Some(Self::PatternPositionSingle),
|
||||
"PATTERN_POSITION_RANGE" => Some(Self::PatternPositionRange),
|
||||
"PATTERN_POSITION_FROM" => Some(Self::PatternPositionFrom),
|
||||
"PATTERN_POSITION_MULTIPLE" => Some(Self::PatternPositionMultiple),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum CharacterConstraintKind {
|
||||
Unspecified = 0,
|
||||
CharacterConstraintAlphabetic = 1,
|
||||
CharacterConstraintNumeric = 2,
|
||||
CharacterConstraintAlphanumeric = 3,
|
||||
CharacterConstraintExact = 4,
|
||||
CharacterConstraintOneOf = 5,
|
||||
CharacterConstraintRegex = 6,
|
||||
}
|
||||
impl CharacterConstraintKind {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Unspecified => "CHARACTER_CONSTRAINT_KIND_UNSPECIFIED",
|
||||
Self::CharacterConstraintAlphabetic => "CHARACTER_CONSTRAINT_ALPHABETIC",
|
||||
Self::CharacterConstraintNumeric => "CHARACTER_CONSTRAINT_NUMERIC",
|
||||
Self::CharacterConstraintAlphanumeric => "CHARACTER_CONSTRAINT_ALPHANUMERIC",
|
||||
Self::CharacterConstraintExact => "CHARACTER_CONSTRAINT_EXACT",
|
||||
Self::CharacterConstraintOneOf => "CHARACTER_CONSTRAINT_ONE_OF",
|
||||
Self::CharacterConstraintRegex => "CHARACTER_CONSTRAINT_REGEX",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"CHARACTER_CONSTRAINT_KIND_UNSPECIFIED" => Some(Self::Unspecified),
|
||||
"CHARACTER_CONSTRAINT_ALPHABETIC" => {
|
||||
Some(Self::CharacterConstraintAlphabetic)
|
||||
}
|
||||
"CHARACTER_CONSTRAINT_NUMERIC" => Some(Self::CharacterConstraintNumeric),
|
||||
"CHARACTER_CONSTRAINT_ALPHANUMERIC" => {
|
||||
Some(Self::CharacterConstraintAlphanumeric)
|
||||
}
|
||||
"CHARACTER_CONSTRAINT_EXACT" => Some(Self::CharacterConstraintExact),
|
||||
"CHARACTER_CONSTRAINT_ONE_OF" => Some(Self::CharacterConstraintOneOf),
|
||||
"CHARACTER_CONSTRAINT_REGEX" => Some(Self::CharacterConstraintRegex),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod table_validation_service_client {
|
||||
#![allow(
|
||||
@@ -178,7 +337,7 @@ pub mod table_validation_service_client {
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
/// Service to fetch validations for a table
|
||||
/// Service for storing and fetching field-validation definitions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TableValidationServiceClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
@@ -290,6 +449,7 @@ pub mod table_validation_service_client {
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Upsert a single field validation definition.
|
||||
pub async fn update_field_validation(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::UpdateFieldValidationRequest>,
|
||||
@@ -319,6 +479,36 @@ pub mod table_validation_service_client {
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Replace the full validation definition set for a table in one transaction.
|
||||
pub async fn replace_table_validation(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::ReplaceTableValidationRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::ReplaceTableValidationResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/komp_ac.table_validation.TableValidationService/ReplaceTableValidation",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"komp_ac.table_validation.TableValidationService",
|
||||
"ReplaceTableValidation",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated server implementations.
|
||||
@@ -341,6 +531,7 @@ pub mod table_validation_service_server {
|
||||
tonic::Response<super::TableValidationResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
/// Upsert a single field validation definition.
|
||||
async fn update_field_validation(
|
||||
&self,
|
||||
request: tonic::Request<super::UpdateFieldValidationRequest>,
|
||||
@@ -348,8 +539,16 @@ pub mod table_validation_service_server {
|
||||
tonic::Response<super::UpdateFieldValidationResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
/// Replace the full validation definition set for a table in one transaction.
|
||||
async fn replace_table_validation(
|
||||
&self,
|
||||
request: tonic::Request<super::ReplaceTableValidationRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::ReplaceTableValidationResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
}
|
||||
/// Service to fetch validations for a table
|
||||
/// Service for storing and fetching field-validation definitions.
|
||||
#[derive(Debug)]
|
||||
pub struct TableValidationServiceServer<T> {
|
||||
inner: Arc<T>,
|
||||
@@ -527,6 +726,57 @@ pub mod table_validation_service_server {
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
"/komp_ac.table_validation.TableValidationService/ReplaceTableValidation" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct ReplaceTableValidationSvc<T: TableValidationService>(
|
||||
pub Arc<T>,
|
||||
);
|
||||
impl<
|
||||
T: TableValidationService,
|
||||
> tonic::server::UnaryService<super::ReplaceTableValidationRequest>
|
||||
for ReplaceTableValidationSvc<T> {
|
||||
type Response = super::ReplaceTableValidationResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<super::ReplaceTableValidationRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as TableValidationService>::replace_table_validation(
|
||||
&inner,
|
||||
request,
|
||||
)
|
||||
.await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = ReplaceTableValidationSvc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
_ => {
|
||||
Box::pin(async move {
|
||||
let mut response = http::Response::new(
|
||||
|
||||
2
server
2
server
Submodule server updated: 403785118a...f828b7688a
Reference in New Issue
Block a user