Compare commits

...

8 Commits

Author SHA1 Message Date
9e362f2168 version: 1.0.159 2026-06-15 16:44:04 -04:00
9fffc7707f Merge branch 'main' of gitea-ssh.thoughtpatterns.ai:cellular/jspg 2026-06-15 16:42:13 -04:00
c5d652c6fd fixed merger ordering issue 2026-06-15 16:41:59 -04:00
d2cdd680ed insert fix checkpoint 2026-06-15 16:18:44 -04:00
6f1c0d7ee9 version: 1.0.158 2026-06-12 22:11:14 -04:00
25bbf2b564 merger: generate entity and changelog ids with uuid v7
Switch the two UUID generation sites in the merger from v4 to time-ordered
v7, and swap the uuid crate feature flag from v4 to v7 accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:11:09 -04:00
c8cc4cbde8 version: 1.0.157 2026-06-11 20:40:36 -04:00
5af2399e3b fixed issue with filter generation where filters or conditions are used internally 2026-06-11 20:40:27 -04:00
11 changed files with 1058 additions and 805 deletions

View File

@ -16,7 +16,7 @@ url = "2.5.8"
fluent-uri = "0.3.2" fluent-uri = "0.3.2"
idna = "1.1.0" idna = "1.1.0"
percent-encoding = "2.3.2" percent-encoding = "2.3.2"
uuid = { version = "1.20.0", features = ["v4", "serde"] } uuid = { version = "1.20.0", features = ["v7", "serde"] }
chrono = { version = "0.4.43", features = ["serde"] } chrono = { version = "0.4.43", features = ["serde"] }
json-pointer = "0.3.4" json-pointer = "0.3.4"
indexmap = { version = "2.13.0", features = ["serde"] } indexmap = { version = "2.13.0", features = ["serde"] }

View File

@ -175,6 +175,7 @@ In the Punc architecture, filters are automatically synthesized, strongly-typed
* **Conditions**: A condition schema is the contract defining the mathematical operations allowed on a primitive field. For example, a `string.condition` allows `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$of` (IN), and `$nof` (NOT IN). * **Conditions**: A condition schema is the contract defining the mathematical operations allowed on a primitive field. For example, a `string.condition` allows `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$of` (IN), and `$nof` (NOT IN).
* **Enum Conditions**: When JSPG synthesizes an enum, it dynamically generates an `<enum>.condition` (e.g., `address_kind.condition`). This strongly-typed condition perfectly mirrors the operations of a `string.condition`, but strictly limits the arrays and inputs of `$eq`, `$ne`, `$of`, and `$nof` to the exact variations defined by that Enum. This context ensures that UI generators know exactly when to render `<Select>` dropdowns instead of generic `<Text>` boxes. * **Enum Conditions**: When JSPG synthesizes an enum, it dynamically generates an `<enum>.condition` (e.g., `address_kind.condition`). This strongly-typed condition perfectly mirrors the operations of a `string.condition`, but strictly limits the arrays and inputs of `$eq`, `$ne`, `$of`, and `$nof` to the exact variations defined by that Enum. This context ensures that UI generators know exactly when to render `<Select>` dropdowns instead of generic `<Text>` boxes.
* **Pre-compiled Condition and Filter Mapping**: To prevent redundant double-wrapping of search structures, any schema property whose type is already a `.condition` or `.filter` type (such as `"string.condition"` or `"$kind.filter"`) maps directly to itself during filter synthesis rather than receiving a redundant `.filter` suffix.
* **Filters**: A filter schema (e.g., `person.filter`) is an object containing condition properties used to filter entities. It natively supports structural composition: * **Filters**: A filter schema (e.g., `person.filter`) is an object containing condition properties used to filter entities. It natively supports structural composition:
* **Inherited Properties**: Filters automatically inherit all valid database columns from their base type schema, immediately converting them to their respective `.condition` schemas. * **Inherited Properties**: Filters automatically inherit all valid database columns from their base type schema, immediately converting them to their respective `.condition` schemas.
* **Relational Proxies**: If a table has a foreign key to another table, the filter automatically generates a proxy property pointing to the related entity's filter (e.g., the `person` filter automatically gains an `organization` property that points to `organization.filter`), allowing infinitely deep nested queries natively. * **Relational Proxies**: If a table has a foreign key to another table, the filter automatically generates a proxy property pointing to the related entity's filter (e.g., the `person` filter automatically gains an `organization` property that points to `organization.filter`), allowing infinitely deep nested queries natively.

