Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61a8c5eed7 | |||
| 77af67aef5 | |||
| cd85a8a2c3 | |||
| d3cb72a5e2 |
@ -869,6 +869,106 @@
|
|||||||
"notify": true,
|
"notify": true,
|
||||||
"relationship": false
|
"relationship": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"hierarchy": [
|
||||||
|
"account",
|
||||||
|
"entity"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"name",
|
||||||
|
"archived",
|
||||||
|
"created_at",
|
||||||
|
"created_by",
|
||||||
|
"modified_at",
|
||||||
|
"modified_by",
|
||||||
|
"kind",
|
||||||
|
"routing_number",
|
||||||
|
"card_number"
|
||||||
|
],
|
||||||
|
"grouped_fields": {
|
||||||
|
"entity": [
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"name",
|
||||||
|
"archived",
|
||||||
|
"created_at",
|
||||||
|
"created_by",
|
||||||
|
"modified_at",
|
||||||
|
"modified_by"
|
||||||
|
],
|
||||||
|
"account": [
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"kind",
|
||||||
|
"routing_number",
|
||||||
|
"card_number"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"field_types": {
|
||||||
|
"id": "uuid",
|
||||||
|
"type": "text",
|
||||||
|
"archived": "boolean",
|
||||||
|
"name": "text",
|
||||||
|
"created_at": "timestamptz",
|
||||||
|
"created_by": "uuid",
|
||||||
|
"modified_at": "timestamptz",
|
||||||
|
"modified_by": "uuid",
|
||||||
|
"kind": "text",
|
||||||
|
"routing_number": "text",
|
||||||
|
"card_number": "text"
|
||||||
|
},
|
||||||
|
"schemas": {
|
||||||
|
"account": {
|
||||||
|
"type": "entity",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "checking"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"properties": {
|
||||||
|
"routing_number": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "credit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"properties": {
|
||||||
|
"card_number": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lookup_fields": [],
|
||||||
|
"historical": true,
|
||||||
|
"notify": true,
|
||||||
|
"relationship": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "invoice",
|
"name": "invoice",
|
||||||
"schemas": {
|
"schemas": {
|
||||||
@ -3107,6 +3207,103 @@
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Insert account with checking kind and routing number",
|
||||||
|
"action": "merge",
|
||||||
|
"schema_id": "account",
|
||||||
|
"data": {
|
||||||
|
"id": "11111111-2222-3333-4444-555555555555",
|
||||||
|
"type": "account",
|
||||||
|
"kind": "checking",
|
||||||
|
"routing_number": "123456789"
|
||||||
|
},
|
||||||
|
"expect": {
|
||||||
|
"success": true,
|
||||||
|
"sql": [
|
||||||
|
[
|
||||||
|
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
|
||||||
|
"FROM agreego.\"account\" t1",
|
||||||
|
"LEFT JOIN agreego.\"entity\" t2 ON t2.id = t1.id",
|
||||||
|
"WHERE t1.id = '11111111-2222-3333-4444-555555555555'"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"INSERT INTO agreego.\"entity\" (",
|
||||||
|
" \"created_at\",",
|
||||||
|
" \"created_by\",",
|
||||||
|
" \"id\",",
|
||||||
|
" \"modified_at\",",
|
||||||
|
" \"modified_by\",",
|
||||||
|
" \"type\"",
|
||||||
|
")",
|
||||||
|
"VALUES (",
|
||||||
|
" '{{timestamp}}',",
|
||||||
|
" '00000000-0000-0000-0000-000000000000',",
|
||||||
|
" '11111111-2222-3333-4444-555555555555',",
|
||||||
|
" '{{timestamp}}',",
|
||||||
|
" '00000000-0000-0000-0000-000000000000',",
|
||||||
|
" 'account'",
|
||||||
|
")"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"INSERT INTO agreego.\"account\" (",
|
||||||
|
" \"id\",",
|
||||||
|
" \"kind\",",
|
||||||
|
" \"routing_number\",",
|
||||||
|
" \"type\"",
|
||||||
|
")",
|
||||||
|
"VALUES (",
|
||||||
|
" '11111111-2222-3333-4444-555555555555',",
|
||||||
|
" 'checking',",
|
||||||
|
" '123456789',",
|
||||||
|
" 'account'",
|
||||||
|
")"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"INSERT INTO agreego.change (",
|
||||||
|
" \"old\",",
|
||||||
|
" \"new\",",
|
||||||
|
" entity_id,",
|
||||||
|
" id,",
|
||||||
|
" kind,",
|
||||||
|
" modified_at,",
|
||||||
|
" modified_by",
|
||||||
|
")",
|
||||||
|
"VALUES (",
|
||||||
|
" NULL,",
|
||||||
|
" '{",
|
||||||
|
" \"kind\":\"checking\",",
|
||||||
|
" \"routing_number\":\"123456789\",",
|
||||||
|
" \"type\":\"account\"",
|
||||||
|
" }',",
|
||||||
|
" '11111111-2222-3333-4444-555555555555',",
|
||||||
|
" '{{uuid}}',",
|
||||||
|
" 'create',",
|
||||||
|
" '{{timestamp}}',",
|
||||||
|
" '00000000-0000-0000-0000-000000000000'",
|
||||||
|
")"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"SELECT pg_notify('entity', '{",
|
||||||
|
" \"complete\":{",
|
||||||
|
" \"created_at\":\"{{timestamp}}\",",
|
||||||
|
" \"created_by\":\"00000000-0000-0000-0000-000000000000\",",
|
||||||
|
" \"id\":\"11111111-2222-3333-4444-555555555555\",",
|
||||||
|
" \"kind\":\"checking\",",
|
||||||
|
" \"modified_at\":\"{{timestamp}}\",",
|
||||||
|
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
|
||||||
|
" \"routing_number\":\"123456789\",",
|
||||||
|
" \"type\":\"account\"",
|
||||||
|
" },",
|
||||||
|
" \"new\":{",
|
||||||
|
" \"kind\":\"checking\",",
|
||||||
|
" \"routing_number\":\"123456789\",",
|
||||||
|
" \"type\":\"account\"",
|
||||||
|
" }",
|
||||||
|
" }')"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,110 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "STI Projections (Lacking Kind Discriminator Definitions)",
|
||||||
|
"database": {
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"name": "widget",
|
||||||
|
"variations": [
|
||||||
|
"widget"
|
||||||
|
],
|
||||||
|
"schemas": {
|
||||||
|
"widget": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stock.widget": {
|
||||||
|
"type": "widget",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projected.widget": {
|
||||||
|
"type": "widget",
|
||||||
|
"properties": {
|
||||||
|
"alias": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemas": {
|
||||||
|
"stock_widget_validation": {
|
||||||
|
"type": "stock.widget"
|
||||||
|
},
|
||||||
|
"projected_widget_validation": {
|
||||||
|
"type": "projected.widget"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "stock.widget securely expects kind when configured",
|
||||||
|
"schema_id": "stock_widget_validation",
|
||||||
|
"data": {
|
||||||
|
"type": "widget",
|
||||||
|
"amount": 5
|
||||||
|
},
|
||||||
|
"action": "validate",
|
||||||
|
"expect": {
|
||||||
|
"success": false,
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"code": "MISSING_KIND",
|
||||||
|
"details": {
|
||||||
|
"path": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "projected.widget seamlessly bypasses kind expectation when excluded from schema",
|
||||||
|
"schema_id": "projected_widget_validation",
|
||||||
|
"data": {
|
||||||
|
"type": "widget",
|
||||||
|
"alias": "Test Projection"
|
||||||
|
},
|
||||||
|
"action": "validate",
|
||||||
|
"expect": {
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "projected.widget securely fails if user erroneously provides extra kind property",
|
||||||
|
"schema_id": "projected_widget_validation",
|
||||||
|
"data": {
|
||||||
|
"type": "widget",
|
||||||
|
"alias": "Test Projection",
|
||||||
|
"kind": "projected"
|
||||||
|
},
|
||||||
|
"action": "validate",
|
||||||
|
"expect": {
|
||||||
|
"success": false,
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"code": "STRICT_PROPERTY_VIOLATION",
|
||||||
|
"details": {
|
||||||
|
"path": "kind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -924,6 +924,91 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"hierarchy": [
|
||||||
|
"account",
|
||||||
|
"entity"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"kind",
|
||||||
|
"archived",
|
||||||
|
"created_at",
|
||||||
|
"routing_number",
|
||||||
|
"card_number"
|
||||||
|
],
|
||||||
|
"grouped_fields": {
|
||||||
|
"entity": [
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
"archived",
|
||||||
|
"created_at"
|
||||||
|
],
|
||||||
|
"account": [
|
||||||
|
"kind",
|
||||||
|
"routing_number",
|
||||||
|
"card_number"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"field_types": {
|
||||||
|
"id": "uuid",
|
||||||
|
"type": "text",
|
||||||
|
"kind": "text",
|
||||||
|
"archived": "boolean",
|
||||||
|
"created_at": "timestamptz",
|
||||||
|
"routing_number": "text",
|
||||||
|
"card_number": "text"
|
||||||
|
},
|
||||||
|
"variations": [
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"schemas": {
|
||||||
|
"account": {
|
||||||
|
"type": "entity",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "checking"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"properties": {
|
||||||
|
"routing_number": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "credit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"properties": {
|
||||||
|
"card_number": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "invoice",
|
"name": "invoice",
|
||||||
"schemas": {
|
"schemas": {
|
||||||
@ -2149,6 +2234,30 @@
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Account select on full schema with cases fields",
|
||||||
|
"action": "query",
|
||||||
|
"schema_id": "account",
|
||||||
|
"expect": {
|
||||||
|
"success": true,
|
||||||
|
"sql": [
|
||||||
|
[
|
||||||
|
"(SELECT jsonb_strip_nulls((SELECT jsonb_build_object(",
|
||||||
|
" 'archived', entity_2.archived,",
|
||||||
|
" 'card_number', account_1.card_number,",
|
||||||
|
" 'created_at', entity_2.created_at,",
|
||||||
|
" 'id', entity_2.id,",
|
||||||
|
" 'kind', account_1.kind,",
|
||||||
|
" 'routing_number', account_1.routing_number,",
|
||||||
|
" 'type', entity_2.type",
|
||||||
|
")",
|
||||||
|
"FROM agreego.account account_1",
|
||||||
|
"JOIN agreego.entity entity_2 ON entity_2.id = account_1.id",
|
||||||
|
"WHERE NOT entity_2.archived)))"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,7 @@ pub struct SchemaObject {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub extensible: Option<bool>,
|
pub extensible: Option<bool>,
|
||||||
|
|
||||||
#[serde(rename = "compiledProperties")]
|
#[serde(rename = "compiledPropertyNames")]
|
||||||
#[serde(skip_deserializing)]
|
#[serde(skip_deserializing)]
|
||||||
#[serde(skip_serializing_if = "crate::database::object::is_once_lock_vec_empty")]
|
#[serde(skip_serializing_if = "crate::database::object::is_once_lock_vec_empty")]
|
||||||
#[serde(serialize_with = "crate::database::object::serialize_once_lock")]
|
#[serde(serialize_with = "crate::database::object::serialize_once_lock")]
|
||||||
|
|||||||
@ -117,13 +117,34 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Set the OnceLock!
|
// 3. Add cases conditionally-defined properties recursively
|
||||||
|
if let Some(cases) = &self.obj.cases {
|
||||||
|
for (i, c) in cases.iter().enumerate() {
|
||||||
|
if let Some(child) = &c.when {
|
||||||
|
child.compile(db, root_id, format!("{}/cases/{}/when", path, i), errors);
|
||||||
|
}
|
||||||
|
if let Some(child) = &c.then {
|
||||||
|
child.compile(db, root_id, format!("{}/cases/{}/then", path, i), errors);
|
||||||
|
if let Some(t_props) = child.obj.compiled_properties.get() {
|
||||||
|
props.extend(t_props.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(child) = &c.else_ {
|
||||||
|
child.compile(db, root_id, format!("{}/cases/{}/else", path, i), errors);
|
||||||
|
if let Some(e_props) = child.obj.compiled_properties.get() {
|
||||||
|
props.extend(e_props.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Set the OnceLock!
|
||||||
let _ = self.obj.compiled_properties.set(props.clone());
|
let _ = self.obj.compiled_properties.set(props.clone());
|
||||||
let mut names: Vec<String> = props.keys().cloned().collect();
|
let mut names: Vec<String> = props.keys().cloned().collect();
|
||||||
names.sort();
|
names.sort();
|
||||||
let _ = self.obj.compiled_property_names.set(names);
|
let _ = self.obj.compiled_property_names.set(names);
|
||||||
|
|
||||||
// 4. Compute Edges natively
|
// 5. Compute Edges natively
|
||||||
let schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
|
let schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
|
||||||
let _ = self.obj.compiled_edges.set(schema_edges);
|
let _ = self.obj.compiled_edges.set(schema_edges);
|
||||||
|
|
||||||
@ -151,22 +172,12 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
if let Some(one_of) = &self.obj.one_of {
|
if let Some(one_of) = &self.obj.one_of {
|
||||||
for (i, child) in one_of.iter().enumerate() {
|
for (i, child) in one_of.iter().enumerate() {
|
||||||
child.compile(
|
child.compile(db, root_id, format!("{}/oneOf/{}", path, i), errors);
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/oneOf/{}", path, i),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(arr) = &self.obj.prefix_items {
|
if let Some(arr) = &self.obj.prefix_items {
|
||||||
for (i, child) in arr.iter().enumerate() {
|
for (i, child) in arr.iter().enumerate() {
|
||||||
child.compile(
|
child.compile(db, root_id, format!("{}/prefixItems/{}", path, i), errors);
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/prefixItems/{}", path, i),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(child) = &self.obj.not {
|
if let Some(child) = &self.obj.not {
|
||||||
@ -175,34 +186,6 @@ impl Schema {
|
|||||||
if let Some(child) = &self.obj.contains {
|
if let Some(child) = &self.obj.contains {
|
||||||
child.compile(db, root_id, format!("{}/contains", path), errors);
|
child.compile(db, root_id, format!("{}/contains", path), errors);
|
||||||
}
|
}
|
||||||
if let Some(cases) = &self.obj.cases {
|
|
||||||
for (i, c) in cases.iter().enumerate() {
|
|
||||||
if let Some(child) = &c.when {
|
|
||||||
child.compile(
|
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/cases/{}/when", path, i),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(child) = &c.then {
|
|
||||||
child.compile(
|
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/cases/{}/then", path, i),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(child) = &c.else_ {
|
|
||||||
child.compile(
|
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/cases/{}/else", path, i),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.compile_polymorphism(db, root_id, &path, errors);
|
self.compile_polymorphism(db, root_id, &path, errors);
|
||||||
}
|
}
|
||||||
@ -285,7 +268,9 @@ impl Schema {
|
|||||||
if let Some(c_type) = child_type_name {
|
if let Some(c_type) = child_type_name {
|
||||||
// Skip edge compilation for JSONB columns — they store data inline, not relationally.
|
// Skip edge compilation for JSONB columns — they store data inline, not relationally.
|
||||||
// The physical column type from field_types is the single source of truth.
|
// The physical column type from field_types is the single source of truth.
|
||||||
if let Some(ft) = type_def.field_types.as_ref()
|
if let Some(ft) = type_def
|
||||||
|
.field_types
|
||||||
|
.as_ref()
|
||||||
.and_then(|v| v.get(prop_name.as_str()))
|
.and_then(|v| v.get(prop_name.as_str()))
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
{
|
{
|
||||||
@ -296,12 +281,7 @@ impl Schema {
|
|||||||
if db.types.contains_key(&c_type) {
|
if db.types.contains_key(&c_type) {
|
||||||
// Ensure the child Schema's AST has accurately compiled its own physical property keys so we can
|
// Ensure the child Schema's AST has accurately compiled its own physical property keys so we can
|
||||||
// inject them securely for Many-to-Many Twin Deduction disambiguation matching.
|
// inject them securely for Many-to-Many Twin Deduction disambiguation matching.
|
||||||
target_schema.compile(
|
target_schema.compile(db, root_id, format!("{}/{}", path, prop_name), errors);
|
||||||
db,
|
|
||||||
root_id,
|
|
||||||
format!("{}/{}", path, prop_name),
|
|
||||||
errors,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(compiled_target_props) = target_schema.obj.compiled_properties.get() {
|
if let Some(compiled_target_props) = target_schema.obj.compiled_properties.get() {
|
||||||
let keys_for_ambiguity: Vec<String> =
|
let keys_for_ambiguity: Vec<String> =
|
||||||
|
|||||||
@ -1457,6 +1457,12 @@ fn test_queryer_0_13() {
|
|||||||
crate::tests::runner::run_test_case(&path, 0, 13).unwrap();
|
crate::tests::runner::run_test_case(&path, 0, 13).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_queryer_0_14() {
|
||||||
|
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::tests::runner::run_test_case(&path, 0, 14).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_polymorphism_0_0() {
|
fn test_polymorphism_0_0() {
|
||||||
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
@ -1559,6 +1565,24 @@ fn test_polymorphism_4_1() {
|
|||||||
crate::tests::runner::run_test_case(&path, 4, 1).unwrap();
|
crate::tests::runner::run_test_case(&path, 4, 1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_polymorphism_5_0() {
|
||||||
|
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::tests::runner::run_test_case(&path, 5, 0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_polymorphism_5_1() {
|
||||||
|
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::tests::runner::run_test_case(&path, 5, 1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_polymorphism_5_2() {
|
||||||
|
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::tests::runner::run_test_case(&path, 5, 2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not_0_0() {
|
fn test_not_0_0() {
|
||||||
let path = format!("{}/fixtures/not.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/fixtures/not.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
@ -8104,3 +8128,9 @@ fn test_merger_0_13() {
|
|||||||
let path = format!("{}/fixtures/merger.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/fixtures/merger.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
crate::tests::runner::run_test_case(&path, 0, 13).unwrap();
|
crate::tests::runner::run_test_case(&path, 0, 13).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_merger_0_14() {
|
||||||
|
let path = format!("{}/fixtures/merger.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::tests::runner::run_test_case(&path, 0, 14).unwrap();
|
||||||
|
}
|
||||||
|
|||||||
@ -95,11 +95,11 @@ fn test_library_api() {
|
|||||||
"name": { "type": "string" },
|
"name": { "type": "string" },
|
||||||
"target": {
|
"target": {
|
||||||
"type": "target_schema",
|
"type": "target_schema",
|
||||||
"compiledProperties": ["value"]
|
"compiledPropertyNames": ["value"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
"compiledProperties": ["name", "target", "type"],
|
"compiledPropertyNames": ["name", "target", "type"],
|
||||||
"compiledEdges": {
|
"compiledEdges": {
|
||||||
"target": {
|
"target": {
|
||||||
"constraint": "fk_test_target",
|
"constraint": "fk_test_target",
|
||||||
@ -112,7 +112,7 @@ fn test_library_api() {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"value": { "type": "number" }
|
"value": { "type": "number" }
|
||||||
},
|
},
|
||||||
"compiledProperties": ["value"]
|
"compiledPropertyNames": ["value"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,9 +24,6 @@ impl<'a> ValidationContext<'a> {
|
|||||||
|
|
||||||
if let Some(obj) = self.instance.as_object() {
|
if let Some(obj) = self.instance.as_object() {
|
||||||
for key in obj.keys() {
|
for key in obj.keys() {
|
||||||
if key == "type" || key == "kind" {
|
|
||||||
continue; // Reserved keywords implicitly allowed
|
|
||||||
}
|
|
||||||
if !result.evaluated_keys.contains(key) && !self.overrides.contains(key) {
|
if !result.evaluated_keys.contains(key) && !self.overrides.contains(key) {
|
||||||
result.errors.push(ValidationError {
|
result.errors.push(ValidationError {
|
||||||
code: "STRICT_PROPERTY_VIOLATION".to_string(),
|
code: "STRICT_PROPERTY_VIOLATION".to_string(),
|
||||||
|
|||||||
@ -54,14 +54,19 @@ impl<'a> ValidationContext<'a> {
|
|||||||
|
|
||||||
// If the target mathematically declares a horizontal structural STI variation natively
|
// If the target mathematically declares a horizontal structural STI variation natively
|
||||||
if schema_identifier_str.contains('.') {
|
if schema_identifier_str.contains('.') {
|
||||||
if obj.get("kind").is_none() {
|
let requires_kind = self.schema.compiled_properties.get()
|
||||||
result.errors.push(ValidationError {
|
.map_or(false, |p| p.contains_key("kind"));
|
||||||
code: "MISSING_KIND".to_string(),
|
|
||||||
message: "Schema mechanically requires horizontal kind discrimination".to_string(),
|
if requires_kind {
|
||||||
path: self.path.clone(),
|
if obj.get("kind").is_none() {
|
||||||
});
|
result.errors.push(ValidationError {
|
||||||
} else {
|
code: "MISSING_KIND".to_string(),
|
||||||
result.evaluated_keys.insert("kind".to_string());
|
message: "Schema mechanically requires horizontal kind discrimination".to_string(),
|
||||||
|
path: self.path.clone(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.evaluated_keys.insert("kind".to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user