Compare commits

...

9 Commits

4 changed files with 473 additions and 50 deletions

View File

@ -146,6 +146,9 @@
"modified_at": {
"type": "string",
"format": "date-time"
},
"organization_id": {
"type": "string"
}
},
"required": [
@ -168,7 +171,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
],
"grouped_fields": {
"entity": [
@ -178,7 +182,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
]
},
"lookup_fields": [],
@ -345,6 +350,10 @@
}
}
}
},
"organization_id": {
"type": "string",
"const": "ffffffff-ffff-ffff-ffff-ffffffffffff"
}
}
}
@ -368,7 +377,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
],
"grouped_fields": {
"person": [
@ -396,7 +406,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
]
},
"lookup_fields": [
@ -446,7 +457,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
],
"grouped_fields": {
"order": [
@ -462,7 +474,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
]
},
"lookup_fields": [
@ -504,7 +517,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
],
"grouped_fields": {
"order_line": [
@ -521,7 +535,8 @@
"created_by",
"modified_at",
"modified_by",
"archived"
"archived",
"organization_id"
]
},
"lookup_fields": [],
@ -1791,6 +1806,7 @@
" \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"organization_id\",",
" \"type\"",
")",
"VALUES (",
@ -1799,6 +1815,7 @@
" '{{uuid:customer_id}}',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'ffffffff-ffff-ffff-ffff-ffffffffffff',",
" 'person'",
")"
],
@ -1854,6 +1871,7 @@
" \"date_of_birth\":\"2000-01-01\",",
" \"first_name\":\"Bob\",",
" \"last_name\":\"Smith\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" }',",
" '{{uuid:customer_id}}',",
@ -1949,12 +1967,14 @@
" \"last_name\":\"Smith\",",
" \"modified_at\":\"{{timestamp}}\",",
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" },",
" \"new\":{",
" \"date_of_birth\":\"2000-01-01\",",
" \"first_name\":\"Bob\",",
" \"last_name\":\"Smith\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" }",
" }')"
@ -3126,10 +3146,26 @@
"type": "invoice",
"number": "INV-1001",
"total": 200.0,
"metadata_line": {"price": 50},
"metadata_lines": [{"price": 25}],
"metadata_nested_line": {"line": {"price": 75}},
"metadata_nested_lines": {"lines": [{"price": 100}]}
"metadata_line": {
"price": 50
},
"metadata_lines": [
{
"price": 25
}
],
"metadata_nested_line": {
"line": {
"price": 75
}
},
"metadata_nested_lines": {
"lines": [
{
"price": 100
}
]
}
},
"expect": {
"success": true,
@ -3304,6 +3340,359 @@
]
]
}
},
{
"description": "Test organization_id syntactic sugar permutations",
"action": "merge",
"data": {
"type": "order",
"organization_id": "parent-org-id",
"customer": {
"type": "person",
"first_name": "Const",
"last_name": "Person"
},
"lines": [
{
"type": "order_line"
},
{
"type": "order_line",
"organization_id": "explicit-org-id"
}
]
},
"schema_id": "order",
"expect": {
"success": true,
"sql": [
[
"INSERT INTO agreego.\"entity\" (",
" \"created_at\",",
" \"created_by\",",
" \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"organization_id\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '{{uuid}}',",
" '{{uuid:person_id}}',",
" '{{timestamp}}',",
" '{{uuid}}',",
" 'ffffffff-ffff-ffff-ffff-ffffffffffff',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"organization\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:person_id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:person_id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"first_name\",",
" \"id\",",
" \"last_name\",",
" \"type\"",
")",
"VALUES (",
" 'Const',",
" '{{uuid:person_id}}',",
" 'Person',",
" 'person'",
")"
],
[
"INSERT INTO agreego.change (",
" \"old\",",
" \"new\",",
" entity_id,",
" id,",
" kind,",
" modified_at,",
" modified_by",
")",
"VALUES (",
" NULL,",
" '{",
" \"first_name\":\"Const\",",
" \"last_name\":\"Person\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" }',",
" '{{uuid:person_id}}',",
" '{{uuid}}',",
" 'create',",
" '{{timestamp}}',",
" '{{uuid}}'",
")"
],
[
"INSERT INTO agreego.\"entity\" (",
" \"created_at\",",
" \"created_by\",",
" \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"organization_id\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '{{uuid}}',",
" '{{uuid:order_id}}',",
" '{{timestamp}}',",
" '{{uuid}}',",
" 'parent-org-id',",
" 'order'",
")"
],
[
"INSERT INTO agreego.\"order\" (",
" \"customer_id\",",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:person_id}}',",
" '{{uuid:order_id}}',",
" 'order'",
")"
],
[
"INSERT INTO agreego.\"entity\" (",
" \"created_at\",",
" \"created_by\",",
" \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"organization_id\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '{{uuid}}',",
" '{{uuid:line1_id}}',",
" '{{timestamp}}',",
" '{{uuid}}',",
" 'parent-org-id',",
" 'order_line'",
")"
],
[
"INSERT INTO agreego.\"order_line\" (",
" \"id\",",
" \"order_id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:line1_id}}',",
" '{{uuid:order_id}}',",
" 'order_line'",
")"
],
[
"INSERT INTO agreego.change (",
" \"old\",",
" \"new\",",
" entity_id,",
" id,",
" kind,",
" modified_at,",
" modified_by",
")",
"VALUES (",
" NULL,",
" '{",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order_line\"",
" }',",
" '{{uuid:line1_id}}',",
" '{{uuid}}',",
" 'create',",
" '{{timestamp}}',",
" '{{uuid}}'",
")"
],
[
"INSERT INTO agreego.\"entity\" (",
" \"created_at\",",
" \"created_by\",",
" \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"organization_id\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '{{uuid}}',",
" '{{uuid:line2_id}}',",
" '{{timestamp}}',",
" '{{uuid}}',",
" 'explicit-org-id',",
" 'order_line'",
")"
],
[
"INSERT INTO agreego.\"order_line\" (",
" \"id\",",
" \"order_id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:line2_id}}',",
" '{{uuid:order_id}}',",
" 'order_line'",
")"
],
[
"INSERT INTO agreego.change (",
" \"old\",",
" \"new\",",
" entity_id,",
" id,",
" kind,",
" modified_at,",
" modified_by",
")",
"VALUES (",
" NULL,",
" '{",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"explicit-org-id\",",
" \"type\":\"order_line\"",
" }',",
" '{{uuid:line2_id}}',",
" '{{uuid}}',",
" 'create',",
" '{{timestamp}}',",
" '{{uuid}}'",
")"
],
[
"INSERT INTO agreego.change (",
" \"old\",",
" \"new\",",
" entity_id,",
" id,",
" kind,",
" modified_at,",
" modified_by",
")",
"VALUES (",
" NULL,",
" '{",
" \"customer_id\":\"{{uuid:person_id}}\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order\"",
" }',",
" '{{uuid:order_id}}',",
" '{{uuid}}',",
" 'create',",
" '{{timestamp}}',",
" '{{uuid}}'",
")"
],
[
"SELECT pg_notify('entity', '{",
" \"complete\":{",
" \"created_at\":\"{{timestamp}}\",",
" \"created_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"customer_id\":\"{{uuid:person_id}}\",",
" \"id\":\"{{uuid:order_id}}\",",
" \"modified_at\":\"{{timestamp}}\",",
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order\"",
" },",
" \"new\":{",
" \"customer_id\":\"{{uuid:person_id}}\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order\"",
" }",
" }')"
],
[
"SELECT pg_notify('entity', '{",
" \"complete\":{",
" \"created_at\":\"{{timestamp}}\",",
" \"created_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"first_name\":\"Const\",",
" \"id\":\"{{uuid:person_id}}\",",
" \"last_name\":\"Person\",",
" \"modified_at\":\"{{timestamp}}\",",
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" },",
" \"new\":{",
" \"first_name\":\"Const\",",
" \"last_name\":\"Person\",",
" \"organization_id\":\"ffffffff-ffff-ffff-ffff-ffffffffffff\",",
" \"type\":\"person\"",
" }",
" }')"
],
[
"SELECT pg_notify('entity', '{",
" \"complete\":{",
" \"created_at\":\"{{timestamp}}\",",
" \"created_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"id\":\"{{uuid:line1_id}}\",",
" \"modified_at\":\"{{timestamp}}\",",
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order_line\"",
" },",
" \"new\":{",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"parent-org-id\",",
" \"type\":\"order_line\"",
" }",
" }')"
],
[
"SELECT pg_notify('entity', '{",
" \"complete\":{",
" \"created_at\":\"{{timestamp}}\",",
" \"created_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"id\":\"{{uuid:line2_id}}\",",
" \"modified_at\":\"{{timestamp}}\",",
" \"modified_by\":\"00000000-0000-0000-0000-000000000000\",",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"explicit-org-id\",",
" \"type\":\"order_line\"",
" },",
" \"new\":{",
" \"order_id\":\"{{uuid:order_id}}\",",
" \"organization_id\":\"explicit-org-id\",",
" \"type\":\"order_line\"",
" }",
" }')"
]
]
}
}
]
}

