Files
komp_ac/common/proto/table_definition.proto

354 lines
12 KiB
Protocol Buffer

// common/proto/table_definition.proto
syntax = "proto3";
package komp_ac.table_definition;
import "common.proto";
// The TableDefinition service manages the entire lifecycle of user-defined
// tables (stored as both metadata and physical PostgreSQL tables) inside
// logical "profiles" (schemas). Each table has stored structure, links, and
// validation rules.
service TableDefinition {
// Creates a new table (and schema if missing) with system columns,
// linked-table foreign keys, user-defined columns, and optional indexes.
// Also inserts metadata and default validation rules. Entirely transactional.
rpc PostTableDefinition(PostTableDefinitionRequest) returns (TableDefinitionResponse);
// Creates a regular user-table bundle from the restricted komp_ac_fields
// contract embedded in a Typst invoice template. This is invoked explicitly
// by the user; templates do not provision tables merely by existing.
rpc CreateInvoiceTemplateTable(CreateInvoiceTemplateTableRequest) returns (CreateInvoiceTemplateTableResponse);
// Appends new user-defined columns to an existing table.
// Existing columns, links, and table logic are never changed by this call.
rpc AddTableColumns(AddTableColumnsRequest) returns (TableDefinitionResponse);
// Lists all profiles (schemas) and their tables with declared dependencies.
// This provides a tree-like overview of table relationships.
rpc GetProfileTree(komp_ac.common.Empty) returns (ProfileTreeResponse);
// Fetches all tables with their columns and scripts for a specific profile.
// Pure data retrieval - no business logic.
rpc GetProfileDetails(GetProfileDetailsRequest) returns (GetProfileDetailsResponse);
// Copies one complete profile into a new profile without copying table data.
rpc CopyProfile(CopyProfileRequest) returns (CopyProfileResponse);
// 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);
// Drops a table and its metadata, then deletes the profile if it becomes empty.
rpc DeleteTable(DeleteTableRequest) returns (DeleteTableResponse);
}
// A single link to another table within the same profile (schema).
message TableLink {
// Name of an existing table within the same profile to link to.
// For each link, a "<linked>_id" column is created on the new table.
// That column references "<linked>"(id) and adds an index automatically.
string linked_table_name = 1;
// If true, the generated foreign key column is NOT NULL.
// Otherwise the column allows NULL.
// Duplicate links to the same target table in one request are rejected.
bool required = 2;
}
// Defines the input for creating a new table definition.
message PostTableDefinitionRequest {
// Table name to create inside the target profile.
// Must be lowercase, alphanumeric with underscores,
// start with a letter, and be <= 63 chars.
// Forbidden names: "id", "deleted", "created_at", "row_revision", or ending in "_id".
string table_name = 1;
// List of links (foreign keys) to existing tables in the same profile.
// Each will automatically get a "<linked>_id" column and an index.
repeated TableLink links = 2;
// List of user-defined columns (adds to system/id/fk columns).
repeated ColumnDefinition columns = 3;
// List of column names to be indexed (must match existing user-defined columns).
// Indexes can target only user-defined columns; system columns ("id", "deleted",
// "created_at", "row_revision") and automatically generated foreign key ("*_id") columns already
// have indexes. Requests trying to index those columns are rejected.
repeated string indexes = 4;
// Name of profile (Postgres schema) where the table will be created.
// Same naming rules as table_name; cannot collide with reserved schemas
// like "public", "information_schema", or ones starting with "pg_".
string profile_name = 5;
// ISO-4217 base currency used by every MONEY column in this table.
string base_currency = 6;
// Column whose value identifies a row to users in pickers. "id" is always
// valid; otherwise this must name one of the user-defined columns above.
string row_display_column = 7;
}
// Defines the input for explicitly creating a table backed by one invoice
// template. typst_source must contain exactly one declaration of the form:
//
// #let komp_ac_fields = (
// "invoice.number": (source: "local", field_type: "TEXT", write: "user"),
// "items[].product": (source: "reference", table: "stock", required: true),
// "items[].quantity": (source: "local", field_type: "DECIMAL(12,3)", write: "user"),
// "items[].total": (source: "local", field_type: "MONEY", write: "script"),
// )
//
// Root fields create columns and references on table_name. Every [] collection
// creates a child table with a required FK to its immediate parent; fields in
// that collection create columns and references on the child table. Multiple
// template paths in one scope may reuse the same reference.
message CreateInvoiceTemplateTableRequest {
string profile_name = 1;
string table_name = 2;
string typst_source = 3;
string base_currency = 4;
string row_display_column = 5;
}
// One physical dynamic table created for an invoice template scope. The root
// has an empty collection_path and parent_table_name. Each [] scope names its
// collection and the generated table that owns those repeated rows.
message GeneratedInvoiceTemplateTable {
string table_name = 1;
string collection_path = 2;
string parent_table_name = 3;
string sql = 4;
}
// Reports the complete table bundle created from one invoice template.
message CreateInvoiceTemplateTableResponse {
bool success = 1;
repeated GeneratedInvoiceTemplateTable tables = 2;
}
// Defines append-only column additions for an existing table.
message AddTableColumnsRequest {
// Existing profile/schema name.
string profile_name = 1;
// Existing table name in the profile.
string table_name = 2;
// New user-defined columns only. Existing columns cannot be changed here.
repeated ColumnDefinition columns = 3;
// Optional indexes for the new columns only.
repeated string indexes = 4;
// Required when adding the first MONEY column to a table.
string base_currency = 5;
}
enum MoneyRounding {
MONEY_ROUNDING_NONE = 0;
MONEY_ROUNDING_HALF_UP = 1;
}
// Describes one user-defined column for a table.
message ColumnDefinition {
// Column name that follows the same validation rules as table_name.
// Must be lowercase, start with a letter, no uppercase characters,
// and cannot be "id", "deleted", "created_at", "row_revision", or end with "_id".
string name = 1;
// Logical column type. Supported values (case-insensitive):
// TEXT
// BOOLEAN
// INSTANT (local input resolved in the authenticated user's timezone
// and displayed in the viewer timezone)
// USER_DATETIME (local input resolved in the user's timezone)
// RAW_DATETIME (timezone-free civil datetime)
// PHONE (international or national phone number; generates extension/type/country/calling-code companions)
// TIME (timezone-free time of day)
// MONEY (= unconstrained NUMERIC; currency comes from the table)
// ACCOUNTING (creates the schema-managed name, debit, and credit columns;
// one stored row contributes one line to the profile journal)
// INT
// BIGINT
// DATE
// DURATION
// PERIOD
// DECIMAL(p,s) → NUMERIC(p,s)
// DECIMAL args must be integers (no sign, no dot, no leading zeros);
// s ≤ p and p ≥ 1.
string field_type = 2;
reserved 3;
// MONEY rounding applied before a value is stored.
MoneyRounding rounding = 4;
// When true, the submitted script result is validated normally and the
// stored value is also refreshed when one of its dependencies later changes.
bool recompute_on_dependency_change = 5;
}
// Response after table creation (success + DDL preview).
message TableDefinitionResponse {
// True if all DB changes and metadata inserts succeeded.
bool success = 1;
// The actual SQL executed: CREATE TABLE + CREATE INDEX statements.
string sql = 2;
}
// Describes the tree of all profiles and their tables.
message ProfileTreeResponse {
// Table entry in a profile.
message Table {
// Internal ID from table_definitions.id (metadata record).
int64 id = 1;
// Table name within the profile (schema).
string name = 2;
// Other tables this one references (based on link definitions only).
repeated string depends_on = 3;
// Column whose value is used as the human-readable row label.
string row_display_column = 4;
// "dynamic" for user-defined tables, "system" for backend-managed tables.
string table_kind = 5;
}
// Profile (schema) entry.
message Profile {
// Name of the schema/profile (as stored in `schemas.name`).
string name = 1;
// All tables in that schema and their dependencies.
repeated Table tables = 2;
}
// All profiles in the system.
repeated Profile profiles = 1;
}
// Request to fetch all tables, columns and scripts for a profile.
message GetProfileDetailsRequest {
// Profile (schema) name to fetch details for.
string profile_name = 1;
}
// Response with all tables, columns and scripts for a profile.
message GetProfileDetailsResponse {
string profile_name = 1;
repeated TableDetail tables = 2;
}
// Request to copy one full profile into a new profile.
message CopyProfileRequest {
string source_profile_name = 1;
string target_profile_name = 2;
repeated string table_names = 3;
}
// Response after copying a profile.
message CopyProfileResponse {
bool success = 1;
string message = 2;
int32 tables_copied = 3;
int32 scripts_copied = 4;
}
// Request to fetch recorded column alias rename history for one profile.
message GetColumnAliasRenameHistoryRequest {
string profile_name = 1;
// 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;
int64 id = 2;
repeated ColumnDefinition columns = 3;
repeated ScriptInfo scripts = 4;
string base_currency = 5;
string row_display_column = 6;
map<string, ColumnBehavior> column_behaviors = 7;
string table_kind = 8;
}
// Server-owned behavior for one logical column returned in table details.
message ColumnBehavior {
// True when the server created the column as a companion of another column.
bool generated = 1;
// True when clients must display but never submit edits for the column.
bool read_only = 2;
// Logical source column name, empty for ordinary user-defined columns.
string generated_from = 3;
}
// A script that targets a specific column in a table.
message ScriptInfo {
int64 script_id = 1;
string target_column = 2;
string target_column_type = 3;
string script = 4;
string description = 5;
}
// Request to rename one user-visible column alias in a table.
message RenameColumnAliasRequest {
string profile_name = 1;
string table_name = 2;
string old_column_name = 3;
string new_column_name = 4;
}
// Response after renaming one column alias.
message RenameColumnAliasResponse {
bool success = 1;
string message = 2;
}
// Request to delete one table definition entirely.
message DeleteTableRequest {
// Profile (schema) name owning the table (must exist).
string profile_name = 1;
// Table to drop (must exist in the profile).
// Executes DROP TABLE "profile"."table" CASCADE and then removes metadata.
string table_name = 2;
}
// Response after table deletion.
message DeleteTableResponse {
// True if table and metadata were successfully deleted in one transaction.
bool success = 1;
// Human-readable summary of what was removed.
string message = 2;
}