@lix-js/sdk 0.6.0-preview.2 → 0.6.0-preview.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +46 -8
- package/dist/engine-wasm/wasm/lix_engine.d.ts +25 -1
- package/dist/engine-wasm/wasm/lix_engine.js +60 -2
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +5 -0
- package/dist/generated/builtin-schemas.d.ts +87 -162
- package/dist/generated/builtin-schemas.js +139 -236
- package/dist/open-lix.d.ts +10 -3
- package/dist/open-lix.js +39 -0
- package/dist-engine-src/src/binary_cas/types.rs +0 -6
- package/dist-engine-src/src/catalog/context.rs +412 -0
- package/dist-engine-src/src/catalog/mod.rs +10 -0
- package/dist-engine-src/src/catalog/schema.rs +4 -0
- package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
- package/dist-engine-src/src/cel/mod.rs +1 -1
- package/dist-engine-src/src/cel/provider.rs +1 -1
- package/dist-engine-src/src/commit_graph/context.rs +328 -1015
- package/dist-engine-src/src/commit_graph/mod.rs +2 -3
- package/dist-engine-src/src/commit_graph/types.rs +7 -43
- package/dist-engine-src/src/commit_graph/walker.rs +57 -81
- package/dist-engine-src/src/commit_store/codec.rs +887 -0
- package/dist-engine-src/src/commit_store/context.rs +944 -0
- package/dist-engine-src/src/commit_store/materialization.rs +84 -0
- package/dist-engine-src/src/commit_store/mod.rs +16 -0
- package/dist-engine-src/src/commit_store/storage.rs +600 -0
- package/dist-engine-src/src/commit_store/types.rs +215 -0
- package/dist-engine-src/src/common/identity.rs +15 -5
- package/dist-engine-src/src/common/json_pointer.rs +67 -0
- package/dist-engine-src/src/common/metadata.rs +17 -12
- package/dist-engine-src/src/common/mod.rs +5 -5
- package/dist-engine-src/src/domain.rs +324 -0
- package/dist-engine-src/src/engine.rs +29 -43
- package/dist-engine-src/src/entity_identity.rs +238 -118
- package/dist-engine-src/src/functions/context.rs +17 -52
- package/dist-engine-src/src/functions/deterministic.rs +1 -1
- package/dist-engine-src/src/functions/mod.rs +1 -1
- package/dist-engine-src/src/functions/provider.rs +4 -4
- package/dist-engine-src/src/functions/state.rs +39 -66
- package/dist-engine-src/src/functions/types.rs +1 -1
- package/dist-engine-src/src/init.rs +204 -151
- package/dist-engine-src/src/json_store/context.rs +354 -60
- package/dist-engine-src/src/json_store/encoded.rs +6 -6
- package/dist-engine-src/src/json_store/mod.rs +4 -1
- package/dist-engine-src/src/json_store/store.rs +884 -11
- package/dist-engine-src/src/json_store/types.rs +166 -1
- package/dist-engine-src/src/lib.rs +11 -10
- package/dist-engine-src/src/live_state/context.rs +608 -830
- package/dist-engine-src/src/live_state/mod.rs +3 -3
- package/dist-engine-src/src/live_state/overlay.rs +7 -7
- package/dist-engine-src/src/live_state/reader.rs +5 -5
- package/dist-engine-src/src/live_state/types.rs +19 -36
- package/dist-engine-src/src/live_state/visibility.rs +19 -14
- package/dist-engine-src/src/plugin/archive.rs +3 -6
- package/dist-engine-src/src/plugin/install.rs +0 -18
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
- package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
- package/dist-engine-src/src/schema/compatibility.rs +787 -0
- package/dist-engine-src/src/schema/definition.json +47 -17
- package/dist-engine-src/src/schema/definition.rs +202 -96
- package/dist-engine-src/src/schema/key.rs +9 -77
- package/dist-engine-src/src/schema/mod.rs +4 -4
- package/dist-engine-src/src/schema/tests.rs +133 -92
- package/dist-engine-src/src/session/context.rs +86 -48
- package/dist-engine-src/src/session/create_version.rs +22 -14
- package/dist-engine-src/src/session/execute.rs +117 -23
- package/dist-engine-src/src/session/merge/apply.rs +4 -4
- package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
- package/dist-engine-src/src/session/merge/stats.rs +1 -1
- package/dist-engine-src/src/session/merge/version.rs +35 -45
- package/dist-engine-src/src/session/mod.rs +9 -7
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
- package/dist-engine-src/src/session/switch_version.rs +17 -28
- package/dist-engine-src/src/session/transaction.rs +76 -0
- package/dist-engine-src/src/sql2/change_provider.rs +14 -20
- package/dist-engine-src/src/sql2/classify.rs +75 -48
- package/dist-engine-src/src/sql2/context.rs +22 -18
- package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
- package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
- package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
- package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
- package/dist-engine-src/src/sql2/error.rs +24 -5
- package/dist-engine-src/src/sql2/execute.rs +426 -272
- package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
- package/dist-engine-src/src/sql2/file_provider.rs +533 -108
- package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
- package/dist-engine-src/src/sql2/history_projection.rs +3 -27
- package/dist-engine-src/src/sql2/history_provider.rs +11 -17
- package/dist-engine-src/src/sql2/history_route.rs +22 -8
- package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
- package/dist-engine-src/src/sql2/mod.rs +8 -4
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
- package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
- package/dist-engine-src/src/sql2/public_bind/dml.rs +172 -0
- package/dist-engine-src/src/sql2/public_bind/mod.rs +26 -0
- package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
- package/dist-engine-src/src/sql2/read_only.rs +10 -12
- package/dist-engine-src/src/sql2/session.rs +7 -10
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
- package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
- package/dist-engine-src/src/sql2/udfs/public_call.rs +238 -0
- package/dist-engine-src/src/sql2/version_provider.rs +46 -31
- package/dist-engine-src/src/sql2/version_scope.rs +4 -4
- package/dist-engine-src/src/storage_bench.rs +1782 -325
- package/dist-engine-src/src/test_support.rs +183 -36
- package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
- package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
- package/dist-engine-src/src/tracked_state/context.rs +1155 -271
- package/dist-engine-src/src/tracked_state/diff.rs +249 -57
- package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
- package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
- package/dist-engine-src/src/tracked_state/merge.rs +37 -19
- package/dist-engine-src/src/tracked_state/mod.rs +8 -7
- package/dist-engine-src/src/tracked_state/storage.rs +138 -6
- package/dist-engine-src/src/tracked_state/tree.rs +695 -252
- package/dist-engine-src/src/tracked_state/types.rs +176 -6
- package/dist-engine-src/src/transaction/commit.rs +695 -435
- package/dist-engine-src/src/transaction/context.rs +551 -310
- package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
- package/dist-engine-src/src/transaction/mod.rs +2 -0
- package/dist-engine-src/src/transaction/normalization.rs +311 -447
- package/dist-engine-src/src/transaction/prep.rs +37 -0
- package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
- package/dist-engine-src/src/transaction/staging.rs +701 -406
- package/dist-engine-src/src/transaction/types.rs +231 -122
- package/dist-engine-src/src/transaction/validation.rs +2717 -1698
- package/dist-engine-src/src/untracked_state/codec.rs +40 -96
- package/dist-engine-src/src/untracked_state/context.rs +21 -5
- package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
- package/dist-engine-src/src/untracked_state/mod.rs +3 -5
- package/dist-engine-src/src/untracked_state/storage.rs +105 -57
- package/dist-engine-src/src/untracked_state/types.rs +63 -13
- package/dist-engine-src/src/version/context.rs +1 -13
- package/dist-engine-src/src/version/lifecycle.rs +221 -0
- package/dist-engine-src/src/version/mod.rs +3 -2
- package/dist-engine-src/src/version/refs.rs +12 -103
- package/dist-engine-src/src/version/stage_rows.rs +15 -19
- package/package.json +1 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -321
- package/dist-engine-src/src/changelog/context.rs +0 -92
- package/dist-engine-src/src/changelog/materialization.rs +0 -121
- package/dist-engine-src/src/changelog/mod.rs +0 -13
- package/dist-engine-src/src/changelog/reader.rs +0 -20
- package/dist-engine-src/src/changelog/storage.rs +0 -220
- package/dist-engine-src/src/changelog/types.rs +0 -38
- package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
- package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
- package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
- package/dist-engine-src/src/schema_registry.rs +0 -294
- package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
- package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
- package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
|
@@ -1,244 +1,200 @@
|
|
|
1
|
-
use std::
|
|
1
|
+
use std::sync::Arc;
|
|
2
2
|
|
|
3
3
|
use serde_json::{Map as JsonMap, Value as JsonValue};
|
|
4
4
|
|
|
5
|
+
use crate::catalog::{CatalogSnapshot, SchemaPlan, SchemaPlanId};
|
|
6
|
+
use crate::common::format_json_pointer;
|
|
5
7
|
use crate::common::normalize_path_segment;
|
|
8
|
+
use crate::domain::Domain;
|
|
6
9
|
use crate::entity_identity::{EntityIdentity, EntityIdentityError};
|
|
7
10
|
use crate::functions::FunctionProviderHandle;
|
|
8
11
|
use crate::schema::{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
schema_key_from_definition, validate_lix_schema, validate_lix_schema_definition, SchemaKey,
|
|
12
|
+
is_seed_schema_key, schema_from_registered_snapshot, validate_lix_schema,
|
|
13
|
+
validate_lix_schema_definition,
|
|
12
14
|
};
|
|
13
|
-
use crate::transaction::types::
|
|
15
|
+
use crate::transaction::types::{PreparedRowFacts, TransactionJson, TransactionWriteRow};
|
|
14
16
|
use crate::LixError;
|
|
15
17
|
|
|
16
18
|
pub(crate) const REGISTERED_SCHEMA_KEY: &str = "lix_registered_schema";
|
|
17
19
|
const DIRECTORY_DESCRIPTOR_SCHEMA_KEY: &str = "lix_directory_descriptor";
|
|
18
20
|
const FILE_DESCRIPTOR_SCHEMA_KEY: &str = "lix_file_descriptor";
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/// transaction can target newly registered schemas.
|
|
27
|
-
#[derive(Debug, Clone, Default)]
|
|
28
|
-
pub(crate) struct TransactionSchemaCatalog {
|
|
29
|
-
schemas_by_key: BTreeMap<SchemaCatalogKey, JsonValue>,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
impl TransactionSchemaCatalog {
|
|
33
|
-
pub(crate) fn from_visible_schemas(visible_schemas: &[JsonValue]) -> Result<Self, LixError> {
|
|
34
|
-
let mut catalog = Self::default();
|
|
35
|
-
for schema in visible_schemas {
|
|
36
|
-
let key = schema_key_from_definition(schema)?;
|
|
37
|
-
catalog.insert_schema(key, schema.clone());
|
|
38
|
-
}
|
|
39
|
-
Ok(catalog)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
pub(crate) fn schema(&self, schema_key: &str, schema_version: &str) -> Option<&JsonValue> {
|
|
43
|
-
self.schemas_by_key.get(&SchemaCatalogKey {
|
|
44
|
-
schema_key: schema_key.to_string(),
|
|
45
|
-
schema_version: schema_version.to_string(),
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
pub(crate) fn insert_schema(&mut self, key: SchemaKey, schema: JsonValue) {
|
|
50
|
-
self.schemas_by_key
|
|
51
|
-
.insert(SchemaCatalogKey::from_schema_key(key), schema);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pub(crate) fn contains(&self, schema_key: &str, schema_version: &str) -> bool {
|
|
55
|
-
self.schemas_by_key.contains_key(&SchemaCatalogKey {
|
|
56
|
-
schema_key: schema_key.to_string(),
|
|
57
|
-
schema_version: schema_version.to_string(),
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
#[cfg(test)]
|
|
62
|
-
pub(crate) fn len(&self) -> usize {
|
|
63
|
-
self.schemas_by_key.len()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
pub(crate) fn schema_by_key(&self, schema_key: &str) -> Option<&JsonValue> {
|
|
67
|
-
self.schemas_by_key
|
|
68
|
-
.iter()
|
|
69
|
-
.find_map(|(key, schema)| (key.schema_key == schema_key).then_some(schema))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
pub(crate) fn schema_key_by_key(&self, schema_key: &str) -> Option<SchemaCatalogKey> {
|
|
73
|
-
self.schemas_by_key
|
|
74
|
-
.keys()
|
|
75
|
-
.find(|key| key.schema_key == schema_key)
|
|
76
|
-
.cloned()
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
pub(crate) fn schemas(&self) -> impl Iterator<Item = (&SchemaCatalogKey, &JsonValue)> {
|
|
80
|
-
self.schemas_by_key.iter()
|
|
81
|
-
}
|
|
22
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
23
|
+
pub(crate) struct NormalizedTransactionWriteRow {
|
|
24
|
+
pub(crate) row: TransactionWriteRow,
|
|
25
|
+
pub(crate) snapshot: Option<TransactionJson>,
|
|
26
|
+
pub(crate) schema_plan_id: SchemaPlanId,
|
|
27
|
+
pub(crate) facts: PreparedRowFacts,
|
|
82
28
|
}
|
|
83
29
|
|
|
84
30
|
/// Normalizes one incoming row into a row with final snapshot/entity identity.
|
|
85
31
|
///
|
|
86
|
-
/// This is the canonical schema-semantics boundary for
|
|
32
|
+
/// This is the canonical schema-semantics boundary for transaction writes. It owns
|
|
87
33
|
/// schema default application, primary-key identity derivation, and explicit
|
|
88
34
|
/// identity mismatch validation. SQL providers should not pre-derive primary
|
|
89
|
-
/// keys for schemas that can be normalized here; they should
|
|
35
|
+
/// keys for schemas that can be normalized here; they should pass decoded
|
|
90
36
|
/// snapshots and let this layer complete them.
|
|
91
37
|
///
|
|
92
38
|
/// This function intentionally does not assign timestamps, change ids, or
|
|
93
|
-
/// commit ids; those are
|
|
94
|
-
///
|
|
95
|
-
pub(crate) fn
|
|
96
|
-
mut row:
|
|
97
|
-
schema_catalog: &mut
|
|
39
|
+
/// commit ids; those are prepared-row fields assigned after semantic
|
|
40
|
+
/// normalization has produced the final identity.
|
|
41
|
+
pub(crate) fn normalize_transaction_write_row(
|
|
42
|
+
mut row: TransactionWriteRow,
|
|
43
|
+
schema_catalog: &mut CatalogSnapshot,
|
|
98
44
|
functions: FunctionProviderHandle,
|
|
99
|
-
) -> Result<
|
|
100
|
-
|
|
45
|
+
) -> Result<NormalizedTransactionWriteRow, LixError> {
|
|
46
|
+
validate_transaction_write_row_schema_identity(&row)?;
|
|
101
47
|
|
|
102
|
-
let Some(
|
|
103
|
-
.schema(&row.schema_key, &row.schema_version)
|
|
104
|
-
.cloned()
|
|
105
|
-
else {
|
|
48
|
+
let Some((schema_plan_id, schema_plan)) = schema_catalog.plan_for_key(&row.schema_key) else {
|
|
106
49
|
return Err(LixError::new(
|
|
107
50
|
LixError::CODE_SCHEMA_DEFINITION,
|
|
108
51
|
format!(
|
|
109
|
-
"schema '{}'
|
|
110
|
-
row.schema_key
|
|
52
|
+
"schema '{}' is not visible to this transaction",
|
|
53
|
+
row.schema_key
|
|
111
54
|
),
|
|
112
55
|
));
|
|
113
56
|
};
|
|
114
57
|
|
|
115
|
-
if let Some(
|
|
116
|
-
let mut snapshot =
|
|
117
|
-
apply_defaults(&mut snapshot,
|
|
118
|
-
normalize_filesystem_descriptor_snapshot(&row, &mut snapshot)?;
|
|
58
|
+
let normalized_snapshot = if let Some(snapshot) = row.snapshot.take() {
|
|
59
|
+
let (mut snapshot, normalized) = snapshot_object_from_transaction_json(snapshot, &row)?;
|
|
60
|
+
let defaults_changed = apply_defaults(&mut snapshot, schema_plan, &row, functions)?;
|
|
61
|
+
let descriptor_changed = normalize_filesystem_descriptor_snapshot(&row, &mut snapshot)?;
|
|
119
62
|
let snapshot = JsonValue::Object(snapshot);
|
|
120
|
-
row.entity_id = Some(resolve_entity_id(&row,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
})?);
|
|
63
|
+
row.entity_id = Some(resolve_entity_id(&row, schema_plan, &snapshot)?);
|
|
64
|
+
if defaults_changed || descriptor_changed {
|
|
65
|
+
Some(TransactionJson::from_value(
|
|
66
|
+
snapshot,
|
|
67
|
+
"normalized transaction snapshot_content",
|
|
68
|
+
)?)
|
|
69
|
+
} else {
|
|
70
|
+
Some(TransactionJson::from_parts(Arc::new(snapshot), normalized))
|
|
71
|
+
}
|
|
130
72
|
} else if row.entity_id.is_none() {
|
|
131
73
|
return Err(LixError::new(
|
|
132
74
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
133
75
|
format!(
|
|
134
|
-
"tombstone for schema '{}'
|
|
135
|
-
row.schema_key
|
|
76
|
+
"tombstone for schema '{}' requires entity_id",
|
|
77
|
+
row.schema_key
|
|
136
78
|
),
|
|
137
79
|
));
|
|
138
|
-
}
|
|
80
|
+
} else {
|
|
81
|
+
None
|
|
82
|
+
};
|
|
139
83
|
|
|
140
84
|
if row.schema_key == REGISTERED_SCHEMA_KEY {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
85
|
+
if row.file_id.is_some() {
|
|
86
|
+
return Err(LixError::new(
|
|
87
|
+
LixError::CODE_SCHEMA_DEFINITION,
|
|
88
|
+
"lix_registered_schema rows must not be scoped to a file",
|
|
89
|
+
)
|
|
90
|
+
.with_hint("Schema definitions are scoped by version and durability only; write them with null file_id."));
|
|
91
|
+
}
|
|
92
|
+
let schema_domain =
|
|
93
|
+
Domain::schema_catalog(row.schema_scope_version_id().to_string(), row.untracked);
|
|
94
|
+
remember_pending_registered_schema(
|
|
95
|
+
normalized_snapshot.as_ref().map(TransactionJson::value),
|
|
96
|
+
schema_domain,
|
|
97
|
+
schema_catalog,
|
|
98
|
+
)?;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Ok(NormalizedTransactionWriteRow {
|
|
102
|
+
row,
|
|
103
|
+
snapshot: normalized_snapshot,
|
|
104
|
+
schema_plan_id,
|
|
105
|
+
facts: PreparedRowFacts::default(),
|
|
106
|
+
})
|
|
145
107
|
}
|
|
146
108
|
|
|
147
|
-
fn
|
|
109
|
+
fn validate_transaction_write_row_schema_identity(
|
|
110
|
+
row: &TransactionWriteRow,
|
|
111
|
+
) -> Result<(), LixError> {
|
|
148
112
|
if row.schema_key.is_empty() {
|
|
149
113
|
return Err(LixError::new(
|
|
150
114
|
LixError::CODE_UNKNOWN,
|
|
151
|
-
"
|
|
152
|
-
));
|
|
153
|
-
}
|
|
154
|
-
if row.schema_version.is_empty() {
|
|
155
|
-
return Err(LixError::new(
|
|
156
|
-
LixError::CODE_UNKNOWN,
|
|
157
|
-
"engine2 transaction staging requires non-empty schema_version",
|
|
115
|
+
"engine transaction staging requires non-empty schema_key",
|
|
158
116
|
));
|
|
159
117
|
}
|
|
160
118
|
Ok(())
|
|
161
119
|
}
|
|
162
120
|
|
|
163
|
-
fn
|
|
164
|
-
|
|
165
|
-
row: &
|
|
166
|
-
) -> Result<JsonMap<String, JsonValue>, LixError> {
|
|
167
|
-
let snapshot =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
})?;
|
|
176
|
-
snapshot.as_object().cloned().ok_or_else(|| {
|
|
177
|
-
LixError::new(
|
|
121
|
+
fn snapshot_object_from_transaction_json(
|
|
122
|
+
snapshot: TransactionJson,
|
|
123
|
+
row: &TransactionWriteRow,
|
|
124
|
+
) -> Result<(JsonMap<String, JsonValue>, Arc<str>), LixError> {
|
|
125
|
+
let (snapshot, normalized) = snapshot.into_parts();
|
|
126
|
+
let snapshot = match Arc::try_unwrap(snapshot) {
|
|
127
|
+
Ok(snapshot) => snapshot,
|
|
128
|
+
Err(snapshot) => snapshot.as_ref().clone(),
|
|
129
|
+
};
|
|
130
|
+
match snapshot {
|
|
131
|
+
JsonValue::Object(snapshot) => Ok((snapshot, normalized)),
|
|
132
|
+
_ => Err(LixError::new(
|
|
178
133
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
179
134
|
format!(
|
|
180
|
-
"snapshot_content for schema '{}'
|
|
181
|
-
row.schema_key
|
|
135
|
+
"snapshot_content for schema '{}' must be a JSON object",
|
|
136
|
+
row.schema_key
|
|
182
137
|
),
|
|
183
|
-
)
|
|
184
|
-
}
|
|
138
|
+
)),
|
|
139
|
+
}
|
|
185
140
|
}
|
|
186
141
|
|
|
187
142
|
fn apply_defaults(
|
|
188
143
|
snapshot: &mut JsonMap<String, JsonValue>,
|
|
189
|
-
|
|
190
|
-
row: &
|
|
144
|
+
schema_plan: &SchemaPlan,
|
|
145
|
+
row: &TransactionWriteRow,
|
|
191
146
|
functions: FunctionProviderHandle,
|
|
192
|
-
) -> Result<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
functions,
|
|
197
|
-
&row.schema_key,
|
|
198
|
-
&row.schema_version,
|
|
199
|
-
)?;
|
|
200
|
-
Ok(())
|
|
147
|
+
) -> Result<bool, LixError> {
|
|
148
|
+
schema_plan
|
|
149
|
+
.defaults
|
|
150
|
+
.apply(snapshot, functions, &row.schema_key)
|
|
201
151
|
}
|
|
202
152
|
|
|
203
153
|
fn normalize_filesystem_descriptor_snapshot(
|
|
204
|
-
row: &
|
|
154
|
+
row: &TransactionWriteRow,
|
|
205
155
|
snapshot: &mut JsonMap<String, JsonValue>,
|
|
206
|
-
) -> Result<
|
|
156
|
+
) -> Result<bool, LixError> {
|
|
207
157
|
match row.schema_key.as_str() {
|
|
208
158
|
DIRECTORY_DESCRIPTOR_SCHEMA_KEY => normalize_directory_descriptor_snapshot(row, snapshot),
|
|
209
159
|
FILE_DESCRIPTOR_SCHEMA_KEY => normalize_file_descriptor_snapshot(row, snapshot),
|
|
210
|
-
_ => Ok(
|
|
160
|
+
_ => Ok(false),
|
|
211
161
|
}
|
|
212
162
|
}
|
|
213
163
|
|
|
214
164
|
fn normalize_directory_descriptor_snapshot(
|
|
215
|
-
row: &
|
|
165
|
+
row: &TransactionWriteRow,
|
|
216
166
|
snapshot: &mut JsonMap<String, JsonValue>,
|
|
217
|
-
) -> Result<
|
|
167
|
+
) -> Result<bool, LixError> {
|
|
218
168
|
let Some(name) = optional_string_field(snapshot, "name", row)? else {
|
|
219
|
-
return Ok(
|
|
169
|
+
return Ok(false);
|
|
220
170
|
};
|
|
221
171
|
let normalized_name = normalize_path_segment(name)?;
|
|
172
|
+
if name == normalized_name {
|
|
173
|
+
return Ok(false);
|
|
174
|
+
}
|
|
222
175
|
snapshot.insert("name".to_string(), JsonValue::String(normalized_name));
|
|
223
|
-
Ok(
|
|
176
|
+
Ok(true)
|
|
224
177
|
}
|
|
225
178
|
|
|
226
179
|
fn normalize_file_descriptor_snapshot(
|
|
227
|
-
row: &
|
|
180
|
+
row: &TransactionWriteRow,
|
|
228
181
|
snapshot: &mut JsonMap<String, JsonValue>,
|
|
229
|
-
) -> Result<
|
|
182
|
+
) -> Result<bool, LixError> {
|
|
230
183
|
let Some(name) = optional_string_field(snapshot, "name", row)? else {
|
|
231
|
-
return Ok(
|
|
184
|
+
return Ok(false);
|
|
232
185
|
};
|
|
233
186
|
let normalized_name = normalize_path_segment(name)?;
|
|
187
|
+
if name == normalized_name {
|
|
188
|
+
return Ok(false);
|
|
189
|
+
}
|
|
234
190
|
snapshot.insert("name".to_string(), JsonValue::String(normalized_name));
|
|
235
|
-
Ok(
|
|
191
|
+
Ok(true)
|
|
236
192
|
}
|
|
237
193
|
|
|
238
194
|
fn optional_string_field<'a>(
|
|
239
195
|
snapshot: &'a JsonMap<String, JsonValue>,
|
|
240
196
|
field: &str,
|
|
241
|
-
row: &
|
|
197
|
+
row: &TransactionWriteRow,
|
|
242
198
|
) -> Result<Option<&'a str>, LixError> {
|
|
243
199
|
let Some(value) = snapshot.get(field) else {
|
|
244
200
|
return Ok(None);
|
|
@@ -247,38 +203,38 @@ fn optional_string_field<'a>(
|
|
|
247
203
|
LixError::new(
|
|
248
204
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
249
205
|
format!(
|
|
250
|
-
"snapshot_content for schema '{}'
|
|
251
|
-
row.schema_key,
|
|
206
|
+
"snapshot_content for schema '{}' field '{}' must be a string",
|
|
207
|
+
row.schema_key, field
|
|
252
208
|
),
|
|
253
209
|
)
|
|
254
210
|
})
|
|
255
211
|
}
|
|
256
212
|
|
|
257
213
|
fn resolve_entity_id(
|
|
258
|
-
row: &
|
|
259
|
-
|
|
214
|
+
row: &TransactionWriteRow,
|
|
215
|
+
schema_plan: &SchemaPlan,
|
|
260
216
|
snapshot: &JsonValue,
|
|
261
217
|
) -> Result<EntityIdentity, LixError> {
|
|
262
|
-
let Some(primary_key_paths) =
|
|
218
|
+
let Some(primary_key_paths) = schema_plan.primary_key.as_ref() else {
|
|
263
219
|
return row.entity_id.clone().ok_or_else(|| {
|
|
264
220
|
LixError::new(
|
|
265
221
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
266
222
|
format!(
|
|
267
|
-
"write for schema '{}'
|
|
268
|
-
row.schema_key
|
|
223
|
+
"write for schema '{}' requires entity_id because the schema has no x-lix-primary-key",
|
|
224
|
+
row.schema_key
|
|
269
225
|
),
|
|
270
226
|
)
|
|
271
227
|
});
|
|
272
228
|
};
|
|
273
|
-
let derived = EntityIdentity::from_primary_key_paths(snapshot,
|
|
274
|
-
.map_err(|error| entity_id_derivation_error(row,
|
|
229
|
+
let derived = EntityIdentity::from_primary_key_paths(snapshot, primary_key_paths)
|
|
230
|
+
.map_err(|error| entity_id_derivation_error(row, primary_key_paths, error))?;
|
|
275
231
|
if let Some(entity_id) = row.entity_id.as_ref() {
|
|
276
232
|
if entity_id != &derived {
|
|
277
233
|
return Err(LixError::new(
|
|
278
234
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
279
235
|
format!(
|
|
280
|
-
"entity_id '{}' does not match x-lix-primary-key derived entity_id '{}' for schema '{}'
|
|
281
|
-
entity_id.
|
|
236
|
+
"entity_id '{}' does not match x-lix-primary-key derived entity_id '{}' for schema '{}'",
|
|
237
|
+
entity_id.as_json_array_text()?, derived.as_json_array_text()?, row.schema_key
|
|
282
238
|
),
|
|
283
239
|
));
|
|
284
240
|
}
|
|
@@ -286,72 +242,8 @@ fn resolve_entity_id(
|
|
|
286
242
|
Ok(derived)
|
|
287
243
|
}
|
|
288
244
|
|
|
289
|
-
fn primary_key_paths(schema: &JsonValue) -> Result<Option<Vec<Vec<String>>>, LixError> {
|
|
290
|
-
let Some(primary_key) = schema.get("x-lix-primary-key") else {
|
|
291
|
-
return Ok(None);
|
|
292
|
-
};
|
|
293
|
-
let primary_key = primary_key.as_array().ok_or_else(|| {
|
|
294
|
-
LixError::new(
|
|
295
|
-
LixError::CODE_SCHEMA_DEFINITION,
|
|
296
|
-
"schema x-lix-primary-key must be an array of JSON Pointers",
|
|
297
|
-
)
|
|
298
|
-
})?;
|
|
299
|
-
primary_key
|
|
300
|
-
.iter()
|
|
301
|
-
.enumerate()
|
|
302
|
-
.map(|(index, pointer)| {
|
|
303
|
-
let pointer = pointer.as_str().ok_or_else(|| {
|
|
304
|
-
LixError::new(
|
|
305
|
-
LixError::CODE_SCHEMA_DEFINITION,
|
|
306
|
-
format!("schema x-lix-primary-key entry at index {index} must be a string"),
|
|
307
|
-
)
|
|
308
|
-
})?;
|
|
309
|
-
parse_json_pointer(pointer)
|
|
310
|
-
})
|
|
311
|
-
.collect::<Result<Vec<_>, _>>()
|
|
312
|
-
.map(Some)
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
fn parse_json_pointer(pointer: &str) -> Result<Vec<String>, LixError> {
|
|
316
|
-
if pointer.is_empty() {
|
|
317
|
-
return Ok(Vec::new());
|
|
318
|
-
}
|
|
319
|
-
if !pointer.starts_with('/') {
|
|
320
|
-
return Err(LixError::new(
|
|
321
|
-
LixError::CODE_SCHEMA_DEFINITION,
|
|
322
|
-
format!("invalid JSON pointer '{pointer}'"),
|
|
323
|
-
));
|
|
324
|
-
}
|
|
325
|
-
pointer[1..]
|
|
326
|
-
.split('/')
|
|
327
|
-
.map(decode_json_pointer_segment)
|
|
328
|
-
.collect()
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
fn decode_json_pointer_segment(segment: &str) -> Result<String, LixError> {
|
|
332
|
-
let mut out = String::new();
|
|
333
|
-
let mut chars = segment.chars();
|
|
334
|
-
while let Some(ch) = chars.next() {
|
|
335
|
-
if ch == '~' {
|
|
336
|
-
match chars.next() {
|
|
337
|
-
Some('0') => out.push('~'),
|
|
338
|
-
Some('1') => out.push('/'),
|
|
339
|
-
_ => {
|
|
340
|
-
return Err(LixError::new(
|
|
341
|
-
LixError::CODE_SCHEMA_DEFINITION,
|
|
342
|
-
"invalid JSON pointer escape",
|
|
343
|
-
))
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
out.push(ch);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
Ok(out)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
245
|
fn entity_id_derivation_error(
|
|
354
|
-
row: &
|
|
246
|
+
row: &TransactionWriteRow,
|
|
355
247
|
primary_key_paths: &[Vec<String>],
|
|
356
248
|
error: EntityIdentityError,
|
|
357
249
|
) -> LixError {
|
|
@@ -360,21 +252,20 @@ fn entity_id_derivation_error(
|
|
|
360
252
|
EntityIdentityError::EmptyPrimaryKeyPath { index } => {
|
|
361
253
|
format!("empty x-lix-primary-key pointer at index {index}")
|
|
362
254
|
}
|
|
255
|
+
EntityIdentityError::EmptyPrimaryKeyValue { index } => {
|
|
256
|
+
let pointer = primary_key_paths
|
|
257
|
+
.get(index)
|
|
258
|
+
.map(|path| format_json_pointer(path))
|
|
259
|
+
.unwrap_or_else(|| format!("index {index}"));
|
|
260
|
+
format!("empty value at primary-key pointer '{pointer}'")
|
|
261
|
+
}
|
|
363
262
|
EntityIdentityError::MissingPrimaryKeyValue { index } => {
|
|
364
263
|
let pointer = format_json_pointer(&primary_key_paths[index]);
|
|
365
264
|
format!("missing value at primary-key pointer '{pointer}'")
|
|
366
265
|
}
|
|
367
|
-
EntityIdentityError::NullPrimaryKeyValue { index } => {
|
|
368
|
-
let pointer = format_json_pointer(&primary_key_paths[index]);
|
|
369
|
-
format!("null value at primary-key pointer '{pointer}'")
|
|
370
|
-
}
|
|
371
|
-
EntityIdentityError::EmptyPrimaryKeyValue { index } => {
|
|
372
|
-
let pointer = format_json_pointer(&primary_key_paths[index]);
|
|
373
|
-
format!("empty value at primary-key pointer '{pointer}'")
|
|
374
|
-
}
|
|
375
266
|
EntityIdentityError::UnsupportedPrimaryKeyValue { index } => {
|
|
376
267
|
let pointer = format_json_pointer(&primary_key_paths[index]);
|
|
377
|
-
format!("
|
|
268
|
+
format!("non-string value at primary-key pointer '{pointer}'")
|
|
378
269
|
}
|
|
379
270
|
EntityIdentityError::InvalidEncodedEntityIdentity => {
|
|
380
271
|
"invalid encoded entity identity".to_string()
|
|
@@ -383,56 +274,38 @@ fn entity_id_derivation_error(
|
|
|
383
274
|
LixError::new(
|
|
384
275
|
LixError::CODE_SCHEMA_VALIDATION,
|
|
385
276
|
format!(
|
|
386
|
-
"failed to derive entity_id for schema '{}'
|
|
387
|
-
row.schema_key
|
|
277
|
+
"failed to derive entity_id for schema '{}': {detail}",
|
|
278
|
+
row.schema_key
|
|
388
279
|
),
|
|
389
280
|
)
|
|
390
281
|
}
|
|
391
282
|
|
|
392
|
-
fn format_json_pointer(segments: &[String]) -> String {
|
|
393
|
-
if segments.is_empty() {
|
|
394
|
-
return String::new();
|
|
395
|
-
}
|
|
396
|
-
format!(
|
|
397
|
-
"/{}",
|
|
398
|
-
segments
|
|
399
|
-
.iter()
|
|
400
|
-
.map(|segment| segment.replace('~', "~0").replace('/', "~1"))
|
|
401
|
-
.collect::<Vec<_>>()
|
|
402
|
-
.join("/")
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
283
|
pub(crate) fn remember_pending_registered_schema(
|
|
407
|
-
|
|
408
|
-
|
|
284
|
+
snapshot: Option<&JsonValue>,
|
|
285
|
+
domain: Domain,
|
|
286
|
+
schema_catalog: &mut CatalogSnapshot,
|
|
409
287
|
) -> Result<(), LixError> {
|
|
410
|
-
let Some(
|
|
288
|
+
let Some(snapshot) = snapshot else {
|
|
411
289
|
return Err(LixError::new(
|
|
412
290
|
LixError::CODE_SCHEMA_DEFINITION,
|
|
413
291
|
"lix_registered_schema rows cannot be deleted yet; schema deletion is not supported",
|
|
414
292
|
));
|
|
415
293
|
};
|
|
416
|
-
let
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
)
|
|
430
|
-
})?;
|
|
431
|
-
if !snapshot.get("value").is_some_and(JsonValue::is_object) {
|
|
432
|
-
validate_lix_schema(®istered_schema_definition, &snapshot)?;
|
|
294
|
+
if let Some(schema) = snapshot.get("value") {
|
|
295
|
+
validate_lix_schema_definition(schema)?;
|
|
296
|
+
}
|
|
297
|
+
{
|
|
298
|
+
let registered_schema_definition = schema_catalog
|
|
299
|
+
.schema(REGISTERED_SCHEMA_KEY)
|
|
300
|
+
.ok_or_else(|| {
|
|
301
|
+
LixError::new(
|
|
302
|
+
LixError::CODE_SCHEMA_DEFINITION,
|
|
303
|
+
"lix_registered_schema schema is not visible to this transaction",
|
|
304
|
+
)
|
|
305
|
+
})?;
|
|
306
|
+
validate_lix_schema(registered_schema_definition, &snapshot)?;
|
|
433
307
|
}
|
|
434
308
|
let (key, schema) = schema_from_registered_snapshot(&snapshot)?;
|
|
435
|
-
reject_unsupported_registered_schema_version(&key)?;
|
|
436
309
|
if is_seed_schema_key(&key.schema_key) {
|
|
437
310
|
return Err(LixError::new(
|
|
438
311
|
LixError::CODE_SCHEMA_DEFINITION,
|
|
@@ -443,26 +316,10 @@ pub(crate) fn remember_pending_registered_schema(
|
|
|
443
316
|
));
|
|
444
317
|
}
|
|
445
318
|
validate_lix_schema_definition(&schema)?;
|
|
446
|
-
|
|
447
|
-
schema_catalog.insert_schema(key, schema);
|
|
319
|
+
schema_catalog.insert_schema_for_domain(domain, key, schema)?;
|
|
448
320
|
Ok(())
|
|
449
321
|
}
|
|
450
322
|
|
|
451
|
-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
452
|
-
pub(crate) struct SchemaCatalogKey {
|
|
453
|
-
pub(crate) schema_key: String,
|
|
454
|
-
pub(crate) schema_version: String,
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
impl SchemaCatalogKey {
|
|
458
|
-
pub(crate) fn from_schema_key(key: SchemaKey) -> Self {
|
|
459
|
-
Self {
|
|
460
|
-
schema_key: key.schema_key,
|
|
461
|
-
schema_version: key.schema_version,
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
323
|
#[cfg(test)]
|
|
467
324
|
mod tests {
|
|
468
325
|
use serde_json::json;
|
|
@@ -474,18 +331,20 @@ mod tests {
|
|
|
474
331
|
#[test]
|
|
475
332
|
fn normalization_derives_entity_id_from_primary_key() {
|
|
476
333
|
let mut catalog = catalog_with(vec![schema_with_default_id()]);
|
|
477
|
-
let row =
|
|
334
|
+
let row = TransactionWriteRow {
|
|
478
335
|
entity_id: None,
|
|
479
336
|
schema_key: "normalization_schema".to_string(),
|
|
480
|
-
|
|
481
|
-
|
|
337
|
+
snapshot: Some(snapshot_json(
|
|
338
|
+
r#"{"id":"entity-from-snapshot","value":"hello"}"#,
|
|
339
|
+
)),
|
|
482
340
|
..base_stage_row()
|
|
483
341
|
};
|
|
484
342
|
|
|
485
|
-
let row =
|
|
343
|
+
let row =
|
|
344
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
486
345
|
|
|
487
346
|
assert_eq!(
|
|
488
|
-
row.entity_id.as_ref(),
|
|
347
|
+
row.row.entity_id.as_ref(),
|
|
489
348
|
Some(&crate::entity_identity::EntityIdentity::single(
|
|
490
349
|
"entity-from-snapshot"
|
|
491
350
|
))
|
|
@@ -495,20 +354,19 @@ mod tests {
|
|
|
495
354
|
#[test]
|
|
496
355
|
fn normalization_applies_json_and_cel_defaults_before_identity_derivation() {
|
|
497
356
|
let mut catalog = catalog_with(vec![schema_with_default_id()]);
|
|
498
|
-
let row =
|
|
357
|
+
let row = TransactionWriteRow {
|
|
499
358
|
entity_id: None,
|
|
500
359
|
schema_key: "normalization_schema".to_string(),
|
|
501
|
-
|
|
502
|
-
snapshot_content: Some(r#"{}"#.to_string()),
|
|
360
|
+
snapshot: Some(snapshot_json(r#"{}"#)),
|
|
503
361
|
..base_stage_row()
|
|
504
362
|
};
|
|
505
363
|
|
|
506
|
-
let row =
|
|
507
|
-
|
|
508
|
-
|
|
364
|
+
let row =
|
|
365
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
366
|
+
let snapshot = normalized_snapshot(&row);
|
|
509
367
|
|
|
510
368
|
assert_eq!(
|
|
511
|
-
row.entity_id.as_ref(),
|
|
369
|
+
row.row.entity_id.as_ref(),
|
|
512
370
|
Some(&crate::entity_identity::EntityIdentity::single(
|
|
513
371
|
"uuid-default"
|
|
514
372
|
))
|
|
@@ -520,17 +378,16 @@ mod tests {
|
|
|
520
378
|
#[test]
|
|
521
379
|
fn normalization_applies_cel_defaults_from_snapshot_context() {
|
|
522
380
|
let mut catalog = catalog_with(vec![schema_with_cel_field_default()]);
|
|
523
|
-
let row =
|
|
381
|
+
let row = TransactionWriteRow {
|
|
524
382
|
entity_id: None,
|
|
525
383
|
schema_key: "cel_field_default_schema".to_string(),
|
|
526
|
-
|
|
527
|
-
snapshot_content: Some(r#"{"id":"entity-1","name":"Sample"}"#.to_string()),
|
|
384
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1","name":"Sample"}"#)),
|
|
528
385
|
..base_stage_row()
|
|
529
386
|
};
|
|
530
387
|
|
|
531
|
-
let row =
|
|
532
|
-
|
|
533
|
-
|
|
388
|
+
let row =
|
|
389
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
390
|
+
let snapshot = normalized_snapshot(&row);
|
|
534
391
|
|
|
535
392
|
assert_eq!(snapshot["slug"], "Sample-slug");
|
|
536
393
|
}
|
|
@@ -538,17 +395,16 @@ mod tests {
|
|
|
538
395
|
#[test]
|
|
539
396
|
fn normalization_x_lix_default_overrides_json_default() {
|
|
540
397
|
let mut catalog = catalog_with(vec![schema_with_overridden_default()]);
|
|
541
|
-
let row =
|
|
398
|
+
let row = TransactionWriteRow {
|
|
542
399
|
entity_id: None,
|
|
543
400
|
schema_key: "overridden_default_schema".to_string(),
|
|
544
|
-
|
|
545
|
-
snapshot_content: Some(r#"{"id":"entity-1"}"#.to_string()),
|
|
401
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1"}"#)),
|
|
546
402
|
..base_stage_row()
|
|
547
403
|
};
|
|
548
404
|
|
|
549
|
-
let row =
|
|
550
|
-
|
|
551
|
-
|
|
405
|
+
let row =
|
|
406
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
407
|
+
let snapshot = normalized_snapshot(&row);
|
|
552
408
|
|
|
553
409
|
assert_eq!(snapshot["status"], "computed");
|
|
554
410
|
}
|
|
@@ -556,17 +412,16 @@ mod tests {
|
|
|
556
412
|
#[test]
|
|
557
413
|
fn normalization_does_not_overwrite_explicit_null_with_default() {
|
|
558
414
|
let mut catalog = catalog_with(vec![schema_with_nullable_default()]);
|
|
559
|
-
let row =
|
|
415
|
+
let row = TransactionWriteRow {
|
|
560
416
|
entity_id: None,
|
|
561
417
|
schema_key: "nullable_default_schema".to_string(),
|
|
562
|
-
|
|
563
|
-
snapshot_content: Some(r#"{"id":"entity-1","status":null}"#.to_string()),
|
|
418
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1","status":null}"#)),
|
|
564
419
|
..base_stage_row()
|
|
565
420
|
};
|
|
566
421
|
|
|
567
|
-
let row =
|
|
568
|
-
|
|
569
|
-
|
|
422
|
+
let row =
|
|
423
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
424
|
+
let snapshot = normalized_snapshot(&row);
|
|
570
425
|
|
|
571
426
|
assert_eq!(snapshot["status"], JsonValue::Null);
|
|
572
427
|
}
|
|
@@ -574,17 +429,16 @@ mod tests {
|
|
|
574
429
|
#[test]
|
|
575
430
|
fn normalization_applies_timestamp_function_default() {
|
|
576
431
|
let mut catalog = catalog_with(vec![schema_with_timestamp_default()]);
|
|
577
|
-
let row =
|
|
432
|
+
let row = TransactionWriteRow {
|
|
578
433
|
entity_id: None,
|
|
579
434
|
schema_key: "timestamp_default_schema".to_string(),
|
|
580
|
-
|
|
581
|
-
snapshot_content: Some(r#"{"id":"entity-1"}"#.to_string()),
|
|
435
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1"}"#)),
|
|
582
436
|
..base_stage_row()
|
|
583
437
|
};
|
|
584
438
|
|
|
585
|
-
let row =
|
|
586
|
-
|
|
587
|
-
|
|
439
|
+
let row =
|
|
440
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
441
|
+
let snapshot = normalized_snapshot(&row);
|
|
588
442
|
|
|
589
443
|
assert_eq!(snapshot["created_at"], "1970-01-01T00:00:00.000Z");
|
|
590
444
|
}
|
|
@@ -592,16 +446,15 @@ mod tests {
|
|
|
592
446
|
#[test]
|
|
593
447
|
fn normalization_surfaces_cel_default_errors() {
|
|
594
448
|
let mut catalog = catalog_with(vec![schema_with_unknown_cel_default()]);
|
|
595
|
-
let row =
|
|
449
|
+
let row = TransactionWriteRow {
|
|
596
450
|
entity_id: None,
|
|
597
451
|
schema_key: "unknown_cel_default_schema".to_string(),
|
|
598
|
-
|
|
599
|
-
snapshot_content: Some(r#"{"id":"entity-1"}"#.to_string()),
|
|
452
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1"}"#)),
|
|
600
453
|
..base_stage_row()
|
|
601
454
|
};
|
|
602
455
|
|
|
603
|
-
let error =
|
|
604
|
-
|
|
456
|
+
let error = normalize_transaction_write_row(row, &mut catalog, functions())
|
|
457
|
+
.expect_err("default should fail");
|
|
605
458
|
|
|
606
459
|
assert!(error.message.contains("failed to evaluate x-lix-default"));
|
|
607
460
|
assert!(error.message.contains("unknown_cel_default_schema.slug"));
|
|
@@ -610,16 +463,15 @@ mod tests {
|
|
|
610
463
|
#[test]
|
|
611
464
|
fn normalization_rejects_entity_id_that_disagrees_with_primary_key() {
|
|
612
465
|
let mut catalog = catalog_with(vec![schema_with_default_id()]);
|
|
613
|
-
let row =
|
|
466
|
+
let row = TransactionWriteRow {
|
|
614
467
|
entity_id: Some(crate::entity_identity::EntityIdentity::single("wrong-id")),
|
|
615
468
|
schema_key: "normalization_schema".to_string(),
|
|
616
|
-
|
|
617
|
-
snapshot_content: Some(r#"{"id":"right-id","value":"hello"}"#.to_string()),
|
|
469
|
+
snapshot: Some(snapshot_json(r#"{"id":"right-id","value":"hello"}"#)),
|
|
618
470
|
..base_stage_row()
|
|
619
471
|
};
|
|
620
472
|
|
|
621
|
-
let error =
|
|
622
|
-
|
|
473
|
+
let error = normalize_transaction_write_row(row, &mut catalog, functions())
|
|
474
|
+
.expect_err("id mismatch fails");
|
|
623
475
|
|
|
624
476
|
assert!(error
|
|
625
477
|
.message
|
|
@@ -627,22 +479,42 @@ mod tests {
|
|
|
627
479
|
}
|
|
628
480
|
|
|
629
481
|
#[test]
|
|
630
|
-
fn
|
|
482
|
+
fn normalization_derives_json_array_entity_id_for_composite_primary_key() {
|
|
483
|
+
let mut catalog = catalog_with(vec![composite_key_schema()]);
|
|
484
|
+
let row = TransactionWriteRow {
|
|
485
|
+
entity_id: None,
|
|
486
|
+
schema_key: "composite_key_schema".to_string(),
|
|
487
|
+
snapshot: Some(snapshot_json(r#"{"namespace":"a~b","key":"1"}"#)),
|
|
488
|
+
..base_stage_row()
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
let row =
|
|
492
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
493
|
+
let entity_id = row.row.entity_id.expect("composite entity id");
|
|
494
|
+
let projected_entity_id = entity_id
|
|
495
|
+
.as_json_array_text()
|
|
496
|
+
.expect("entity id should project");
|
|
497
|
+
|
|
498
|
+
assert_eq!(projected_entity_id, "[\"a~b\",\"1\"]");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
#[test]
|
|
502
|
+
fn normalization_rejects_non_string_primary_key_values() {
|
|
631
503
|
let mut catalog = catalog_with(vec![composite_key_schema()]);
|
|
632
|
-
let row =
|
|
504
|
+
let row = TransactionWriteRow {
|
|
633
505
|
entity_id: None,
|
|
634
506
|
schema_key: "composite_key_schema".to_string(),
|
|
635
|
-
|
|
636
|
-
snapshot_content: Some(r#"{"namespace":"a~b","key":"1"}"#.to_string()),
|
|
507
|
+
snapshot: Some(snapshot_json(r#"{"namespace":"a~b","key":1}"#)),
|
|
637
508
|
..base_stage_row()
|
|
638
509
|
};
|
|
639
510
|
|
|
640
|
-
let
|
|
641
|
-
|
|
642
|
-
let projected_entity_id = entity_id.as_string().expect("entity id should project");
|
|
511
|
+
let error = normalize_transaction_write_row(row, &mut catalog, functions())
|
|
512
|
+
.expect_err("non-string primary key values should fail");
|
|
643
513
|
|
|
644
|
-
|
|
645
|
-
|
|
514
|
+
assert_eq!(error.code, LixError::CODE_SCHEMA_VALIDATION);
|
|
515
|
+
assert!(error
|
|
516
|
+
.message
|
|
517
|
+
.contains("non-string value at primary-key pointer '/key'"));
|
|
646
518
|
}
|
|
647
519
|
|
|
648
520
|
#[test]
|
|
@@ -657,17 +529,17 @@ mod tests {
|
|
|
657
529
|
&[vec!["namespace".to_string()], vec!["key".to_string()]],
|
|
658
530
|
)
|
|
659
531
|
.expect("identity should derive");
|
|
660
|
-
let row =
|
|
532
|
+
let row = TransactionWriteRow {
|
|
661
533
|
entity_id: Some(derived.clone()),
|
|
662
534
|
schema_key: "composite_key_schema".to_string(),
|
|
663
|
-
|
|
664
|
-
snapshot_content: Some(snapshot.to_string()),
|
|
535
|
+
snapshot: Some(transaction_json(snapshot.clone())),
|
|
665
536
|
..base_stage_row()
|
|
666
537
|
};
|
|
667
538
|
|
|
668
|
-
let row =
|
|
539
|
+
let row =
|
|
540
|
+
normalize_transaction_write_row(row, &mut catalog, functions()).expect("normalize row");
|
|
669
541
|
|
|
670
|
-
assert_eq!(row.entity_id.as_ref(), Some(&derived));
|
|
542
|
+
assert_eq!(row.row.entity_id.as_ref(), Some(&derived));
|
|
671
543
|
}
|
|
672
544
|
|
|
673
545
|
#[test]
|
|
@@ -675,32 +547,29 @@ mod tests {
|
|
|
675
547
|
let mut catalog = catalog_with(vec![seed_schema_definition(REGISTERED_SCHEMA_KEY)
|
|
676
548
|
.expect("registered schema builtin")
|
|
677
549
|
.clone()]);
|
|
678
|
-
let registered =
|
|
550
|
+
let registered = TransactionWriteRow {
|
|
679
551
|
entity_id: None,
|
|
680
552
|
schema_key: REGISTERED_SCHEMA_KEY.to_string(),
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
"value": dynamic_schema_definition(),
|
|
685
|
-
})
|
|
686
|
-
.to_string(),
|
|
687
|
-
),
|
|
553
|
+
snapshot: Some(transaction_json(json!({
|
|
554
|
+
"value": dynamic_schema_definition(),
|
|
555
|
+
}))),
|
|
688
556
|
..base_stage_row()
|
|
689
557
|
};
|
|
690
558
|
|
|
691
|
-
|
|
559
|
+
normalize_transaction_write_row(registered, &mut catalog, functions())
|
|
560
|
+
.expect("register schema");
|
|
692
561
|
|
|
693
|
-
let dynamic =
|
|
562
|
+
let dynamic = TransactionWriteRow {
|
|
694
563
|
entity_id: None,
|
|
695
564
|
schema_key: "dynamic_schema".to_string(),
|
|
696
|
-
|
|
697
|
-
snapshot_content: Some(r#"{"id":"dynamic-1"}"#.to_string()),
|
|
565
|
+
snapshot: Some(snapshot_json(r#"{"id":"dynamic-1"}"#)),
|
|
698
566
|
..base_stage_row()
|
|
699
567
|
};
|
|
700
|
-
let dynamic =
|
|
568
|
+
let dynamic = normalize_transaction_write_row(dynamic, &mut catalog, functions())
|
|
569
|
+
.expect("dynamic row");
|
|
701
570
|
|
|
702
571
|
assert_eq!(
|
|
703
|
-
dynamic.entity_id.as_ref(),
|
|
572
|
+
dynamic.row.entity_id.as_ref(),
|
|
704
573
|
Some(&crate::entity_identity::EntityIdentity::single("dynamic-1"))
|
|
705
574
|
);
|
|
706
575
|
}
|
|
@@ -712,43 +581,36 @@ mod tests {
|
|
|
712
581
|
builtin_schema(DIRECTORY_DESCRIPTOR_SCHEMA_KEY),
|
|
713
582
|
]);
|
|
714
583
|
|
|
715
|
-
let file =
|
|
584
|
+
let file = TransactionWriteRow {
|
|
716
585
|
entity_id: None,
|
|
717
586
|
schema_key: FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
})
|
|
724
|
-
.to_string(),
|
|
725
|
-
),
|
|
587
|
+
snapshot: Some(transaction_json(json!({
|
|
588
|
+
"id": "file-cafe",
|
|
589
|
+
"directory_id": null,
|
|
590
|
+
"name": "Cafe\u{301}.txt",
|
|
591
|
+
}))),
|
|
726
592
|
global: false,
|
|
727
593
|
..base_stage_row()
|
|
728
594
|
};
|
|
729
|
-
let file =
|
|
730
|
-
|
|
731
|
-
|
|
595
|
+
let file = normalize_transaction_write_row(file, &mut catalog, functions())
|
|
596
|
+
.expect("normalize file");
|
|
597
|
+
let file_snapshot = normalized_snapshot(&file);
|
|
732
598
|
assert_eq!(file_snapshot["name"], "Café.txt");
|
|
733
599
|
|
|
734
|
-
let directory =
|
|
600
|
+
let directory = TransactionWriteRow {
|
|
735
601
|
entity_id: None,
|
|
736
602
|
schema_key: DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
})
|
|
743
|
-
.to_string(),
|
|
744
|
-
),
|
|
603
|
+
snapshot: Some(transaction_json(json!({
|
|
604
|
+
"id": "dir-cafe",
|
|
605
|
+
"parent_id": null,
|
|
606
|
+
"name": "Cafe\u{301}",
|
|
607
|
+
}))),
|
|
745
608
|
global: false,
|
|
746
609
|
..base_stage_row()
|
|
747
610
|
};
|
|
748
|
-
let directory =
|
|
749
|
-
|
|
750
|
-
let directory_snapshot
|
|
751
|
-
serde_json::from_str(directory.snapshot_content.as_deref().unwrap()).unwrap();
|
|
611
|
+
let directory = normalize_transaction_write_row(directory, &mut catalog, functions())
|
|
612
|
+
.expect("normalize directory");
|
|
613
|
+
let directory_snapshot = normalized_snapshot(&directory);
|
|
752
614
|
assert_eq!(directory_snapshot["name"], "Café");
|
|
753
615
|
}
|
|
754
616
|
|
|
@@ -759,18 +621,15 @@ mod tests {
|
|
|
759
621
|
builtin_schema(DIRECTORY_DESCRIPTOR_SCHEMA_KEY),
|
|
760
622
|
]);
|
|
761
623
|
|
|
762
|
-
let dot_segment =
|
|
763
|
-
|
|
624
|
+
let dot_segment = normalize_transaction_write_row(
|
|
625
|
+
TransactionWriteRow {
|
|
764
626
|
entity_id: None,
|
|
765
627
|
schema_key: FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
})
|
|
772
|
-
.to_string(),
|
|
773
|
-
),
|
|
628
|
+
snapshot: Some(transaction_json(json!({
|
|
629
|
+
"id": "file-dotdot",
|
|
630
|
+
"directory_id": null,
|
|
631
|
+
"name": "..",
|
|
632
|
+
}))),
|
|
774
633
|
global: false,
|
|
775
634
|
..base_stage_row()
|
|
776
635
|
},
|
|
@@ -780,18 +639,15 @@ mod tests {
|
|
|
780
639
|
.expect_err("file descriptor name should reject dot segments");
|
|
781
640
|
assert_eq!(dot_segment.code, "LIX_ERROR_PATH_DOT_SEGMENT");
|
|
782
641
|
|
|
783
|
-
let bidi =
|
|
784
|
-
|
|
642
|
+
let bidi = normalize_transaction_write_row(
|
|
643
|
+
TransactionWriteRow {
|
|
785
644
|
entity_id: None,
|
|
786
645
|
schema_key: FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
})
|
|
793
|
-
.to_string(),
|
|
794
|
-
),
|
|
646
|
+
snapshot: Some(transaction_json(json!({
|
|
647
|
+
"id": "file-bidi",
|
|
648
|
+
"directory_id": null,
|
|
649
|
+
"name": "safe\u{202E}txt",
|
|
650
|
+
}))),
|
|
795
651
|
global: false,
|
|
796
652
|
..base_stage_row()
|
|
797
653
|
},
|
|
@@ -801,18 +657,15 @@ mod tests {
|
|
|
801
657
|
.expect_err("file descriptor name should reject bidi formatting characters");
|
|
802
658
|
assert_eq!(bidi.code, "LIX_ERROR_PATH_INVALID_SEGMENT_CODE_POINT");
|
|
803
659
|
|
|
804
|
-
let zero_width =
|
|
805
|
-
|
|
660
|
+
let zero_width = normalize_transaction_write_row(
|
|
661
|
+
TransactionWriteRow {
|
|
806
662
|
entity_id: None,
|
|
807
663
|
schema_key: DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
})
|
|
814
|
-
.to_string(),
|
|
815
|
-
),
|
|
664
|
+
snapshot: Some(transaction_json(json!({
|
|
665
|
+
"id": "dir-zero-width",
|
|
666
|
+
"parent_id": null,
|
|
667
|
+
"name": "zero\u{200D}width",
|
|
668
|
+
}))),
|
|
816
669
|
global: false,
|
|
817
670
|
..base_stage_row()
|
|
818
671
|
},
|
|
@@ -827,18 +680,15 @@ mod tests {
|
|
|
827
680
|
fn normalization_keeps_file_descriptor_name_opaque() {
|
|
828
681
|
let mut catalog = catalog_with(vec![builtin_schema(FILE_DESCRIPTOR_SCHEMA_KEY)]);
|
|
829
682
|
|
|
830
|
-
let row =
|
|
831
|
-
|
|
683
|
+
let row = normalize_transaction_write_row(
|
|
684
|
+
TransactionWriteRow {
|
|
832
685
|
entity_id: None,
|
|
833
686
|
schema_key: FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
})
|
|
840
|
-
.to_string(),
|
|
841
|
-
),
|
|
687
|
+
snapshot: Some(transaction_json(json!({
|
|
688
|
+
"id": "file-opaque-name",
|
|
689
|
+
"directory_id": null,
|
|
690
|
+
"name": "foo.bar",
|
|
691
|
+
}))),
|
|
842
692
|
global: false,
|
|
843
693
|
..base_stage_row()
|
|
844
694
|
},
|
|
@@ -847,13 +697,28 @@ mod tests {
|
|
|
847
697
|
)
|
|
848
698
|
.expect("file descriptor name should be an opaque basename");
|
|
849
699
|
|
|
850
|
-
let snapshot
|
|
851
|
-
serde_json::from_str(row.snapshot_content.as_deref().unwrap()).unwrap();
|
|
700
|
+
let snapshot = normalized_snapshot(&row);
|
|
852
701
|
assert_eq!(snapshot["name"], "foo.bar");
|
|
853
702
|
}
|
|
854
703
|
|
|
855
|
-
fn
|
|
856
|
-
|
|
704
|
+
fn normalized_snapshot(row: &NormalizedTransactionWriteRow) -> &JsonValue {
|
|
705
|
+
row.snapshot
|
|
706
|
+
.as_ref()
|
|
707
|
+
.expect("normalized test row should have a snapshot")
|
|
708
|
+
.value()
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
fn catalog_with(schemas: Vec<JsonValue>) -> CatalogSnapshot {
|
|
712
|
+
let mut visible_schemas = schemas;
|
|
713
|
+
if visible_schemas.iter().any(|schema| {
|
|
714
|
+
schema.get("x-lix-key").and_then(JsonValue::as_str) == Some(FILE_DESCRIPTOR_SCHEMA_KEY)
|
|
715
|
+
}) && !visible_schemas.iter().any(|schema| {
|
|
716
|
+
schema.get("x-lix-key").and_then(JsonValue::as_str)
|
|
717
|
+
== Some(DIRECTORY_DESCRIPTOR_SCHEMA_KEY)
|
|
718
|
+
}) {
|
|
719
|
+
visible_schemas.push(builtin_schema(DIRECTORY_DESCRIPTOR_SCHEMA_KEY));
|
|
720
|
+
}
|
|
721
|
+
CatalogSnapshot::from_visible_schemas(&visible_schemas).expect("catalog")
|
|
857
722
|
}
|
|
858
723
|
|
|
859
724
|
fn builtin_schema(schema_key: &str) -> JsonValue {
|
|
@@ -862,15 +727,22 @@ mod tests {
|
|
|
862
727
|
.clone()
|
|
863
728
|
}
|
|
864
729
|
|
|
865
|
-
fn
|
|
866
|
-
|
|
730
|
+
fn transaction_json(value: JsonValue) -> TransactionJson {
|
|
731
|
+
TransactionJson::from_value_for_test(value)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
fn snapshot_json(value: &str) -> TransactionJson {
|
|
735
|
+
transaction_json(serde_json::from_str(value).expect("test snapshot should parse"))
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
fn base_stage_row() -> TransactionWriteRow {
|
|
739
|
+
TransactionWriteRow {
|
|
867
740
|
entity_id: Some(crate::entity_identity::EntityIdentity::single("entity-1")),
|
|
868
741
|
schema_key: "normalization_schema".to_string(),
|
|
869
742
|
file_id: None,
|
|
870
|
-
|
|
743
|
+
snapshot: Some(snapshot_json(r#"{"id":"entity-1","value":"hello"}"#)),
|
|
871
744
|
metadata: None,
|
|
872
745
|
origin: None,
|
|
873
|
-
schema_version: "1".to_string(),
|
|
874
746
|
created_at: None,
|
|
875
747
|
updated_at: None,
|
|
876
748
|
global: true,
|
|
@@ -884,7 +756,6 @@ mod tests {
|
|
|
884
756
|
fn schema_with_default_id() -> JsonValue {
|
|
885
757
|
json!({
|
|
886
758
|
"x-lix-key": "normalization_schema",
|
|
887
|
-
"x-lix-version": "1",
|
|
888
759
|
"x-lix-primary-key": ["/id"],
|
|
889
760
|
"type": "object",
|
|
890
761
|
"properties": {
|
|
@@ -899,7 +770,6 @@ mod tests {
|
|
|
899
770
|
fn schema_with_cel_field_default() -> JsonValue {
|
|
900
771
|
json!({
|
|
901
772
|
"x-lix-key": "cel_field_default_schema",
|
|
902
|
-
"x-lix-version": "1",
|
|
903
773
|
"x-lix-primary-key": ["/id"],
|
|
904
774
|
"type": "object",
|
|
905
775
|
"properties": {
|
|
@@ -915,7 +785,6 @@ mod tests {
|
|
|
915
785
|
fn schema_with_overridden_default() -> JsonValue {
|
|
916
786
|
json!({
|
|
917
787
|
"x-lix-key": "overridden_default_schema",
|
|
918
|
-
"x-lix-version": "1",
|
|
919
788
|
"x-lix-primary-key": ["/id"],
|
|
920
789
|
"type": "object",
|
|
921
790
|
"properties": {
|
|
@@ -934,7 +803,6 @@ mod tests {
|
|
|
934
803
|
fn schema_with_nullable_default() -> JsonValue {
|
|
935
804
|
json!({
|
|
936
805
|
"x-lix-key": "nullable_default_schema",
|
|
937
|
-
"x-lix-version": "1",
|
|
938
806
|
"x-lix-primary-key": ["/id"],
|
|
939
807
|
"type": "object",
|
|
940
808
|
"properties": {
|
|
@@ -952,7 +820,6 @@ mod tests {
|
|
|
952
820
|
fn schema_with_timestamp_default() -> JsonValue {
|
|
953
821
|
json!({
|
|
954
822
|
"x-lix-key": "timestamp_default_schema",
|
|
955
|
-
"x-lix-version": "1",
|
|
956
823
|
"x-lix-primary-key": ["/id"],
|
|
957
824
|
"type": "object",
|
|
958
825
|
"properties": {
|
|
@@ -967,7 +834,6 @@ mod tests {
|
|
|
967
834
|
fn schema_with_unknown_cel_default() -> JsonValue {
|
|
968
835
|
json!({
|
|
969
836
|
"x-lix-key": "unknown_cel_default_schema",
|
|
970
|
-
"x-lix-version": "1",
|
|
971
837
|
"x-lix-primary-key": ["/id"],
|
|
972
838
|
"type": "object",
|
|
973
839
|
"properties": {
|
|
@@ -982,7 +848,6 @@ mod tests {
|
|
|
982
848
|
fn composite_key_schema() -> JsonValue {
|
|
983
849
|
json!({
|
|
984
850
|
"x-lix-key": "composite_key_schema",
|
|
985
|
-
"x-lix-version": "1",
|
|
986
851
|
"x-lix-primary-key": ["/namespace", "/key"],
|
|
987
852
|
"type": "object",
|
|
988
853
|
"properties": {
|
|
@@ -997,7 +862,6 @@ mod tests {
|
|
|
997
862
|
fn dynamic_schema_definition() -> JsonValue {
|
|
998
863
|
json!({
|
|
999
864
|
"x-lix-key": "dynamic_schema",
|
|
1000
|
-
"x-lix-version": "1",
|
|
1001
865
|
"x-lix-primary-key": ["/id"],
|
|
1002
866
|
"type": "object",
|
|
1003
867
|
"properties": {
|