View File

@ -40,7 +40,7 @@ impl Merger {
}
};
let result = self.merge_internal(target_schema, data, &mut notifications_queue);
let result = self.merge_internal(target_schema, data, &mut notifications_queue, None, false);
let val_resolved = match result {
Ok(val) => val,
@ -134,9 +134,11 @@ impl Merger {
mut schema: Arc<crate::database::schema::Schema>,
data: Value,
notifications: &mut Vec<String>,
parent_org_id: Option<String>,
is_child: bool,
) -> Result<Value, String> {
match data {
Value::Array(items) => self.merge_array(schema, items, notifications),
Value::Array(items) => self.merge_array(schema, items, notifications, parent_org_id, is_child),
Value::Object(map) => {
if let Some(options) = schema.obj.compiled_options.get() {
if let Some(disc) = schema.obj.compiled_discriminator.get() {
@ -144,9 +146,7 @@ impl Merger {
if let Some(v) = val {
if let Some((idx_opt, target_id_opt)) = options.get(v) {
if let Some(target_id) = target_id_opt {
if let Some(target_schema) =
self.db.schemas.get(target_id)
{
if let Some(target_schema) = self.db.schemas.get(target_id) {
schema = target_schema.clone();
} else {
return Err(format!(
@ -185,7 +185,7 @@ impl Merger {
}
}
}
self.merge_object(schema, map, notifications)
self.merge_object(schema, map, notifications, parent_org_id, is_child)
}
_ => Err("Invalid merge payload: root must be an Object or Array".to_string()),
}
@ -196,6 +196,8 @@ impl Merger {
schema: Arc<crate::database::schema::Schema>,
items: Vec<Value>,
notifications: &mut Vec<String>,
parent_org_id: Option<String>,
is_child: bool,
) -> Result<Value, String> {
let mut item_schema = schema.clone();
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &schema.obj.type_ {
@ -208,7 +210,7 @@ impl Merger {
let mut resolved_items = Vec::new();
for item in items {
let resolved = self.merge_internal(item_schema.clone(), item, notifications)?;
let resolved = self.merge_internal(item_schema.clone(), item, notifications, parent_org_id.clone(), is_child)?;
resolved_items.push(resolved);
}
Ok(Value::Array(resolved_items))
@ -219,6 +221,8 @@ impl Merger {
schema: Arc<crate::database::schema::Schema>,
obj: serde_json::Map<String, Value>,
notifications: &mut Vec<String>,
parent_org_id: Option<String>,
is_child: bool,
) -> Result<Value, String> {
let queue_start = notifications.len();
@ -278,6 +282,20 @@ impl Merger {
}
}
let mut current_org_id = None;
if let Some(compiled_props) = schema.obj.compiled_properties.get() {
if let Some(org_schema) = compiled_props.get("organization_id") {
if let Some(c) = &org_schema.obj.const_ {
if let Some(c_str) = c.as_str() {
current_org_id = Some(c_str.to_string());
}
}
}
}
if current_org_id.is_none() {
current_org_id = parent_org_id.clone();
}
let user_id = self.db.auth_user_id()?;
let timestamp = self.db.timestamp()?;
@ -292,6 +310,16 @@ impl Merger {
entity_change_kind = kind;
entity_fetched = fetched;
entity_replaces = replaces;
if entity_change_kind.as_deref() == Some("create") {
if is_child {
if !entity_fields.contains_key("organization_id") {
if let Some(ref org_id) = current_org_id {
entity_fields.insert("organization_id".to_string(), Value::String(org_id.clone()));
}
}
}
}
}
let mut entity_response = serde_json::Map::new();
@ -312,17 +340,14 @@ impl Merger {
if let Some(relation) = self.db.relations.get(&edge.constraint) {
let parent_is_source = edge.forward;
let org_id_to_pass = entity_fields.get("organization_id").and_then(|v| v.as_str()).map(|s| s.to_string());
if parent_is_source {
if !relative.contains_key("organization_id") {
if let Some(org_id) = entity_fields.get("organization_id") {
relative.insert("organization_id".to_string(), org_id.clone());
}
}
let mut merged_relative = match self.merge_internal(
rel_schema.clone(),
Value::Object(relative),
notifications,
org_id_to_pass.clone(),
true,
)? {
Value::Object(m) => m,
_ => continue,
@ -338,12 +363,6 @@ impl Merger {
);
entity_response.insert(relation_name, Value::Object(merged_relative));
} else {
if !relative.contains_key("organization_id") {
if let Some(org_id) = entity_fields.get("organization_id") {
relative.insert("organization_id".to_string(), org_id.clone());
}
}
Self::apply_entity_relation(
&mut relative,
&relation.source_columns,
@ -355,6 +374,8 @@ impl Merger {
rel_schema.clone(),
Value::Object(relative),
notifications,
org_id_to_pass.clone(),
true,
)? {
Value::Object(m) => m,
_ => continue,
@ -374,6 +395,16 @@ impl Merger {
entity_change_kind = kind;
entity_fetched = fetched;
entity_replaces = replaces;
if entity_change_kind.as_deref() == Some("create") {
if is_child {
if !entity_fields.contains_key("organization_id") {
if let Some(ref org_id) = current_org_id {
entity_fields.insert("organization_id".to_string(), Value::String(org_id.clone()));
}
}
}
}
}
self.merge_entity_fields(
@ -401,15 +432,21 @@ impl Merger {
if let Some(compiled_edges) = schema.obj.compiled_edges.get() {
if let Some(edge) = compiled_edges.get(&relation_name) {
if let Some(relation) = self.db.relations.get(&edge.constraint) {
let mut item_schema = rel_schema.clone();
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
&rel_schema.obj.type_
{
if t == "array" {
if let Some(items_def) = &rel_schema.obj.items {
item_schema = items_def.clone();
}
}
}
let org_id_to_pass = entity_fields.get("organization_id").and_then(|v| v.as_str()).map(|s| s.to_string());
let mut relative_responses = Vec::new();
for relative_item_val in relative_arr {
if let Value::Object(mut relative_item) = relative_item_val {
if !relative_item.contains_key("organization_id") {
if let Some(org_id) = entity_fields.get("organization_id") {
relative_item.insert("organization_id".to_string(), org_id.clone());
}
}
Self::apply_entity_relation(
&mut relative_item,
&relation.source_columns,
@ -417,21 +454,12 @@ impl Merger {
&entity_fields,
);
let mut item_schema = rel_schema.clone();
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
&rel_schema.obj.type_
{
if t == "array" {
if let Some(items_def) = &rel_schema.obj.items {
item_schema = items_def.clone();
}
}
}
let merged_relative = match self.merge_internal(
item_schema,
item_schema.clone(),
Value::Object(relative_item),
notifications,
org_id_to_pass.clone(),
true,
)? {
Value::Object(m) => m,
_ => continue,

View File

@ -8188,3 +8188,9 @@ 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();
}
#[test]
fn test_merger_0_15() {
let path = format!("{}/fixtures/merger.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 0, 15).unwrap();
}

View File

@ -1 +1 @@
1.0.135
1.0.139