View File

@ -791,8 +791,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"invoice", "entity",
"entity" "invoice"
], ],
"fields": [ "fields": [
"id", "id",
@ -867,8 +867,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"invoice_line", "entity",
"entity" "invoice_line"
], ],
"fields": [ "fields": [
"id", "id",

View File

@ -466,7 +466,7 @@
}, },
"filter": { "filter": {
"type": [ "type": [
"$kind.filter.filter", "$kind.filter",
"null" "null"
] ]
}, },

View File

@ -110,8 +110,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"activity", "entity",
"entity" "activity"
], ],
"variations": [ "variations": [
"activity", "activity",
@ -166,9 +166,9 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"invoice", "entity",
"activity", "activity",
"entity" "invoice"
], ],
"variations": [ "variations": [
"invoice" "invoice"
@ -237,8 +237,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"attachment", "entity",
"entity" "attachment"
], ],
"variations": [ "variations": [
"attachment" "attachment"

View File

@ -219,8 +219,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"organization", "entity",
"entity" "organization"
], ],
"fields": [ "fields": [
"id", "id",
@ -262,9 +262,9 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"user", "entity",
"organization", "organization",
"entity" "user"
], ],
"fields": [ "fields": [
"id", "id",
@ -359,10 +359,10 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"person", "entity",
"user",
"organization", "organization",
"entity" "user",
"person"
], ],
"fields": [ "fields": [
"id", "id",
@ -445,8 +445,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"order", "entity",
"entity" "order"
], ],
"fields": [ "fields": [
"id", "id",
@ -504,8 +504,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"order_line", "entity",
"entity" "order_line"
], ],
"fields": [ "fields": [
"id", "id",
@ -548,8 +548,8 @@
"name": "relationship", "name": "relationship",
"relationship": true, "relationship": true,
"hierarchy": [ "hierarchy": [
"relationship", "entity",
"entity" "relationship"
], ],
"fields": [ "fields": [
"source_id", "source_id",
@ -611,9 +611,9 @@
"name": "contact", "name": "contact",
"relationship": true, "relationship": true,
"hierarchy": [ "hierarchy": [
"contact", "entity",
"relationship", "relationship",
"entity" "contact"
], ],
"fields": [ "fields": [
"is_primary", "is_primary",
@ -683,8 +683,8 @@
{ {
"name": "phone_number", "name": "phone_number",
"hierarchy": [ "hierarchy": [
"phone_number", "entity",
"entity" "phone_number"
], ],
"fields": [ "fields": [
"number", "number",
@ -741,8 +741,8 @@
{ {
"name": "email_address", "name": "email_address",
"hierarchy": [ "hierarchy": [
"email_address", "entity",
"entity" "email_address"
], ],
"fields": [ "fields": [
"address", "address",
@ -834,8 +834,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"attachment", "entity",
"entity" "attachment"
], ],
"fields": [ "fields": [
"id", "id",
@ -887,8 +887,8 @@
{ {
"name": "account", "name": "account",
"hierarchy": [ "hierarchy": [
"account", "entity",
"entity" "account"
], ],
"fields": [ "fields": [
"id", "id",
@ -1031,8 +1031,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"invoice", "entity",
"entity" "invoice"
], ],
"fields": [ "fields": [
"id", "id",
@ -1107,8 +1107,8 @@
} }
}, },
"hierarchy": [ "hierarchy": [
"invoice_line", "entity",
"entity" "invoice_line"
], ],
"fields": [ "fields": [
"id", "id",
@ -1309,10 +1309,10 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" (\"first_name\" = 'LookupFirst'", " (\"first_name\" = 'LookupFirst'",
" AND \"last_name\" = 'LookupLast'", " AND \"last_name\" = 'LookupLast'",
@ -1320,17 +1320,62 @@
" AND \"pronouns\" = 'they/them'))" " AND \"pronouns\" = 'they/them'))"
], ],
[ [
"UPDATE agreego.\"person\" SET", "INSERT INTO agreego.\"entity\" (",
" contact_id = 'abc-contact'", " \"created_at\",",
"WHERE", " \"created_by\",",
" id = '{{uuid:mocks.0.id}}'" " \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" '{{uuid:generated_0}}',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'person'",
")"
], ],
[ [
"UPDATE agreego.\"entity\" SET", "INSERT INTO agreego.\"organization\" (",
" modified_at = '{{timestamp}}',", " \"id\",",
" modified_by = '00000000-0000-0000-0000-000000000000'", " \"type\"",
"WHERE", ")",
" id = '{{uuid:mocks.0.id}}'" "VALUES (",
" '{{uuid:generated_0}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:generated_0}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"contact_id\",",
" \"date_of_birth\",",
" \"first_name\",",
" \"id\",",
" \"last_name\",",
" \"pronouns\",",
" \"type\"",
")",
"VALUES (",
" 'abc-contact',",
" '{{timestamp}}',",
" 'LookupFirst',",
" '{{uuid:generated_0}}',",
" 'LookupLast',",
" 'they/them',",
" 'person'",
")"
], ],
[ [
"INSERT INTO agreego.change (", "INSERT INTO agreego.change (",
@ -1343,40 +1388,45 @@
" \"modified_by\"", " \"modified_by\"",
")", ")",
"VALUES (", "VALUES (",
" NULL,",
" '{", " '{",
" \"contact_id\": \"old-contact\"", " \"first_name\": \"LookupFirst\",",
" }',", " \"last_name\": \"LookupLast\",",
" '{", " \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" }',", " }',",
" '{{uuid:mocks.0.id}}',",
" '{{uuid:generated_0}}',", " '{{uuid:generated_0}}',",
" 'update',", " '{{uuid:generated_1}}',",
" 'create',",
" '{{timestamp}}',", " '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000'", " '00000000-0000-0000-0000-000000000000'",
")" ")"
], ],
[ [
"(SELECT pg_notify('entity', '{", "(SELECT pg_notify('entity', '{",
" \"kind\": \"update\",", " \"kind\": \"create\",",
" \"complete\": {", " \"complete\": {",
" \"id\": \"{{uuid:mocks.0.id}}\",",
" \"type\": \"person\",",
" \"first_name\": \"LookupFirst\",", " \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",", " \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",", " \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",", " \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"id\": \"{{uuid:generated_0}}\",",
" \"type\": \"person\",",
" \"created_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"created_at\": \"{{timestamp}}\",",
" \"modified_by\": \"00000000-0000-0000-0000-000000000000\",", " \"modified_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"modified_at\": \"{{timestamp}}\"", " \"modified_at\": \"{{timestamp}}\"",
" },", " },",
" \"new\": {", " \"new\": {",
" \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" },",
" \"old\": {",
" \"contact_id\": \"old-contact\"",
" }", " }",
"}'))" "}'))"
] ]
@ -1412,10 +1462,10 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:data.id}}'", " t1.id = '{{uuid:data.id}}'",
" OR (\"first_name\" = 'LookupFirst'", " OR (\"first_name\" = 'LookupFirst'",
@ -1424,17 +1474,62 @@
" AND \"pronouns\" = 'they/them'))" " AND \"pronouns\" = 'they/them'))"
], ],
[ [
"UPDATE agreego.\"person\" SET", "INSERT INTO agreego.\"entity\" (",
" contact_id = 'abc-contact'", " \"created_at\",",
"WHERE", " \"created_by\",",
" id = '{{uuid:mocks.0.id}}'" " \"id\",",
" \"modified_at\",",
" \"modified_by\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" '{{uuid:data.id}}',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'person'",
")"
], ],
[ [
"UPDATE agreego.\"entity\" SET", "INSERT INTO agreego.\"organization\" (",
" modified_at = '{{timestamp}}',", " \"id\",",
" modified_by = '00000000-0000-0000-0000-000000000000'", " \"type\"",
"WHERE", ")",
" id = '{{uuid:mocks.0.id}}'" "VALUES (",
" '{{uuid:data.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:data.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"contact_id\",",
" \"date_of_birth\",",
" \"first_name\",",
" \"id\",",
" \"last_name\",",
" \"pronouns\",",
" \"type\"",
")",
"VALUES (",
" 'abc-contact',",
" '{{timestamp}}',",
" 'LookupFirst',",
" '{{uuid:data.id}}',",
" 'LookupLast',",
" 'they/them',",
" 'person'",
")"
], ],
[ [
"INSERT INTO agreego.change (", "INSERT INTO agreego.change (",
@ -1447,42 +1542,46 @@
" \"modified_by\"", " \"modified_by\"",
")", ")",
"VALUES (", "VALUES (",
" NULL,",
" '{", " '{",
" \"contact_id\": \"old-contact\"", " \"first_name\": \"LookupFirst\",",
" }',", " \"last_name\": \"LookupLast\",",
" '{", " \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" }',", " }',",
" '{{uuid:mocks.0.id}}',", " '{{uuid:data.id}}',",
" '{{uuid:generated_0}}',", " '{{uuid:generated_0}}',",
" 'update',", " 'create',",
" '{{timestamp}}',", " '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000'", " '00000000-0000-0000-0000-000000000000'",
")" ")"
], ],
[ [
"(SELECT pg_notify('entity', '{", "(SELECT pg_notify('entity', '{",
" \"kind\": \"update\",", " \"kind\": \"create\",",
" \"complete\": {", " \"complete\": {",
" \"id\": \"{{uuid:mocks.0.id}}\",",
" \"type\": \"person\",",
" \"first_name\": \"LookupFirst\",", " \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",", " \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",", " \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",", " \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"id\": \"{{uuid:data.id}}\",",
" \"type\": \"person\",",
" \"created_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"created_at\": \"{{timestamp}}\",",
" \"modified_by\": \"00000000-0000-0000-0000-000000000000\",", " \"modified_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"modified_at\": \"{{timestamp}}\"", " \"modified_at\": \"{{timestamp}}\"",
" },", " },",
" \"new\": {", " \"new\": {",
" \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"contact_id\": \"abc-contact\",", " \"contact_id\": \"abc-contact\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" },", " }",
" \"old\": {",
" \"contact_id\": \"old-contact\"",
" },",
" \"replaces\": \"{{uuid:data.id}}\"",
"}'))" "}'))"
] ]
] ]
@ -1516,10 +1615,10 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:data.id}}'", " t1.id = '{{uuid:data.id}}'",
" OR (\"first_name\" = 'LookupFirst'", " OR (\"first_name\" = 'LookupFirst'",
@ -1528,23 +1627,109 @@
" AND \"pronouns\" = 'they/them'))" " AND \"pronouns\" = 'they/them'))"
], ],
[ [
"(SELECT pg_notify('entity', '{", "INSERT INTO agreego.\"entity\" (",
" \"kind\": \"replace\",", " \"created_at\",",
" \"complete\": {", " \"created_by\",",
" \"id\": \"{{uuid:mocks.0.id}}\",", " \"id\",",
" \"type\": \"person\",", " \"modified_at\",",
" \"modified_by\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" '{{uuid:data.id}}',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"organization\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:data.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:data.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"date_of_birth\",",
" \"first_name\",",
" \"id\",",
" \"last_name\",",
" \"pronouns\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" 'LookupFirst',",
" '{{uuid:data.id}}',",
" 'LookupLast',",
" 'they/them',",
" 'person'",
")"
],
[
"INSERT INTO agreego.change (",
" \"old\",",
" \"new\",",
" \"entity_id\",",
" \"id\",",
" \"kind\",",
" \"modified_at\",",
" \"modified_by\"",
")",
"VALUES (",
" NULL,",
" '{",
" \"first_name\": \"LookupFirst\",", " \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",", " \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",", " \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",", " \"pronouns\": \"they/them\",",
" \"contact_id\": \"old-contact\",", " \"type\": \"person\"",
" }',",
" '{{uuid:data.id}}',",
" '{{uuid:generated_0}}',",
" 'create',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000'",
")"
],
[
"(SELECT pg_notify('entity', '{",
" \"kind\": \"create\",",
" \"complete\": {",
" \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"id\": \"{{uuid:data.id}}\",",
" \"type\": \"person\",",
" \"created_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"created_at\": \"{{timestamp}}\",",
" \"modified_by\": \"00000000-0000-0000-0000-000000000000\",", " \"modified_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"modified_at\": \"{{timestamp}}\"", " \"modified_at\": \"{{timestamp}}\"",
" },", " },",
" \"new\": {", " \"new\": {",
" \"first_name\": \"LookupFirst\",",
" \"last_name\": \"LookupLast\",",
" \"date_of_birth\": \"{{timestamp}}\",",
" \"pronouns\": \"they/them\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" },", " }",
" \"replaces\": \"{{uuid:data.id}}\"",
"}'))" "}'))"
] ]
] ]
@ -1573,26 +1758,64 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:mocks.0.id}}')" " t1.id = '{{uuid:mocks.0.id}}')"
], ],
[ [
"UPDATE agreego.\"person\" SET", "INSERT INTO agreego.\"entity\" (",
" first_name = 'NewFirst',", " \"created_at\",",
" last_name = 'NewLast'", " \"created_by\",",
"WHERE", " \"id\",",
" id = '{{uuid:mocks.0.id}}'" " \"modified_at\",",
" \"modified_by\",",
" \"type\"",
")",
"VALUES (",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" '{{uuid:mocks.0.id}}',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'person'",
")"
], ],
[ [
"UPDATE agreego.\"entity\" SET", "INSERT INTO agreego.\"organization\" (",
" modified_at = '{{timestamp}}',", " \"id\",",
" modified_by = '00000000-0000-0000-0000-000000000000'", " \"type\"",
"WHERE", ")",
" id = '{{uuid:mocks.0.id}}'" "VALUES (",
" '{{uuid:mocks.0.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" '{{uuid:mocks.0.id}}',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"first_name\",",
" \"id\",",
" \"last_name\",",
" \"type\"",
")",
"VALUES (",
" 'NewFirst',",
" '{{uuid:mocks.0.id}}',",
" 'NewLast',",
" 'person'",
")"
], ],
[ [
"INSERT INTO agreego.change (", "INSERT INTO agreego.change (",
@ -1605,10 +1828,7 @@
" \"modified_by\"", " \"modified_by\"",
")", ")",
"VALUES (", "VALUES (",
" '{", " NULL,",
" \"first_name\": \"OldFirst\",",
" \"last_name\": \"OldLast\"",
" }',",
" '{", " '{",
" \"first_name\": \"NewFirst\",", " \"first_name\": \"NewFirst\",",
" \"last_name\": \"NewLast\",", " \"last_name\": \"NewLast\",",
@ -1616,19 +1836,21 @@
" }',", " }',",
" '{{uuid:mocks.0.id}}',", " '{{uuid:mocks.0.id}}',",
" '{{uuid:generated_0}}',", " '{{uuid:generated_0}}',",
" 'update',", " 'create',",
" '{{timestamp}}',", " '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000'", " '00000000-0000-0000-0000-000000000000'",
")" ")"
], ],
[ [
"(SELECT pg_notify('entity', '{", "(SELECT pg_notify('entity', '{",
" \"kind\": \"update\",", " \"kind\": \"create\",",
" \"complete\": {", " \"complete\": {",
" \"id\": \"{{uuid:mocks.0.id}}\",",
" \"type\": \"person\",",
" \"first_name\": \"NewFirst\",", " \"first_name\": \"NewFirst\",",
" \"last_name\": \"NewLast\",", " \"last_name\": \"NewLast\",",
" \"id\": \"{{uuid:mocks.0.id}}\",",
" \"type\": \"person\",",
" \"created_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"created_at\": \"{{timestamp}}\",",
" \"modified_by\": \"00000000-0000-0000-0000-000000000000\",", " \"modified_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"modified_at\": \"{{timestamp}}\"", " \"modified_at\": \"{{timestamp}}\"",
" },", " },",
@ -1636,10 +1858,6 @@
" \"first_name\": \"NewFirst\",", " \"first_name\": \"NewFirst\",",
" \"last_name\": \"NewLast\",", " \"last_name\": \"NewLast\",",
" \"type\": \"person\"", " \"type\": \"person\"",
" },",
" \"old\": {",
" \"first_name\": \"OldFirst\",",
" \"last_name\": \"OldLast\"",
" }", " }",
"}'))" "}'))"
] ]
@ -1663,10 +1881,10 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" t1.id = '123')" " t1.id = '123')"
], ],
@ -2002,8 +2220,8 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"order\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"entity\" t2 ON ", "JOIN agreego.\"order\" t2 ON ",
"WHERE", "WHERE",
" t1.id = 'abc'", " t1.id = 'abc'",
" OR (\"id\" = 'abc'))" " OR (\"id\" = 'abc'))"
@ -2805,20 +3023,62 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"person\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"user\" t2 ON ", "JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"organization\" t3 ON ", "JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"entity\" t4 ON ", "JOIN agreego.\"person\" t4 ON ",
"WHERE", "WHERE",
" t1.id = 'abc-archived')" " t1.id = 'abc-archived')"
], ],
[ [
"UPDATE agreego.\"entity\" SET", "INSERT INTO agreego.\"entity\" (",
" archived = true,", " \"archived\",",
" modified_at = '{{timestamp}}',", " \"created_at\",",
" modified_by = '00000000-0000-0000-0000-000000000000'", " \"created_by\",",
"WHERE", " \"id\",",
" id = 'abc-archived'" " \"modified_at\",",
" \"modified_by\",",
" \"type\"",
")",
"VALUES (",
" true,",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'abc-archived',",
" '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"organization\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" 'abc-archived',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"user\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" 'abc-archived',",
" 'person'",
")"
],
[
"INSERT INTO agreego.\"person\" (",
" \"id\",",
" \"type\"",
")",
"VALUES (",
" 'abc-archived',",
" 'person'",
")"
], ],
[ [
"INSERT INTO agreego.change (", "INSERT INTO agreego.change (",
@ -2831,38 +3091,33 @@
" \"modified_by\"", " \"modified_by\"",
")", ")",
"VALUES (", "VALUES (",
" '{", " NULL,",
" \"archived\": false",
" }',",
" '{", " '{",
" \"archived\": true,", " \"archived\": true,",
" \"type\": \"person\"", " \"type\": \"person\"",
" }',", " }',",
" 'abc-archived',", " 'abc-archived',",
" '{{uuid:generated_0}}',", " '{{uuid:generated_0}}',",
" 'delete',", " 'create',",
" '{{timestamp}}',", " '{{timestamp}}',",
" '00000000-0000-0000-0000-000000000000'", " '00000000-0000-0000-0000-000000000000'",
")" ")"
], ],
[ [
"(SELECT pg_notify('entity', '{", "(SELECT pg_notify('entity', '{",
" \"kind\": \"delete\",", " \"kind\": \"create\",",
" \"complete\": {", " \"complete\": {",
" \"archived\": true,",
" \"id\": \"abc-archived\",", " \"id\": \"abc-archived\",",
" \"type\": \"person\",", " \"type\": \"person\",",
" \"first_name\": \"ArchivedFirst\",", " \"created_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"last_name\": \"ArchivedLast\",", " \"created_at\": \"{{timestamp}}\",",
" \"archived\": true,",
" \"modified_by\": \"00000000-0000-0000-0000-000000000000\",", " \"modified_by\": \"00000000-0000-0000-0000-000000000000\",",
" \"modified_at\": \"{{timestamp}}\"", " \"modified_at\": \"{{timestamp}}\"",
" },", " },",
" \"new\": {", " \"new\": {",
" \"archived\": true,", " \"archived\": true,",
" \"type\": \"person\"", " \"type\": \"person\"",
" },",
" \"old\": {",
" \"archived\": false",
" }", " }",
"}'))" "}'))"
] ]
@ -3121,8 +3376,8 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"order_line\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"entity\" t2 ON ", "JOIN agreego.\"order_line\" t2 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:data.lines.0.id}}')" " t1.id = '{{uuid:data.lines.0.id}}')"
], ],
@ -3245,8 +3500,8 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"invoice\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"entity\" t2 ON ", "JOIN agreego.\"invoice\" t2 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:data.id}}'", " t1.id = '{{uuid:data.id}}'",
" OR (\"id\" = '{{uuid:data.id}}'))" " OR (\"id\" = '{{uuid:data.id}}'))"
@ -3362,8 +3617,8 @@
"sql": [ "sql": [
[ [
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)", "(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"account\" t1", "FROM agreego.\"entity\" t1",
"JOIN agreego.\"entity\" t2 ON ", "JOIN agreego.\"account\" t2 ON ",
"WHERE", "WHERE",
" t1.id = '{{uuid:data.id}}')" " t1.id = '{{uuid:data.id}}')"
], ],

File diff suppressed because it is too large Load Diff

View File

@ -159,7 +159,9 @@ impl Schema {
}, },
"null" => None, "null" => None,
custom => { custom => {
if db.enums.contains_key(custom) { if custom.ends_with(".condition") || custom.ends_with(".filter") {
Some(vec![custom.to_string()])
} else if db.enums.contains_key(custom) {
Some(vec![format!("{}.condition", custom)]) Some(vec![format!("{}.condition", custom)])
} else { } else {
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built // Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built

View File

@ -588,7 +588,7 @@ impl Merger {
.and_then(|v| v.as_str()) .and_then(|v| v.as_str())
.unwrap_or(""); .unwrap_or("");
let id_val = if entity_id.is_empty() { let id_val = if entity_id.is_empty() {
Value::String(uuid::Uuid::new_v4().to_string()) Value::String(uuid::Uuid::now_v7().to_string())
} else { } else {
Value::String(entity_id.to_string()) Value::String(entity_id.to_string())
}; };
@ -791,13 +791,8 @@ impl Merger {
} }
}; };
let mut execute_order: Vec<String> = entity_type.hierarchy.clone(); for table_name in &entity_type.hierarchy {
if change_kind == "create" { let table_fields = match grouped_fields.get(table_name).and_then(|v| v.as_array()) {
execute_order.reverse();
}
for table_name in execute_order {
let table_fields = match grouped_fields.get(&table_name).and_then(|v| v.as_array()) {
Some(arr) => arr Some(arr) => arr
.iter() .iter()
.filter_map(|v| v.as_str().map(|s| s.to_string())) .filter_map(|v| v.as_str().map(|s| s.to_string()))
@ -980,7 +975,7 @@ impl Merger {
Self::quote_literal(&old_val_obj), Self::quote_literal(&old_val_obj),
Self::quote_literal(&new_val_obj), Self::quote_literal(&new_val_obj),
Self::quote_literal(id_str), Self::quote_literal(id_str),
Self::quote_literal(&Value::String(uuid::Uuid::new_v4().to_string())), Self::quote_literal(&Value::String(uuid::Uuid::now_v7().to_string())),
Self::quote_literal(&Value::String(change_kind.to_string())), Self::quote_literal(&Value::String(change_kind.to_string())),
Self::quote_literal(&Value::String(timestamp.to_string())), Self::quote_literal(&Value::String(timestamp.to_string())),
Self::quote_literal(&Value::String(user_id.to_string())) Self::quote_literal(&Value::String(user_id.to_string()))

View File

@ -44,7 +44,7 @@ fn test_library_api() {
{ {
"name": "source_schema", "name": "source_schema",
"variations": ["source_schema"], "variations": ["source_schema"],
"hierarchy": ["source_schema", "entity"], "hierarchy": ["entity", "source_schema"],
"schemas": { "schemas": {
"source_schema": { "source_schema": {
"type": "object", "type": "object",
@ -60,7 +60,7 @@ fn test_library_api() {
{ {
"name": "target_schema", "name": "target_schema",
"variations": ["target_schema"], "variations": ["target_schema"],
"hierarchy": ["target_schema", "entity"], "hierarchy": ["entity", "target_schema"],
"schemas": { "schemas": {
"target_schema": { "target_schema": {
"type": "object", "type": "object",
@ -109,7 +109,7 @@ fn test_library_api() {
"field_types": null, "field_types": null,
"fields": [], "fields": [],
"grouped_fields": null, "grouped_fields": null,
"hierarchy": ["source_schema", "entity"], "hierarchy": ["entity", "source_schema"],
"historical": false, "historical": false,
"id": "", "id": "",
"longevity": null, "longevity": null,
@ -174,7 +174,7 @@ fn test_library_api() {
"field_types": null, "field_types": null,
"fields": [], "fields": [],
"grouped_fields": null, "grouped_fields": null,
"hierarchy": ["target_schema", "entity"], "hierarchy": ["entity", "target_schema"],
"historical": false, "historical": false,
"id": "", "id": "",
"longevity": null, "longevity": null,

View File

@ -1 +1 @@
1.0.156 1.0.159