Files
komp_ac/common/proto/tables_data.proto
2025-10-26 22:02:44 +01:00

225 lines
8.2 KiB
Protocol Buffer
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// common/proto/tables_data.proto
syntax = "proto3";
package komp_ac.tables_data;
import "common.proto";
import "google/protobuf/struct.proto";
// Read and write row data for user-defined tables inside profiles (schemas).
// Operations are performed against the physical PostgreSQL table that
// corresponds to the logical table definition and are scoped by profile
// (schema). Deletions are soft (set deleted = true). Typed binding and
// script-based validation are enforced consistently.
service TablesData {
// Insert a new row into a table with strict type binding and script validation.
//
// Behavior:
// - Validates that profile (schema) exists and table is defined for it
// - Validates provided columns exist (user-defined or allowed system/FK columns)
// - For columns targeted by scripts in this table, the client MUST provide the
// value, and it MUST equal the scripts calculated value (compared type-safely)
// - Binds values with correct SQL types, rejects invalid formats/ranges
// - Inserts the row and returns the new id; queues search indexing (best effort)
// - If the physical table is missing but the definition exists, returns INTERNAL
rpc PostTableData(PostTableDataRequest) returns (PostTableDataResponse);
// Update existing row data with strict type binding and script validation.
//
// Behavior:
// - Validates profile and table, and that the record exists
// - If request data is empty, returns success without changing the row
// - For columns targeted by scripts:
// • If included in update, provided value must equal the script result
// • If not included, update must not cause the script result to differ
// from the current stored value; otherwise FAILED_PRECONDITION is returned
// - Binds values with correct SQL types; rejects invalid formats/ranges
// - Updates the row and returns the id; queues search indexing (best effort)
rpc PutTableData(PutTableDataRequest) returns (PutTableDataResponse);
// Soft-delete a single record (sets deleted = true) if it exists and is not already deleted.
//
// Behavior:
// - Validates profile and table definition
// - Updates only rows with deleted = false
// - success = true means a row was actually changed; false means nothing to delete
// - If the physical table is missing but the definition exists, returns INTERNAL
rpc DeleteTableData(DeleteTableDataRequest) returns (DeleteTableDataResponse);
// Fetch a single non-deleted row by id as textified values.
//
// Behavior:
// - Validates profile and table definition
// - Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
// including: id, deleted, all user-defined columns, and FK columns
// named "<linked_table>_id" for each table link
// - Fails with NOT_FOUND if record does not exist or is soft-deleted
// - If the physical table is missing but the definition exists, returns INTERNAL
rpc GetTableData(GetTableDataRequest) returns (GetTableDataResponse);
// Count non-deleted rows in a table.
//
// Behavior:
// - Validates profile and table definition
// - Returns komp_ac.common.CountResponse.count with rows where deleted = FALSE
// - If the physical table is missing but the definition exists, returns INTERNAL
rpc GetTableDataCount(GetTableDataCountRequest) returns (komp_ac.common.CountResponse);
// Fetch the N-th non-deleted row by id order (1-based), then return its full data.
//
// Behavior:
// - position is 1-based (position = 1 → first row by id ASC with deleted = FALSE)
// - Returns NOT_FOUND if position is out of bounds
// - Otherwise identical to GetTableData for the selected id
rpc GetTableDataByPosition(GetTableDataByPositionRequest) returns (GetTableDataResponse);
}
// Insert a new row.
message PostTableDataRequest {
// Required. Profile (PostgreSQL schema) name that owns the table.
// Must exist in the schemas table.
string profile_name = 1;
// Required. Logical table (definition) name within the profile.
// Must exist in table_definitions for the given profile.
string table_name = 2;
// Required. Key-value data for columns to insert.
//
// Allowed keys:
// - User-defined columns from the table definition
// - System/FK columns:
// • "deleted" (BOOLEAN), optional; default FALSE if not provided
// • "<linked_table>_id" (BIGINT) for each table link
//
// Type expectations by SQL type:
// - TEXT: string value; empty string is treated as NULL
// - BOOLEAN: bool value
// - TIMESTAMPTZ: ISO 8601/RFC 3339 string (parsed to TIMESTAMPTZ)
// - INTEGER: number with no fractional part and within i32 range
// - BIGINT: number with no fractional part and within i64 range
// - NUMERIC(p,s): string representation only; empty string becomes NULL
// (numbers for NUMERIC are rejected to avoid precision loss)
//
// Script validation rules:
// - If a script exists for a target column, that column MUST be present here,
// and its provided value MUST equal the scripts computed value (type-aware
// comparison, e.g., decimals are compared numerically).
//
// Notes:
// - Unknown/invalid column names are rejected
// - Some application-specific validations may apply (e.g., max length for
// certain fields like "telefon")
map<string, google.protobuf.Value> data = 3;
}
// Insert response.
message PostTableDataResponse {
// True if the insert succeeded.
bool success = 1;
// Human-readable message.
string message = 2;
// The id of the inserted row.
int64 inserted_id = 3;
}
// Update an existing row.
message PutTableDataRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
// Required. Id of the row to update.
int64 id = 3;
// Required. Columns to update (same typing rules as PostTableDataRequest.data).
//
// Special script rules:
// - If a script targets column X and X is included here, the value for X must
// equal the scripts result (type-aware).
// - If X is not included here but the update would cause the scripts result
// to change compared to the current stored value, the update is rejected with
// FAILED_PRECONDITION, instructing the caller to include X explicitly.
//
// Passing an empty map results in a no-op success response.
map<string, google.protobuf.Value> data = 4;
}
// Update response.
message PutTableDataResponse {
// True if the update succeeded (or no-op on empty data).
bool success = 1;
// Human-readable message.
string message = 2;
// The id of the updated row.
int64 updated_id = 3;
}
// Soft-delete a single row.
message DeleteTableDataRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
// Required. Row id to soft-delete.
int64 record_id = 3;
}
// Soft-delete response.
message DeleteTableDataResponse {
// True if a row was marked deleted (id existed and was not already deleted).
bool success = 1;
}
// Fetch a single non-deleted row by id.
message GetTableDataRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
// Required. Id of the row to fetch.
int64 id = 3;
}
// Row payload: all columns returned as strings.
message GetTableDataResponse {
// Map of column_name → stringified value for:
// - id, deleted
// - all user-defined columns from the table definition
// - FK columns named "<linked_table>_id" for each table link
//
// All values are returned as TEXT via col::TEXT and COALESCEed to empty string
// (NULL becomes ""). The row is returned only if deleted = FALSE.
map<string, string> data = 1;
}
// Count non-deleted rows.
message GetTableDataCountRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
}
// Fetch by ordinal position among non-deleted rows (1-based).
message GetTableDataByPositionRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
// Required. 1-based position by id ascending among rows with deleted = FALSE.
int32 position = 3;
}