feat(core): add bounded key creation contract
Expose create_redis_value through the Tauri bridge, add NX-based string creation in redis-core, and surface duplicate-key handling through stable operator notices. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -2,8 +2,9 @@ use redis_core::{
|
||||
BackendBootstrap, BackendError, CommandExecutionResult, ConnectionTestResult,
|
||||
RedisCommandRequest, RedisConnectionRequest, RedisKeyBrowseRequest, RedisKeyBrowseResult,
|
||||
RedisKeyMetadata, RedisKeyMetadataRequest, RedisKeyTtlUpdateRequest, RedisKeyTtlUpdateResult,
|
||||
RedisValueReadRequest, RedisValueRecord, RedisValueWriteRequest, browse_keys, execute_command,
|
||||
inspect_key, read_value, test_connection, update_key_ttl, write_value,
|
||||
RedisValueCreateRequest, RedisValueCreateResult, RedisValueReadRequest, RedisValueRecord,
|
||||
RedisValueWriteRequest, browse_keys, create_value, execute_command, inspect_key, read_value,
|
||||
test_connection, update_key_ttl, write_value,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
@@ -45,6 +46,13 @@ fn write_redis_value(request: RedisValueWriteRequest) -> Result<RedisValueRecord
|
||||
write_value(request)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn create_redis_value(
|
||||
request: RedisValueCreateRequest,
|
||||
) -> Result<RedisValueCreateResult, BackendError> {
|
||||
create_value(request)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn update_redis_key_ttl(
|
||||
request: RedisKeyTtlUpdateRequest,
|
||||
@@ -62,6 +70,7 @@ fn main() {
|
||||
inspect_redis_key,
|
||||
read_redis_value,
|
||||
write_redis_value,
|
||||
create_redis_value,
|
||||
update_redis_key_ttl
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
@@ -65,6 +65,28 @@ describe("operator notices", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("maps duplicate key creation into a stable neutral notice", () => {
|
||||
expect(
|
||||
toBackendErrorNotice(
|
||||
{
|
||||
code: "already_exists",
|
||||
message: "Redis key creation does not overwrite an existing key.",
|
||||
detail: "key `draft:key` already exists",
|
||||
},
|
||||
{
|
||||
operation: "key_create",
|
||||
connectionName: "redis-local-fixture",
|
||||
databaseLabel: "db0",
|
||||
keyName: "draft:key",
|
||||
},
|
||||
),
|
||||
).toEqual(
|
||||
createNeutralNotice(
|
||||
"Redis refused to proceed while creating draft:key on redis-local-fixture / db0 because the key already exists. Choose a new key name or inspect the existing key before retrying. Backend detail: Redis key creation does not overwrite an existing key. (key `draft:key` already exists).",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("describes command failures with command and scope context", () => {
|
||||
expect(
|
||||
toBackendErrorNotice(
|
||||
|
||||
@@ -9,6 +9,7 @@ export type BackendErrorCode =
|
||||
| "invalid_connection_config"
|
||||
| "unsupported_tls_mode"
|
||||
| "unsupported_operation"
|
||||
| "already_exists"
|
||||
| "connection_failed"
|
||||
| "authentication_failed"
|
||||
| "command_failed"
|
||||
@@ -21,6 +22,12 @@ export type BackendError = {
|
||||
};
|
||||
|
||||
export type BackendErrorContext =
|
||||
| {
|
||||
operation: "key_create";
|
||||
connectionName: string;
|
||||
databaseLabel: string;
|
||||
keyName: string;
|
||||
}
|
||||
| {
|
||||
operation: "connection_test";
|
||||
connectionName: string;
|
||||
@@ -96,6 +103,7 @@ export function toBackendErrorNotice(
|
||||
const backendError = toBackendError(error);
|
||||
const tone =
|
||||
backendError.code === "invalid_connection_config" ||
|
||||
backendError.code === "already_exists" ||
|
||||
backendError.code === "unsupported_operation" ||
|
||||
backendError.code === "unsupported_tls_mode"
|
||||
? "neutral"
|
||||
@@ -115,6 +123,7 @@ function normalizeBackendErrorCode(value: unknown): BackendErrorCode {
|
||||
switch (value) {
|
||||
case "invalid_connection_config":
|
||||
case "unsupported_tls_mode":
|
||||
case "already_exists":
|
||||
case "unsupported_operation":
|
||||
case "connection_failed":
|
||||
case "authentication_failed":
|
||||
@@ -134,6 +143,8 @@ function buildHeadline(error: BackendError, context: BackendErrorContext) {
|
||||
return `The request for ${target} was rejected before Redis was contacted.`;
|
||||
case "unsupported_tls_mode":
|
||||
return `The request for ${target} requires TLS handling that this foundation build does not support yet.`;
|
||||
case "already_exists":
|
||||
return `Redis refused to proceed while ${target} because the key already exists.`;
|
||||
case "unsupported_operation":
|
||||
return `The current desktop contract does not allow ${target} yet.`;
|
||||
case "connection_failed":
|
||||
@@ -155,6 +166,8 @@ function buildHint(code: BackendErrorCode, context: BackendErrorContext) {
|
||||
return "Check host, port, database, search pattern, and key input before retrying.";
|
||||
case "unsupported_tls_mode":
|
||||
return "Keep TLS disabled in this foundation build unless product scope explicitly adds transport controls.";
|
||||
case "already_exists":
|
||||
return "Choose a new key name or inspect the existing key before retrying.";
|
||||
case "unsupported_operation":
|
||||
return "Stay on the visible read-only path or wait for a backend contract change before retrying.";
|
||||
case "connection_failed":
|
||||
@@ -181,6 +194,8 @@ function describeContext(context: BackendErrorContext) {
|
||||
switch (context.operation) {
|
||||
case "connection_test":
|
||||
return `testing ${context.connectionName} on ${context.databaseLabel}`;
|
||||
case "key_create":
|
||||
return `creating ${context.keyName} on ${context.connectionName} / ${context.databaseLabel}`;
|
||||
case "key_browse":
|
||||
return `loading keys from ${context.connectionName} / ${context.databaseLabel}`;
|
||||
case "value_read":
|
||||
|
||||
Reference in New Issue
Block a user