From e83bc8316ef7a9fcb3d5852bf3c5df1794098b75 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Sun, 21 Jul 2024 23:43:53 +0200 Subject: [PATCH] Move away from models and manage subpackage This architecture was started when the project still used Diesel. Now that it uses SQLx, less things are done in Rust and more are done in SQL. This commit now moves more of the query logic into SQL, which should lead to more efficient queries and less moving data around. --- ...7c40ce73946e62744a486cbbac256c4a4cf55.json | 34 ++++ ...f28fd05f18706f70220b6ebb7354d2a0a9e3b.json | 64 -------- ...c75aea0c7d22e009299d1b87443e350578171.json | 56 +++++++ ...6bf44ecdb0b6145f8abba155478643a474125.json | 28 ++++ ...8f65f11c170b67424e35697540ae12e577227.json | 34 ++++ ...aff9ed698f585df5e798d1f42206042e31a1b.json | 28 ++++ ...eb93e01fd0983c19aff3fca67bdc0afab4f37.json | 26 +++ ...aa92283151d67f52fbb22e5d976b1c6a5c367.json | 62 ------- ...edbbdfe2cbf68a6346f93c072d47f5add9e46.json | 19 +++ ...c34edd6be47cd1706b3612a332bbfd4bb54b4.json | 48 ------ ...338a7275ba56a9b17278fd886175c3a27b0dd.json | 22 --- ...f8f3c3841a4424da04aee0136c7e4f8df79e7.json | 22 +++ ...598749d931af69a80d24f4575a4bc2c740f3b.json | 64 -------- ...574381493e559860258f4f6bffb12825b1ed7.json | 32 ++++ ...86f1f9fdb076e8f8b98f10fc1b981cfe8038c.json | 64 -------- ...1981cfee40fd04cb4e3552cc5f4ebf4ed0572.json | 69 -------- ...9b518f5cb6936e3e4ea23c200b13787a401e1.json | 17 ++ ...6ac6ebe71d81057cd865c5f946d6ddb62b552.json | 24 +++ ...5938b3959ae41744098c4e9565404abf09ae.json} | 18 +- ...7ee841c5b89c86af12f32493ba05c0f0ead91.json | 26 +++ ...8bf3208b55b51433ec92b4e7a452929a81945.json | 64 -------- ...fbc5433e972b5522b367098afe6cb90a30bf2.json | 64 -------- ...1013767fe36575c28574733c5ab5cbf557fb5.json | 22 --- ...8ce3b65f49c34ad848321dc1b151c21cb0043.json | 70 ++++++++ ...ccf5d87b053857c161651360e5a8fdd80e397.json | 34 ++++ ...cfa2f2a497180e2517a937471f81a1f9c5538.json | 26 --- ...3cd119362d0baf5effbbd09f969ee31f385ef.json | 68 -------- ...730edf887d66a4196a0834c59f0df9f9314d3.json | 68 -------- ...b6198594f94a3e0856abde0efdb4e49dbab8.json} | 32 ++-- ...96b95917b15768ebe0250ce7840391036616.json} | 12 +- ...b952397a017b33c351c5a33f1daa78ab908a.json} | 8 +- ...8f9229dfbbfd289039db7b733c7d1d050f4bf.json | 49 ------ ...42b19308d9a558703b02e758f60e7d8644d00.json | 64 -------- ...709be6fbcfc596063878c0bad9df00037dbe.json} | 20 +-- ...6b0ad52d1ae047fd004f1359be7122facf8e7.json | 26 +++ ...7fa308f4df6a0ca6442fa7f9bbda1e34ffbbe.json | 34 ++++ Cargo.lock | 10 ++ Cargo.toml | 1 + src/frontend/item/add.rs | 95 ++++++----- src/frontend/item/delete.rs | 7 +- src/frontend/item/edit.rs | 92 ++++++++--- src/frontend/item/list.rs | 114 +++++++------ src/frontend/item/show.rs | 154 ++++++++++------- src/frontend/item_class/add.rs | 23 +-- src/frontend/item_class/delete.rs | 7 +- src/frontend/item_class/edit.rs | 56 +++++-- src/frontend/item_class/list.rs | 46 ++++-- src/frontend/item_class/show.rs | 88 ++++++---- src/frontend/jump.rs | 35 +++- src/frontend/labels.rs | 32 ++-- src/frontend/templates/datalist.rs | 40 ++--- src/frontend/templates/helpers.rs | 19 +++ src/label/mod.rs | 24 +-- src/lib.rs | 2 - src/manage/item.rs | 155 ------------------ src/manage/item_class.rs | 83 ---------- src/manage/mod.rs | 29 ---- src/models.rs | 56 ------- 58 files changed, 1074 insertions(+), 1512 deletions(-) create mode 100644 .sqlx/query-02438c29c249ce2b446d15a41d07c40ce73946e62744a486cbbac256c4a4cf55.json delete mode 100644 .sqlx/query-2e9619ce6db1047e2f5447c4925f28fd05f18706f70220b6ebb7354d2a0a9e3b.json create mode 100644 .sqlx/query-3dedb7b184103c1d418f7b94e26c75aea0c7d22e009299d1b87443e350578171.json create mode 100644 .sqlx/query-460f15b3c5ec2774c237fc581166bf44ecdb0b6145f8abba155478643a474125.json create mode 100644 .sqlx/query-482df01f0509cc8ec18ffe1caea8f65f11c170b67424e35697540ae12e577227.json create mode 100644 .sqlx/query-4ed0c3101202abc920d81ee6acdaff9ed698f585df5e798d1f42206042e31a1b.json create mode 100644 .sqlx/query-53be3ef02ba8b3deb3a74b184a4eb93e01fd0983c19aff3fca67bdc0afab4f37.json delete mode 100644 .sqlx/query-58969747bdccae4a2d3ad8d8117aa92283151d67f52fbb22e5d976b1c6a5c367.json create mode 100644 .sqlx/query-6737f54ceb18b9b744bac838801edbbdfe2cbf68a6346f93c072d47f5add9e46.json delete mode 100644 .sqlx/query-68d93c71840f87d5f5bb14b4d7fc34edd6be47cd1706b3612a332bbfd4bb54b4.json delete mode 100644 .sqlx/query-6dd8b63f4aaefbc1ab5d5a9bae2338a7275ba56a9b17278fd886175c3a27b0dd.json create mode 100644 .sqlx/query-719aff68ead7ada499158fec5e9f8f3c3841a4424da04aee0136c7e4f8df79e7.json delete mode 100644 .sqlx/query-79d8bfe2ed76ee550cdc31f282f598749d931af69a80d24f4575a4bc2c740f3b.json create mode 100644 .sqlx/query-7b3f478c9b217e09043daeca5dc574381493e559860258f4f6bffb12825b1ed7.json delete mode 100644 .sqlx/query-7eeb752c8b00ac4000104f0254186f1f9fdb076e8f8b98f10fc1b981cfe8038c.json delete mode 100644 .sqlx/query-82df5c0633a655d376b8a91e9f11981cfee40fd04cb4e3552cc5f4ebf4ed0572.json create mode 100644 .sqlx/query-835261895368c22af5780c4e4b69b518f5cb6936e3e4ea23c200b13787a401e1.json create mode 100644 .sqlx/query-83fe5e57b2c7db1fdfbf05749646ac6ebe71d81057cd865c5f946d6ddb62b552.json rename .sqlx/{query-c552c0a40bc8995cb95726a85f1d0c0b86eb2322035e6a720e2e6d425072a8c1.json => query-84b4620db57dd9b963e09153c3de5938b3959ae41744098c4e9565404abf09ae.json} (56%) create mode 100644 .sqlx/query-857bbaea0fa73c37679d927aa627ee841c5b89c86af12f32493ba05c0f0ead91.json delete mode 100644 .sqlx/query-9286b6ee0c08b8446ede68b890b8bf3208b55b51433ec92b4e7a452929a81945.json delete mode 100644 .sqlx/query-94958a3a57c3178e6b0de5723b1fbc5433e972b5522b367098afe6cb90a30bf2.json delete mode 100644 .sqlx/query-97d6a7ee24e75dc5a9dc41a581e1013767fe36575c28574733c5ab5cbf557fb5.json create mode 100644 .sqlx/query-9c1a1e9c33539f6ec4800c9e8fc8ce3b65f49c34ad848321dc1b151c21cb0043.json create mode 100644 .sqlx/query-9e1704d0b60906061460e2c972fccf5d87b053857c161651360e5a8fdd80e397.json delete mode 100644 .sqlx/query-a6f646089b4424deffc0b1a454bcfa2f2a497180e2517a937471f81a1f9c5538.json delete mode 100644 .sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json delete mode 100644 .sqlx/query-b80bb07ab582b0705b5c0370066730edf887d66a4196a0834c59f0df9f9314d3.json rename .sqlx/{query-1e9045f52c002a19b815351fee1c7ee7520478ff4f5886b4523fb0dc4df0e204.json => query-c49b88eda9a62743783bc894f01bb6198594f94a3e0856abde0efdb4e49dbab8.json} (59%) rename .sqlx/{query-6e7b3389c47091d9fc8c7638b401b413f804c6f3e082a818b67ebab0938acb39.json => query-c7e35dee5f56da8da3c083e191f396b95917b15768ebe0250ce7840391036616.json} (58%) rename .sqlx/{query-5cf503740d71431d8b9d256960c0ce194ede48a1c46326315cae9f07347597f6.json => query-cfce4ef19a3e0a3a0582d3c7d266b952397a017b33c351c5a33f1daa78ab908a.json} (57%) delete mode 100644 .sqlx/query-e0129afa95f896d79f772fb177a8f9229dfbbfd289039db7b733c7d1d050f4bf.json delete mode 100644 .sqlx/query-efe2258db42b60f732d562d106842b19308d9a558703b02e758f60e7d8644d00.json rename .sqlx/{query-308962c26250f9312287a3f2f21e5da76e4cf488eedd4704019b4f14b6fbafb2.json => query-f446c6ab881c166102f3b20056e0709be6fbcfc596063878c0bad9df00037dbe.json} (58%) create mode 100644 .sqlx/query-fc5e536bddb5d76021164ae4f5a6b0ad52d1ae047fd004f1359be7122facf8e7.json create mode 100644 .sqlx/query-ff39480d9395b357d71e9af3eb37fa308f4df6a0ca6442fa7f9bbda1e34ffbbe.json delete mode 100644 src/manage/item.rs delete mode 100644 src/manage/item_class.rs delete mode 100644 src/manage/mod.rs delete mode 100644 src/models.rs diff --git a/.sqlx/query-02438c29c249ce2b446d15a41d07c40ce73946e62744a486cbbac256c4a4cf55.json b/.sqlx/query-02438c29c249ce2b446d15a41d07c40ce73946e62744a486cbbac256c4a4cf55.json new file mode 100644 index 0000000..69bc7e1 --- /dev/null +++ b/.sqlx/query-02438c29c249ce2b446d15a41d07c40ce73946e62744a486cbbac256c4a4cf55.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT items.id, items.name, item_classes.name AS \"class_name\"\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id\n WHERE items.original_packaging = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "02438c29c249ce2b446d15a41d07c40ce73946e62744a486cbbac256c4a4cf55" +} diff --git a/.sqlx/query-2e9619ce6db1047e2f5447c4925f28fd05f18706f70220b6ebb7354d2a0a9e3b.json b/.sqlx/query-2e9619ce6db1047e2f5447c4925f28fd05f18706f70220b6ebb7354d2a0a9e3b.json deleted file mode 100644 index bd6f7f3..0000000 --- a/.sqlx/query-2e9619ce6db1047e2f5447c4925f28fd05f18706f70220b6ebb7354d2a0a9e3b.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE parent = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "2e9619ce6db1047e2f5447c4925f28fd05f18706f70220b6ebb7354d2a0a9e3b" -} diff --git a/.sqlx/query-3dedb7b184103c1d418f7b94e26c75aea0c7d22e009299d1b87443e350578171.json b/.sqlx/query-3dedb7b184103c1d418f7b94e26c75aea0c7d22e009299d1b87443e350578171.json new file mode 100644 index 0000000..767fcf1 --- /dev/null +++ b/.sqlx/query-3dedb7b184103c1d418f7b94e26c75aea0c7d22e009299d1b87443e350578171.json @@ -0,0 +1,56 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH RECURSIVE cte AS (\n SELECT\n id,\n ARRAY[]::UUID[] AS parents,\n ARRAY[]::VARCHAR[] AS parent_names,\n ARRAY[]::VARCHAR[] AS parent_class_names\n FROM items\n WHERE parent IS NULL\n\n UNION\n\n SELECT\n items.id,\n cte.parents || items.parent,\n cte.parent_names || parent.name,\n cte.parent_class_names || parent_class.name\n FROM cte\n JOIN items\n ON items.parent = cte.id\n JOIN items AS \"parent\"\n ON parent.id = cte.id\n JOIN item_classes AS \"parent_class\"\n ON parent.class = parent_class.id\n )\n SELECT\n cte.id AS \"id!\",\n items.name,\n items.class,\n item_classes.name AS \"class_name\",\n cte.parents AS \"parents!\",\n cte.parent_names AS \"parent_names!: Vec>\",\n cte.parent_class_names AS \"parent_class_names!\"\n FROM cte\n JOIN items\n ON cte.id = items.id\n JOIN item_classes\n ON items.class = item_classes.id\n ORDER BY items.created_at\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id!", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "class_name", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "parents!", + "type_info": "UuidArray" + }, + { + "ordinal": 5, + "name": "parent_names!: Vec>", + "type_info": "VarcharArray" + }, + { + "ordinal": 6, + "name": "parent_class_names!", + "type_info": "VarcharArray" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + true, + false, + false, + null, + null, + null + ] + }, + "hash": "3dedb7b184103c1d418f7b94e26c75aea0c7d22e009299d1b87443e350578171" +} diff --git a/.sqlx/query-460f15b3c5ec2774c237fc581166bf44ecdb0b6145f8abba155478643a474125.json b/.sqlx/query-460f15b3c5ec2774c237fc581166bf44ecdb0b6145f8abba155478643a474125.json new file mode 100644 index 0000000..25af06d --- /dev/null +++ b/.sqlx/query-460f15b3c5ec2774c237fc581166bf44ecdb0b6145f8abba155478643a474125.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id AS \"id?\", to_char(short_id, '000000') AS \"short_id?\"\n FROM items\n WHERE id = ANY ($1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id?", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "short_id?", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + null + ] + }, + "hash": "460f15b3c5ec2774c237fc581166bf44ecdb0b6145f8abba155478643a474125" +} diff --git a/.sqlx/query-482df01f0509cc8ec18ffe1caea8f65f11c170b67424e35697540ae12e577227.json b/.sqlx/query-482df01f0509cc8ec18ffe1caea8f65f11c170b67424e35697540ae12e577227.json new file mode 100644 index 0000000..a35d951 --- /dev/null +++ b/.sqlx/query-482df01f0509cc8ec18ffe1caea8f65f11c170b67424e35697540ae12e577227.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT items.id, items.name, item_classes.name AS \"class_name\"\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id\n WHERE items.class = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "482df01f0509cc8ec18ffe1caea8f65f11c170b67424e35697540ae12e577227" +} diff --git a/.sqlx/query-4ed0c3101202abc920d81ee6acdaff9ed698f585df5e798d1f42206042e31a1b.json b/.sqlx/query-4ed0c3101202abc920d81ee6acdaff9ed698f585df5e798d1f42206042e31a1b.json new file mode 100644 index 0000000..9ee3865 --- /dev/null +++ b/.sqlx/query-4ed0c3101202abc920d81ee6acdaff9ed698f585df5e798d1f42206042e31a1b.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, name FROM item_classes WHERE parent = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "4ed0c3101202abc920d81ee6acdaff9ed698f585df5e798d1f42206042e31a1b" +} diff --git a/.sqlx/query-53be3ef02ba8b3deb3a74b184a4eb93e01fd0983c19aff3fca67bdc0afab4f37.json b/.sqlx/query-53be3ef02ba8b3deb3a74b184a4eb93e01fd0983c19aff3fca67bdc0afab4f37.json new file mode 100644 index 0000000..739ccba --- /dev/null +++ b/.sqlx/query-53be3ef02ba8b3deb3a74b184a4eb93e01fd0983c19aff3fca67bdc0afab4f37.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO items (name, parent, class, original_packaging, description)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Uuid", + "Uuid", + "Uuid", + "Varchar" + ] + }, + "nullable": [ + false + ] + }, + "hash": "53be3ef02ba8b3deb3a74b184a4eb93e01fd0983c19aff3fca67bdc0afab4f37" +} diff --git a/.sqlx/query-58969747bdccae4a2d3ad8d8117aa92283151d67f52fbb22e5d976b1c6a5c367.json b/.sqlx/query-58969747bdccae4a2d3ad8d8117aa92283151d67f52fbb22e5d976b1c6a5c367.json deleted file mode 100644 index eb09468..0000000 --- a/.sqlx/query-58969747bdccae4a2d3ad8d8117aa92283151d67f52fbb22e5d976b1c6a5c367.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items ORDER BY created_at", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "58969747bdccae4a2d3ad8d8117aa92283151d67f52fbb22e5d976b1c6a5c367" -} diff --git a/.sqlx/query-6737f54ceb18b9b744bac838801edbbdfe2cbf68a6346f93c072d47f5add9e46.json b/.sqlx/query-6737f54ceb18b9b744bac838801edbbdfe2cbf68a6346f93c072d47f5add9e46.json new file mode 100644 index 0000000..9a1b5aa --- /dev/null +++ b/.sqlx/query-6737f54ceb18b9b744bac838801edbbdfe2cbf68a6346f93c072d47f5add9e46.json @@ -0,0 +1,19 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE items\n SET name = $2, parent = $3, class = $4, original_packaging = $5, description = $6\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar", + "Uuid", + "Uuid", + "Uuid", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "6737f54ceb18b9b744bac838801edbbdfe2cbf68a6346f93c072d47f5add9e46" +} diff --git a/.sqlx/query-68d93c71840f87d5f5bb14b4d7fc34edd6be47cd1706b3612a332bbfd4bb54b4.json b/.sqlx/query-68d93c71840f87d5f5bb14b4d7fc34edd6be47cd1706b3612a332bbfd4bb54b4.json deleted file mode 100644 index 5ae565b..0000000 --- a/.sqlx/query-68d93c71840f87d5f5bb14b4d7fc34edd6be47cd1706b3612a332bbfd4bb54b4.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO item_classes (name, parent, description) VALUES ($1, $2, $3) RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Varchar" - ] - }, - "nullable": [ - false, - false, - true, - false, - false - ] - }, - "hash": "68d93c71840f87d5f5bb14b4d7fc34edd6be47cd1706b3612a332bbfd4bb54b4" -} diff --git a/.sqlx/query-6dd8b63f4aaefbc1ab5d5a9bae2338a7275ba56a9b17278fd886175c3a27b0dd.json b/.sqlx/query-6dd8b63f4aaefbc1ab5d5a9bae2338a7275ba56a9b17278fd886175c3a27b0dd.json deleted file mode 100644 index bef38c8..0000000 --- a/.sqlx/query-6dd8b63f4aaefbc1ab5d5a9bae2338a7275ba56a9b17278fd886175c3a27b0dd.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT type as \"type!\" FROM\n (SELECT id, 'item' AS \"type\" FROM items\n UNION ALL\n SELECT id, 'item_class' AS \"type\" FROM item_classes) id_mapping\n WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "type!", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "6dd8b63f4aaefbc1ab5d5a9bae2338a7275ba56a9b17278fd886175c3a27b0dd" -} diff --git a/.sqlx/query-719aff68ead7ada499158fec5e9f8f3c3841a4424da04aee0136c7e4f8df79e7.json b/.sqlx/query-719aff68ead7ada499158fec5e9f8f3c3841a4424da04aee0136c7e4f8df79e7.json new file mode 100644 index 0000000..ea7e0a5 --- /dev/null +++ b/.sqlx/query-719aff68ead7ada499158fec5e9f8f3c3841a4424da04aee0136c7e4f8df79e7.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT type as \"type!\"\n FROM (SELECT id, 'item' AS \"type\" FROM items\n UNION ALL\n SELECT id, 'item_class' AS \"type\" FROM item_classes) id_mapping\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "type!", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "719aff68ead7ada499158fec5e9f8f3c3841a4424da04aee0136c7e4f8df79e7" +} diff --git a/.sqlx/query-79d8bfe2ed76ee550cdc31f282f598749d931af69a80d24f4575a4bc2c740f3b.json b/.sqlx/query-79d8bfe2ed76ee550cdc31f282f598749d931af69a80d24f4575a4bc2c740f3b.json deleted file mode 100644 index 9e9b102..0000000 --- a/.sqlx/query-79d8bfe2ed76ee550cdc31f282f598749d931af69a80d24f4575a4bc2c740f3b.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE id = ANY ($1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "79d8bfe2ed76ee550cdc31f282f598749d931af69a80d24f4575a4bc2c740f3b" -} diff --git a/.sqlx/query-7b3f478c9b217e09043daeca5dc574381493e559860258f4f6bffb12825b1ed7.json b/.sqlx/query-7b3f478c9b217e09043daeca5dc574381493e559860258f4f6bffb12825b1ed7.json new file mode 100644 index 0000000..a2e25ad --- /dev/null +++ b/.sqlx/query-7b3f478c9b217e09043daeca5dc574381493e559860258f4f6bffb12825b1ed7.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT items.id, items.name, item_classes.name AS \"class_name\"\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "7b3f478c9b217e09043daeca5dc574381493e559860258f4f6bffb12825b1ed7" +} diff --git a/.sqlx/query-7eeb752c8b00ac4000104f0254186f1f9fdb076e8f8b98f10fc1b981cfe8038c.json b/.sqlx/query-7eeb752c8b00ac4000104f0254186f1f9fdb076e8f8b98f10fc1b981cfe8038c.json deleted file mode 100644 index 1fff101..0000000 --- a/.sqlx/query-7eeb752c8b00ac4000104f0254186f1f9fdb076e8f8b98f10fc1b981cfe8038c.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE short_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "7eeb752c8b00ac4000104f0254186f1f9fdb076e8f8b98f10fc1b981cfe8038c" -} diff --git a/.sqlx/query-82df5c0633a655d376b8a91e9f11981cfee40fd04cb4e3552cc5f4ebf4ed0572.json b/.sqlx/query-82df5c0633a655d376b8a91e9f11981cfee40fd04cb4e3552cc5f4ebf4ed0572.json deleted file mode 100644 index 5593f47..0000000 --- a/.sqlx/query-82df5c0633a655d376b8a91e9f11981cfee40fd04cb4e3552cc5f4ebf4ed0572.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE items SET name = $2, parent = $3, class = $4, original_packaging = $5, description = $6 WHERE id = $1 RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Uuid", - "Uuid", - "Uuid", - "Varchar" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "82df5c0633a655d376b8a91e9f11981cfee40fd04cb4e3552cc5f4ebf4ed0572" -} diff --git a/.sqlx/query-835261895368c22af5780c4e4b69b518f5cb6936e3e4ea23c200b13787a401e1.json b/.sqlx/query-835261895368c22af5780c4e4b69b518f5cb6936e3e4ea23c200b13787a401e1.json new file mode 100644 index 0000000..8ea7142 --- /dev/null +++ b/.sqlx/query-835261895368c22af5780c4e4b69b518f5cb6936e3e4ea23c200b13787a401e1.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE item_classes\n SET name = $2, parent = $3, description = $4\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar", + "Uuid", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "835261895368c22af5780c4e4b69b518f5cb6936e3e4ea23c200b13787a401e1" +} diff --git a/.sqlx/query-83fe5e57b2c7db1fdfbf05749646ac6ebe71d81057cd865c5f946d6ddb62b552.json b/.sqlx/query-83fe5e57b2c7db1fdfbf05749646ac6ebe71d81057cd865c5f946d6ddb62b552.json new file mode 100644 index 0000000..44a24e0 --- /dev/null +++ b/.sqlx/query-83fe5e57b2c7db1fdfbf05749646ac6ebe71d81057cd865c5f946d6ddb62b552.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO item_classes (name, parent, description)\n VALUES ($1, $2, $3)\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Uuid", + "Varchar" + ] + }, + "nullable": [ + false + ] + }, + "hash": "83fe5e57b2c7db1fdfbf05749646ac6ebe71d81057cd865c5f946d6ddb62b552" +} diff --git a/.sqlx/query-c552c0a40bc8995cb95726a85f1d0c0b86eb2322035e6a720e2e6d425072a8c1.json b/.sqlx/query-84b4620db57dd9b963e09153c3de5938b3959ae41744098c4e9565404abf09ae.json similarity index 56% rename from .sqlx/query-c552c0a40bc8995cb95726a85f1d0c0b86eb2322035e6a720e2e6d425072a8c1.json rename to .sqlx/query-84b4620db57dd9b963e09153c3de5938b3959ae41744098c4e9565404abf09ae.json index e91dc01..7074e7d 100644 --- a/.sqlx/query-c552c0a40bc8995cb95726a85f1d0c0b86eb2322035e6a720e2e6d425072a8c1.json +++ b/.sqlx/query-84b4620db57dd9b963e09153c3de5938b3959ae41744098c4e9565404abf09ae.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM item_classes WHERE parent = $1", + "query": "SELECT\n class.id,\n class.name,\n class.description,\n class.parent,\n parent.name AS \"parent_name?\"\n FROM item_classes AS \"class\"\n LEFT JOIN item_classes AS \"parent\"\n ON class.parent = parent.id\n WHERE class.id = $1", "describe": { "columns": [ { @@ -15,17 +15,17 @@ }, { "ordinal": 2, + "name": "description", + "type_info": "Varchar" + }, + { + "ordinal": 3, "name": "parent", "type_info": "Uuid" }, - { - "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - }, { "ordinal": 4, - "name": "description", + "name": "parent_name?", "type_info": "Varchar" } ], @@ -37,10 +37,10 @@ "nullable": [ false, false, - true, false, + true, false ] }, - "hash": "c552c0a40bc8995cb95726a85f1d0c0b86eb2322035e6a720e2e6d425072a8c1" + "hash": "84b4620db57dd9b963e09153c3de5938b3959ae41744098c4e9565404abf09ae" } diff --git a/.sqlx/query-857bbaea0fa73c37679d927aa627ee841c5b89c86af12f32493ba05c0f0ead91.json b/.sqlx/query-857bbaea0fa73c37679d927aa627ee841c5b89c86af12f32493ba05c0f0ead91.json new file mode 100644 index 0000000..23d3be8 --- /dev/null +++ b/.sqlx/query-857bbaea0fa73c37679d927aa627ee841c5b89c86af12f32493ba05c0f0ead91.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO items (name, parent, class, original_packaging, description)\n SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[])\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "VarcharArray", + "UuidArray", + "UuidArray", + "UuidArray", + "VarcharArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "857bbaea0fa73c37679d927aa627ee841c5b89c86af12f32493ba05c0f0ead91" +} diff --git a/.sqlx/query-9286b6ee0c08b8446ede68b890b8bf3208b55b51433ec92b4e7a452929a81945.json b/.sqlx/query-9286b6ee0c08b8446ede68b890b8bf3208b55b51433ec92b4e7a452929a81945.json deleted file mode 100644 index c12a41f..0000000 --- a/.sqlx/query-9286b6ee0c08b8446ede68b890b8bf3208b55b51433ec92b4e7a452929a81945.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT items.*\n FROM items\n INNER JOIN\n unnest((SELECT parents FROM item_tree WHERE id = $1))\n WITH ORDINALITY AS parents(id, n)\n ON items.id = parents.id\n ORDER BY parents.n;", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "9286b6ee0c08b8446ede68b890b8bf3208b55b51433ec92b4e7a452929a81945" -} diff --git a/.sqlx/query-94958a3a57c3178e6b0de5723b1fbc5433e972b5522b367098afe6cb90a30bf2.json b/.sqlx/query-94958a3a57c3178e6b0de5723b1fbc5433e972b5522b367098afe6cb90a30bf2.json deleted file mode 100644 index 904cff3..0000000 --- a/.sqlx/query-94958a3a57c3178e6b0de5723b1fbc5433e972b5522b367098afe6cb90a30bf2.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE class = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "94958a3a57c3178e6b0de5723b1fbc5433e972b5522b367098afe6cb90a30bf2" -} diff --git a/.sqlx/query-97d6a7ee24e75dc5a9dc41a581e1013767fe36575c28574733c5ab5cbf557fb5.json b/.sqlx/query-97d6a7ee24e75dc5a9dc41a581e1013767fe36575c28574733c5ab5cbf557fb5.json deleted file mode 100644 index cfbdff0..0000000 --- a/.sqlx/query-97d6a7ee24e75dc5a9dc41a581e1013767fe36575c28574733c5ab5cbf557fb5.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT unnest(parents) as \"parents!\" FROM item_tree WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parents!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "97d6a7ee24e75dc5a9dc41a581e1013767fe36575c28574733c5ab5cbf557fb5" -} diff --git a/.sqlx/query-9c1a1e9c33539f6ec4800c9e8fc8ce3b65f49c34ad848321dc1b151c21cb0043.json b/.sqlx/query-9c1a1e9c33539f6ec4800c9e8fc8ce3b65f49c34ad848321dc1b151c21cb0043.json new file mode 100644 index 0000000..893c114 --- /dev/null +++ b/.sqlx/query-9c1a1e9c33539f6ec4800c9e8fc8ce3b65f49c34ad848321dc1b151c21cb0043.json @@ -0,0 +1,70 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n items.id,\n items.short_id,\n items.name,\n items.class,\n item_classes.name AS \"class_name\",\n items.original_packaging,\n op.name AS \"original_packaging_name?\",\n op_class.name AS \"original_packaging_class_name?\",\n items.description\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id\n LEFT JOIN items AS \"op\"\n ON items.original_packaging = op.id\n LEFT JOIN item_classes AS \"op_class\"\n ON op.class = op_class.id\n WHERE items.id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "short_id", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "class", + "type_info": "Uuid" + }, + { + "ordinal": 4, + "name": "class_name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "original_packaging", + "type_info": "Uuid" + }, + { + "ordinal": 6, + "name": "original_packaging_name?", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "original_packaging_class_name?", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "description", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + true, + true, + false, + false + ] + }, + "hash": "9c1a1e9c33539f6ec4800c9e8fc8ce3b65f49c34ad848321dc1b151c21cb0043" +} diff --git a/.sqlx/query-9e1704d0b60906061460e2c972fccf5d87b053857c161651360e5a8fdd80e397.json b/.sqlx/query-9e1704d0b60906061460e2c972fccf5d87b053857c161651360e5a8fdd80e397.json new file mode 100644 index 0000000..33de83f --- /dev/null +++ b/.sqlx/query-9e1704d0b60906061460e2c972fccf5d87b053857c161651360e5a8fdd80e397.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT items.id, items.name, item_classes.name AS \"class_name\"\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id\n WHERE items.parent = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "9e1704d0b60906061460e2c972fccf5d87b053857c161651360e5a8fdd80e397" +} diff --git a/.sqlx/query-a6f646089b4424deffc0b1a454bcfa2f2a497180e2517a937471f81a1f9c5538.json b/.sqlx/query-a6f646089b4424deffc0b1a454bcfa2f2a497180e2517a937471f81a1f9c5538.json deleted file mode 100644 index 666377d..0000000 --- a/.sqlx/query-a6f646089b4424deffc0b1a454bcfa2f2a497180e2517a937471f81a1f9c5538.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id as \"id!\", parents as \"parents!\" FROM item_tree", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "parents!", - "type_info": "UuidArray" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - true, - true - ] - }, - "hash": "a6f646089b4424deffc0b1a454bcfa2f2a497180e2517a937471f81a1f9c5538" -} diff --git a/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json b/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json deleted file mode 100644 index 7a3a4db..0000000 --- a/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO items (name, parent, class, original_packaging, description)\n SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[])\n RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "VarcharArray", - "UuidArray", - "UuidArray", - "UuidArray", - "VarcharArray" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef" -} diff --git a/.sqlx/query-b80bb07ab582b0705b5c0370066730edf887d66a4196a0834c59f0df9f9314d3.json b/.sqlx/query-b80bb07ab582b0705b5c0370066730edf887d66a4196a0834c59f0df9f9314d3.json deleted file mode 100644 index 9a7c965..0000000 --- a/.sqlx/query-b80bb07ab582b0705b5c0370066730edf887d66a4196a0834c59f0df9f9314d3.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO items (name, parent, class, original_packaging, description) VALUES ($1, $2, $3, $4, $5) RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Uuid", - "Uuid", - "Varchar" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "b80bb07ab582b0705b5c0370066730edf887d66a4196a0834c59f0df9f9314d3" -} diff --git a/.sqlx/query-1e9045f52c002a19b815351fee1c7ee7520478ff4f5886b4523fb0dc4df0e204.json b/.sqlx/query-c49b88eda9a62743783bc894f01bb6198594f94a3e0856abde0efdb4e49dbab8.json similarity index 59% rename from .sqlx/query-1e9045f52c002a19b815351fee1c7ee7520478ff4f5886b4523fb0dc4df0e204.json rename to .sqlx/query-c49b88eda9a62743783bc894f01bb6198594f94a3e0856abde0efdb4e49dbab8.json index 7d5a7be..9b27b4a 100644 --- a/.sqlx/query-1e9045f52c002a19b815351fee1c7ee7520478ff4f5886b4523fb0dc4df0e204.json +++ b/.sqlx/query-c49b88eda9a62743783bc894f01bb6198594f94a3e0856abde0efdb4e49dbab8.json @@ -1,45 +1,35 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE id = $1", + "query": "SELECT\n items.name,\n items.parent,\n items.class,\n item_classes.name AS \"class_name\",\n items.original_packaging,\n items.description\n FROM items\n JOIN item_classes\n ON items.class = item_classes.id\n WHERE items.id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, "name": "name", "type_info": "Varchar" }, { - "ordinal": 2, + "ordinal": 1, "name": "parent", "type_info": "Uuid" }, { - "ordinal": 3, + "ordinal": 2, "name": "class", "type_info": "Uuid" }, + { + "ordinal": 3, + "name": "class_name", + "type_info": "Varchar" + }, { "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, "name": "original_packaging", "type_info": "Uuid" }, { - "ordinal": 7, + "ordinal": 5, "name": "description", "type_info": "Varchar" } @@ -50,15 +40,13 @@ ] }, "nullable": [ - false, true, true, false, false, - false, true, false ] }, - "hash": "1e9045f52c002a19b815351fee1c7ee7520478ff4f5886b4523fb0dc4df0e204" + "hash": "c49b88eda9a62743783bc894f01bb6198594f94a3e0856abde0efdb4e49dbab8" } diff --git a/.sqlx/query-6e7b3389c47091d9fc8c7638b401b413f804c6f3e082a818b67ebab0938acb39.json b/.sqlx/query-c7e35dee5f56da8da3c083e191f396b95917b15768ebe0250ce7840391036616.json similarity index 58% rename from .sqlx/query-6e7b3389c47091d9fc8c7638b401b413f804c6f3e082a818b67ebab0938acb39.json rename to .sqlx/query-c7e35dee5f56da8da3c083e191f396b95917b15768ebe0250ce7840391036616.json index 147b301..1745408 100644 --- a/.sqlx/query-6e7b3389c47091d9fc8c7638b401b413f804c6f3e082a818b67ebab0938acb39.json +++ b/.sqlx/query-c7e35dee5f56da8da3c083e191f396b95917b15768ebe0250ce7840391036616.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM item_classes ORDER BY created_at", + "query": "SELECT class.id, class.name, class.parent, parent.name AS \"parent_name?\"\n FROM item_classes AS \"class\"\n LEFT JOIN item_classes AS \"parent\"\n ON class.parent = parent.id\n ORDER BY class.created_at\n ", "describe": { "columns": [ { @@ -20,12 +20,7 @@ }, { "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "description", + "name": "parent_name?", "type_info": "Varchar" } ], @@ -36,9 +31,8 @@ false, false, true, - false, false ] }, - "hash": "6e7b3389c47091d9fc8c7638b401b413f804c6f3e082a818b67ebab0938acb39" + "hash": "c7e35dee5f56da8da3c083e191f396b95917b15768ebe0250ce7840391036616" } diff --git a/.sqlx/query-5cf503740d71431d8b9d256960c0ce194ede48a1c46326315cae9f07347597f6.json b/.sqlx/query-cfce4ef19a3e0a3a0582d3c7d266b952397a017b33c351c5a33f1daa78ab908a.json similarity index 57% rename from .sqlx/query-5cf503740d71431d8b9d256960c0ce194ede48a1c46326315cae9f07347597f6.json rename to .sqlx/query-cfce4ef19a3e0a3a0582d3c7d266b952397a017b33c351c5a33f1daa78ab908a.json index 5f22e6d..0c4b0cf 100644 --- a/.sqlx/query-5cf503740d71431d8b9d256960c0ce194ede48a1c46326315cae9f07347597f6.json +++ b/.sqlx/query-cfce4ef19a3e0a3a0582d3c7d266b952397a017b33c351c5a33f1daa78ab908a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id FROM item_classes ORDER BY created_at", + "query": "SELECT id FROM items WHERE short_id = $1", "describe": { "columns": [ { @@ -10,11 +10,13 @@ } ], "parameters": { - "Left": [] + "Left": [ + "Int4" + ] }, "nullable": [ false ] }, - "hash": "5cf503740d71431d8b9d256960c0ce194ede48a1c46326315cae9f07347597f6" + "hash": "cfce4ef19a3e0a3a0582d3c7d266b952397a017b33c351c5a33f1daa78ab908a" } diff --git a/.sqlx/query-e0129afa95f896d79f772fb177a8f9229dfbbfd289039db7b733c7d1d050f4bf.json b/.sqlx/query-e0129afa95f896d79f772fb177a8f9229dfbbfd289039db7b733c7d1d050f4bf.json deleted file mode 100644 index 0635a6d..0000000 --- a/.sqlx/query-e0129afa95f896d79f772fb177a8f9229dfbbfd289039db7b733c7d1d050f4bf.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE item_classes SET name = $2, parent = $3, description = $4 WHERE id = $1 RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Uuid", - "Varchar" - ] - }, - "nullable": [ - false, - false, - true, - false, - false - ] - }, - "hash": "e0129afa95f896d79f772fb177a8f9229dfbbfd289039db7b733c7d1d050f4bf" -} diff --git a/.sqlx/query-efe2258db42b60f732d562d106842b19308d9a558703b02e758f60e7d8644d00.json b/.sqlx/query-efe2258db42b60f732d562d106842b19308d9a558703b02e758f60e7d8644d00.json deleted file mode 100644 index 40f8a6c..0000000 --- a/.sqlx/query-efe2258db42b60f732d562d106842b19308d9a558703b02e758f60e7d8644d00.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM items WHERE original_packaging = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "class", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "short_id", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "original_packaging", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "description", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - true, - false, - false, - false, - true, - false - ] - }, - "hash": "efe2258db42b60f732d562d106842b19308d9a558703b02e758f60e7d8644d00" -} diff --git a/.sqlx/query-308962c26250f9312287a3f2f21e5da76e4cf488eedd4704019b4f14b6fbafb2.json b/.sqlx/query-f446c6ab881c166102f3b20056e0709be6fbcfc596063878c0bad9df00037dbe.json similarity index 58% rename from .sqlx/query-308962c26250f9312287a3f2f21e5da76e4cf488eedd4704019b4f14b6fbafb2.json rename to .sqlx/query-f446c6ab881c166102f3b20056e0709be6fbcfc596063878c0bad9df00037dbe.json index d9eb78e..4c5d1f4 100644 --- a/.sqlx/query-308962c26250f9312287a3f2f21e5da76e4cf488eedd4704019b4f14b6fbafb2.json +++ b/.sqlx/query-f446c6ab881c166102f3b20056e0709be6fbcfc596063878c0bad9df00037dbe.json @@ -1,30 +1,20 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM item_classes WHERE id = $1", + "query": "SELECT name, parent, description FROM item_classes WHERE id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, "name": "name", "type_info": "Varchar" }, { - "ordinal": 2, + "ordinal": 1, "name": "parent", "type_info": "Uuid" }, { - "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, + "ordinal": 2, "name": "description", "type_info": "Varchar" } @@ -35,12 +25,10 @@ ] }, "nullable": [ - false, false, true, - false, false ] }, - "hash": "308962c26250f9312287a3f2f21e5da76e4cf488eedd4704019b4f14b6fbafb2" + "hash": "f446c6ab881c166102f3b20056e0709be6fbcfc596063878c0bad9df00037dbe" } diff --git a/.sqlx/query-fc5e536bddb5d76021164ae4f5a6b0ad52d1ae047fd004f1359be7122facf8e7.json b/.sqlx/query-fc5e536bddb5d76021164ae4f5a6b0ad52d1ae047fd004f1359be7122facf8e7.json new file mode 100644 index 0000000..933db4d --- /dev/null +++ b/.sqlx/query-fc5e536bddb5d76021164ae4f5a6b0ad52d1ae047fd004f1359be7122facf8e7.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, name FROM item_classes", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false + ] + }, + "hash": "fc5e536bddb5d76021164ae4f5a6b0ad52d1ae047fd004f1359be7122facf8e7" +} diff --git a/.sqlx/query-ff39480d9395b357d71e9af3eb37fa308f4df6a0ca6442fa7f9bbda1e34ffbbe.json b/.sqlx/query-ff39480d9395b357d71e9af3eb37fa308f4df6a0ca6442fa7f9bbda1e34ffbbe.json new file mode 100644 index 0000000..36cc7c5 --- /dev/null +++ b/.sqlx/query-ff39480d9395b357d71e9af3eb37fa308f4df6a0ca6442fa7f9bbda1e34ffbbe.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT items.id, items.name, item_classes.name AS \"class_name\"\n FROM items\n JOIN unnest((SELECT parents FROM item_tree WHERE id = $1))\n WITH ORDINALITY AS parents(id, n)\n ON items.id = parents.id\n JOIN item_classes\n ON items.class = item_classes.id\n ORDER BY parents.n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "class_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "ff39480d9395b357d71e9af3eb37fa308f4df6a0ca6442fa7f9bbda1e34ffbbe" +} diff --git a/Cargo.lock b/Cargo.lock index c1243ca..99c9ad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,6 +1113,15 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1165,6 +1174,7 @@ dependencies = [ "enum-iterator", "env_logger", "futures-util", + "itertools", "log", "maud", "mime", diff --git a/Cargo.toml b/Cargo.toml index a087601..b2ed5d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ datamatrix = "0.3.1" enum-iterator = "2.1.0" env_logger = "0.11.3" futures-util = "0.3.30" +itertools = "0.13.0" log = "0.4.21" maud = { version = "0.26.0", features = ["actix-web"] } mime = "0.3.17" diff --git a/src/frontend/item/add.rs b/src/frontend/item/add.rs index d1d035c..f5eff99 100644 --- a/src/frontend/item/add.rs +++ b/src/frontend/item/add.rs @@ -8,12 +8,10 @@ use actix_identity::Identity; use actix_web::{error, get, post, web, HttpRequest, Responder}; use maud::html; use serde::Deserialize; -use sqlx::PgPool; +use sqlx::{query_scalar, PgPool}; use uuid::Uuid; use crate::frontend::templates::{self, datalist, forms, helpers::PageActionGroup, TemplateConfig}; -use crate::manage; -use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get).service(post); @@ -130,51 +128,64 @@ async fn post( user: Identity, ) -> actix_web::Result { let data = data.into_inner(); - let new_item = NewItem { - name: data.name, - class: data.class, - parent: data.parent, - original_packaging: data.original_packaging, - description: data.description, - }; if data.quantity == 1 { - let item = manage::item::add(&pool, new_item) - .await - .map_err(error::ErrorInternalServerError)?; - Ok( - web::Redirect::to("/item/".to_owned() + &item.id.to_string()) + query_scalar!( + "INSERT INTO items (name, parent, class, original_packaging, description) + VALUES ($1, $2, $3, $4, $5) + RETURNING id", + data.name, + data.parent, + data.class, + data.original_packaging, + data.description + ) + .fetch_one(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError) + .map(|id| { + web::Redirect::to("/item/".to_owned() + &id.to_string()) .see_other() .respond_to(&req) - .map_into_boxed_body(), - ) + .map_into_boxed_body() + }) } else { - let items = manage::item::add_multiple(&pool, new_item, data.quantity) - .await - .map_err(error::ErrorInternalServerError)?; - Ok(templates::base( - TemplateConfig { - path: "/items/add", - title: Some("Added Items"), - page_title: Some(Box::new("Added Items")), - page_actions: vec![PageActionGroup::generate_labels( - &items.iter().map(|i| i.id).collect::>(), - )], - user: Some(user), - ..Default::default() - }, - html! { - ul { - @for item in items { - li { - a href={ "/item/" (item.id) } { (item.id) } + query_scalar!( + "INSERT INTO items (name, parent, class, original_packaging, description) + SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[]) + RETURNING id", + &vec![data.name; data.quantity] as &[Option], + &vec![data.parent; data.quantity] as &[Option], + &vec![data.class; data.quantity], + &vec![data.original_packaging; data.quantity] as &[Option], + &vec![data.description; data.quantity] + ) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError) + .map(|ids| { + templates::base( + TemplateConfig { + path: "/items/add", + title: Some("Added Items"), + page_title: Some(Box::new("Added Items")), + page_actions: vec![PageActionGroup::generate_labels(&ids)], + user: Some(user), + ..Default::default() + }, + html! { + ul { + @for id in &ids { + li { + a href={ "/item/" (id) } { (id) } + } } } - } - a href="/items" { "Back to all items" } - }, - ) - .respond_to(&req) - .map_into_boxed_body()) + a href="/items" { "Back to all items" } + }, + ) + .respond_to(&req) + .map_into_boxed_body() + }) } } diff --git a/src/frontend/item/delete.rs b/src/frontend/item/delete.rs index f7f548b..7da12b2 100644 --- a/src/frontend/item/delete.rs +++ b/src/frontend/item/delete.rs @@ -4,11 +4,9 @@ use actix_identity::Identity; use actix_web::{error, post, web, Responder}; -use sqlx::PgPool; +use sqlx::{query, PgPool}; use uuid::Uuid; -use crate::manage; - pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(post); } @@ -21,7 +19,8 @@ async fn post( ) -> actix_web::Result { let id = path.into_inner(); - manage::item::delete(&pool, id) + query!("DELETE FROM items WHERE id = $1", id) + .execute(pool.as_ref()) .await .map_err(error::ErrorInternalServerError)?; diff --git a/src/frontend/item/edit.rs b/src/frontend/item/edit.rs index 69a0054..cf90bd0 100644 --- a/src/frontend/item/edit.rs +++ b/src/frontend/item/edit.rs @@ -7,17 +7,25 @@ use std::fmt::Display; use actix_identity::Identity; use actix_web::{error, get, post, web, Responder}; use maud::html; -use sqlx::PgPool; +use serde::Deserialize; +use sqlx::{query, PgPool}; use uuid::Uuid; use crate::frontend::templates::{self, datalist, forms, helpers::ItemName, TemplateConfig}; -use crate::manage; -use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get).service(post); } +#[derive(Deserialize)] +struct ItemEditForm { + name: Option, + parent: Option, + class: Uuid, + original_packaging: Option, + description: String, +} + #[get("/item/{id}/edit")] async fn get( pool: web::Data, @@ -26,13 +34,35 @@ async fn get( ) -> actix_web::Result { let id = path.into_inner(); - let item = manage::item::get(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; - - let item_class = manage::item_class::get(&pool, item.class) - .await - .map_err(error::ErrorInternalServerError)?; + let (item_name, form) = query!( + r#"SELECT + items.name, + items.parent, + items.class, + item_classes.name AS "class_name", + items.original_packaging, + items.description + FROM items + JOIN item_classes + ON items.class = item_classes.id + WHERE items.id = $1"#, + id + ) + .map(|row| { + ( + ItemName::new(row.name.as_ref(), &row.class_name), + ItemEditForm { + name: row.name, + parent: row.parent, + class: row.class, + original_packaging: row.original_packaging, + description: row.description, + }, + ) + }) + .fetch_one(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; let datalist_items = datalist::items(&pool) .await @@ -42,13 +72,12 @@ async fn get( .await .map_err(error::ErrorInternalServerError)?; - let item_name = ItemName::new(item.name.as_ref(), &item_class.name); let mut title = item_name.to_string(); title.push_str(" – Edit Item"); Ok(templates::base( TemplateConfig { - path: &format!("/item/{}/edit", item.id), + path: &format!("/item/{}/edit", id), title: Some(&title), page_title: Some(Box::new(item_name.clone())), datalists: vec![&datalist_items, &datalist_item_classes], @@ -63,7 +92,7 @@ async fn get( title: "UUID", required: true, disabled: true, - value: Some(&item.id), + value: Some(&id), ..Default::default() }) (forms::InputGroup { @@ -71,8 +100,8 @@ async fn get( name: "name", title: "Name", optional: true, - disabled: item.name.is_none(), - value: item.name.as_ref().map(|s| s as &dyn Display), + disabled: form.name.is_none(), + value: form.name.as_ref().map(|s| s as &dyn Display), ..Default::default() }) (forms::InputGroup { @@ -80,7 +109,7 @@ async fn get( name: "class", title: "Class", required: true, - value: Some(&item.class), + value: Some(&form.class), datalist: Some(&datalist_item_classes), ..Default::default() }) @@ -89,8 +118,8 @@ async fn get( name: "parent", title: "Parent", optional: true, - value: item.parent.as_ref().map(|id| id as &dyn Display), - disabled: item.parent.is_none(), + value: form.parent.as_ref().map(|id| id as &dyn Display), + disabled: form.parent.is_none(), datalist: Some(&datalist_items), ..Default::default() }) @@ -99,8 +128,8 @@ async fn get( name: "original_packaging", title: "Original Packaging", optional: true, - value: item.original_packaging.as_ref().map(|id| id as &dyn Display), - disabled: item.original_packaging.is_none(), + value: form.original_packaging.as_ref().map(|id| id as &dyn Display), + disabled: form.original_packaging.is_none(), datalist: Some(&datalist_items), ..Default::default() }) @@ -108,7 +137,7 @@ async fn get( r#type: forms::InputType::Textarea, name: "description", title: "Description ", - value: Some(&item.description), + value: Some(&form.description), ..Default::default() }) @@ -122,14 +151,25 @@ async fn get( async fn post( pool: web::Data, path: web::Path, - data: web::Form, + data: web::Form, _user: Identity, ) -> actix_web::Result { let id = path.into_inner(); - let item = manage::item::update(&pool, id, data.into_inner()) - .await - .map_err(error::ErrorInternalServerError)?; + query!( + "UPDATE items + SET name = $2, parent = $3, class = $4, original_packaging = $5, description = $6 + WHERE id = $1", + id, + data.name, + data.parent, + data.class, + data.original_packaging, + data.description + ) + .execute(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - Ok(web::Redirect::to("/item/".to_owned() + &item.id.to_string()).see_other()) + Ok(web::Redirect::to("/item/".to_owned() + &id.to_string()).see_other()) } diff --git a/src/frontend/item/list.rs b/src/frontend/item/list.rs index 622ec9e..0af11a1 100644 --- a/src/frontend/item/list.rs +++ b/src/frontend/item/list.rs @@ -2,12 +2,10 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -use std::collections::HashMap; - use actix_identity::Identity; use actix_web::{error, get, web, Responder}; use maud::html; -use sqlx::PgPool; +use sqlx::{query, PgPool}; use uuid::Uuid; use crate::frontend::templates::{ @@ -15,44 +13,75 @@ use crate::frontend::templates::{ helpers::{Colour, ItemName, ItemPreview, PageAction, PageActionGroup, PageActionMethod}, TemplateConfig, }; -use crate::manage; -use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get); } +struct ItemListEntry { + id: Uuid, + name: ItemName, + class: Uuid, + class_name: String, + parents: Vec, +} + #[get("/items")] async fn get(pool: web::Data, user: Identity) -> actix_web::Result { - let item_list = manage::item::get_all(&pool) - .await - .map_err(error::ErrorInternalServerError)?; + let items = query!( + r#" + WITH RECURSIVE cte AS ( + SELECT + id, + ARRAY[]::UUID[] AS parents, + ARRAY[]::VARCHAR[] AS parent_names, + ARRAY[]::VARCHAR[] AS parent_class_names + FROM items + WHERE parent IS NULL - let items = manage::item::get_all_as_map(&pool) - .await - .map_err(error::ErrorInternalServerError)?; + UNION - let item_classes = manage::item_class::get_all_as_map(&pool) - .await - .map_err(error::ErrorInternalServerError)?; - - let item_tree = manage::item::get_all_parents(&pool) - .await - .map_err(error::ErrorInternalServerError)?; - - // TODO: remove clone (should be possible without it) - let item_parents: HashMap> = item_tree - .iter() - .map(|(id, parent_ids)| { - ( - *id, - parent_ids - .iter() - .map(|parent_id| items.get(parent_id).unwrap().clone()) - .collect(), - ) - }) - .collect(); + SELECT + items.id, + cte.parents || items.parent, + cte.parent_names || parent.name, + cte.parent_class_names || parent_class.name + FROM cte + JOIN items + ON items.parent = cte.id + JOIN items AS "parent" + ON parent.id = cte.id + JOIN item_classes AS "parent_class" + ON parent.class = parent_class.id + ) + SELECT + cte.id AS "id!", + items.name, + items.class, + item_classes.name AS "class_name", + cte.parents AS "parents!", + cte.parent_names AS "parent_names!: Vec>", + cte.parent_class_names AS "parent_class_names!" + FROM cte + JOIN items + ON cte.id = items.id + JOIN item_classes + ON items.class = item_classes.id + ORDER BY items.created_at + "# + ) + .map(|row| ItemListEntry { + id: row.id, + name: ItemName::new(row.name.as_ref(), &row.class_name), + class: row.class, + class_name: row.class_name, + parents: itertools::izip!(row.parents, row.parent_names, row.parent_class_names) + .map(|(id, name, class_name)| ItemPreview::from_parts(id, name.as_ref(), &class_name)) + .collect(), + }) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; Ok(templates::base( TemplateConfig { @@ -82,24 +111,11 @@ async fn get(pool: web::Data, user: Identity) -> actix_web::Result>(), - false - )) - } + td { (ItemPreview::new(item.id, item.name.clone().terse())) } + td { a href={ "/item-class/" (item.class) } { (item.class_name) } } + td { (templates::helpers::parents_breadcrumb(item.name, &item.parents, false)) } } } } diff --git a/src/frontend/item/show.rs b/src/frontend/item/show.rs index 569a43c..a45da76 100644 --- a/src/frontend/item/show.rs +++ b/src/frontend/item/show.rs @@ -5,7 +5,7 @@ use actix_identity::Identity; use actix_web::{error, get, web, Responder}; use maud::html; -use sqlx::PgPool; +use sqlx::{query, PgPool}; use uuid::Uuid; use crate::frontend::templates::{ @@ -13,12 +13,21 @@ use crate::frontend::templates::{ helpers::{Colour, ItemName, ItemPreview, PageAction, PageActionGroup, PageActionMethod}, TemplateConfig, }; -use crate::manage; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get); } +struct ItemDetails { + id: Uuid, + short_id: i32, + name: ItemName, + class: Uuid, + class_name: String, + original_packaging: Option, + description: String, +} + #[get("/item/{id}")] async fn get( pool: web::Data, @@ -27,46 +36,96 @@ async fn get( ) -> actix_web::Result { let id = path.into_inner(); - let item = manage::item::get(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; + let item = query!( + r#"SELECT + items.id, + items.short_id, + items.name, + items.class, + item_classes.name AS "class_name", + items.original_packaging, + op.name AS "original_packaging_name?", + op_class.name AS "original_packaging_class_name?", + items.description + FROM items + JOIN item_classes + ON items.class = item_classes.id + LEFT JOIN items AS "op" + ON items.original_packaging = op.id + LEFT JOIN item_classes AS "op_class" + ON op.class = op_class.id + WHERE items.id = $1"#, + id + ) + .map(|row| ItemDetails { + id: row.id, + short_id: row.short_id, + name: ItemName::new(row.name.as_ref(), &row.class_name), + class: row.class, + class_name: row.class_name, + original_packaging: row.original_packaging.map(|id| { + ItemPreview::from_parts( + id, + row.original_packaging_name.as_ref(), + &row.original_packaging_class_name.unwrap(), + ) + }), + description: row.description, + }) + .fetch_one(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - let item_classes = manage::item_class::get_all_as_map(&pool) - .await - .map_err(error::ErrorInternalServerError)?; + let parents = query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN unnest((SELECT parents FROM item_tree WHERE id = $1)) + WITH ORDINALITY AS parents(id, n) + ON items.id = parents.id + JOIN item_classes + ON items.class = item_classes.id + ORDER BY parents.n"#, + id + ) + .map(|row| ItemPreview::from_parts(row.id, row.name.as_ref(), &row.class_name)) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - let parents = manage::item::get_parents_details(&pool, item.id) - .await - .map_err(error::ErrorInternalServerError)?; + let children = query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN item_classes + ON items.class = item_classes.id + WHERE items.parent = $1"#, + id + ) + .map(|row| ItemPreview::from_parts(row.id, row.name.as_ref(), &row.class_name)) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - let children = manage::item::get_children(&pool, item.id) - .await - .map_err(error::ErrorInternalServerError)?; + let original_packaging_of = query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN item_classes + ON items.class = item_classes.id + WHERE items.original_packaging = $1"#, + id + ) + .map(|row| ItemPreview::from_parts(row.id, row.name.as_ref(), &row.class_name)) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - let original_packaging = match item.original_packaging { - Some(id) => Some( - manage::item::get(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?, - ), - None => None, - }; - - let original_packaging_of = manage::item::original_packaging_contents(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; - - let item_class = item_classes.get(&item.class).unwrap(); - - let item_name = ItemName::new(item.name.as_ref(), &item_class.name); - let mut title = item_name.to_string(); + let mut title = item.name.to_string(); title.push_str(" – Item Details"); Ok(templates::base( TemplateConfig { path: &format!("/item/{}", item.id), title: Some(&title), - page_title: Some(Box::new(item_name.clone())), + page_title: Some(Box::new(item.name.clone())), page_actions: vec![ (PageActionGroup::Button { action: PageAction { @@ -109,32 +168,21 @@ async fn get( } tr { th { "Name" } - td { (item_name.clone().terse()) } + td { (item.name.clone().terse()) } } tr { th { "Class" } - td { a href={ "/item-class/" (item.class) } { (item_class.name) } } + td { a href={ "/item-class/" (item.class) } { (item.class_name) } } } tr { th { "Parents" } - td { - (templates::helpers::parents_breadcrumb( - ItemName::new( - item.name.as_ref(), - &item_class.name - ), - &parents.iter().map(|parent| ItemPreview::from_parts(parent.id, parent.name.as_ref(), &item_classes.get(&parent.class).unwrap().name)).collect::>(), - true - )) - } + td { (templates::helpers::parents_breadcrumb(item.name, &parents, true)) } } tr { th { "Original Packaging" } td { - @if let Some(original_packaging) = original_packaging { - a - href={ "/item/" (original_packaging.id) } - { (ItemName::new(original_packaging.name.as_ref(), &item_classes.get(&original_packaging.class).unwrap().name)) } + @if let Some(original_packaging) = item.original_packaging { + (original_packaging) } @else { "-" } @@ -153,16 +201,14 @@ async fn get( } div { (PageActionGroup::generate_labels( - &children.iter().map(|i| i.id).collect::>(), + &children.iter().map(|ip| ip.id).collect::>(), )) } } ul { @for child in children { - li { - (ItemPreview::from_parts(child.id, child.name.as_ref(), &item_classes.get(&child.class).unwrap().name)) - } + li { (child) } } } } @@ -172,9 +218,7 @@ async fn get( ul { @for item in original_packaging_of { - li { - (ItemPreview::from_parts(item.id, item.name.as_ref(), &item_classes.get(&item.class).unwrap().name)) - } + li { (item) } } } } diff --git a/src/frontend/item_class/add.rs b/src/frontend/item_class/add.rs index 1e6860c..356e678 100644 --- a/src/frontend/item_class/add.rs +++ b/src/frontend/item_class/add.rs @@ -8,12 +8,10 @@ use actix_identity::Identity; use actix_web::{error, get, post, web, Responder}; use maud::html; use serde::Deserialize; -use sqlx::PgPool; +use sqlx::{query_scalar, PgPool}; use uuid::Uuid; use crate::frontend::templates::{self, datalist, forms, TemplateConfig}; -use crate::manage; -use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get).service(post); @@ -93,15 +91,18 @@ async fn post( _user: Identity, ) -> actix_web::Result { let data = data.into_inner(); - let item = manage::item_class::add( - &pool, - NewItemClass { - name: data.name, - parent: data.parent, - description: data.description, - }, + + let id = query_scalar!( + "INSERT INTO item_classes (name, parent, description) + VALUES ($1, $2, $3) + RETURNING id", + data.name, + data.parent, + data.description ) + .fetch_one(pool.as_ref()) .await .map_err(error::ErrorInternalServerError)?; - Ok(web::Redirect::to("/item-class/".to_owned() + &item.id.to_string()).see_other()) + + Ok(web::Redirect::to("/item-class/".to_owned() + &id.to_string()).see_other()) } diff --git a/src/frontend/item_class/delete.rs b/src/frontend/item_class/delete.rs index 4c5aa3e..cc4cb57 100644 --- a/src/frontend/item_class/delete.rs +++ b/src/frontend/item_class/delete.rs @@ -4,11 +4,9 @@ use actix_identity::Identity; use actix_web::{error, post, web, Responder}; -use sqlx::PgPool; +use sqlx::{query, PgPool}; use uuid::Uuid; -use crate::manage; - pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(post); } @@ -21,7 +19,8 @@ async fn post( ) -> actix_web::Result { let id = path.into_inner(); - manage::item_class::delete(&pool, id) + query!("DELETE FROM item_classes WHERE id = $1", id) + .execute(pool.as_ref()) .await .map_err(error::ErrorInternalServerError)?; diff --git a/src/frontend/item_class/edit.rs b/src/frontend/item_class/edit.rs index 4c0b7ac..ee1473d 100644 --- a/src/frontend/item_class/edit.rs +++ b/src/frontend/item_class/edit.rs @@ -7,17 +7,23 @@ use std::fmt::Display; use actix_identity::Identity; use actix_web::{error, get, post, web, Responder}; use maud::html; -use sqlx::PgPool; +use serde::Deserialize; +use sqlx::{query, query_as, PgPool}; use uuid::Uuid; use crate::frontend::templates::{self, datalist, forms, TemplateConfig}; -use crate::manage; -use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get).service(post); } +#[derive(Deserialize)] +struct ItemClassEditForm { + name: String, + parent: Option, + description: String, +} + #[get("/item-class/{id}/edit")] async fn get( pool: web::Data, @@ -26,22 +32,27 @@ async fn get( ) -> actix_web::Result { let id = path.into_inner(); - let item_class = manage::item_class::get(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; + let form = query_as!( + ItemClassEditForm, + "SELECT name, parent, description FROM item_classes WHERE id = $1", + id + ) + .fetch_one(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; let datalist_item_classes = datalist::item_classes(&pool) .await .map_err(error::ErrorInternalServerError)?; - let mut title = item_class.name.clone(); + let mut title = form.name.clone(); title.push_str(" – Item Details"); Ok(templates::base( TemplateConfig { path: &format!("/items-class/{}/add", id), title: Some(&title), - page_title: Some(Box::new(item_class.name.clone())), + page_title: Some(Box::new(form.name.clone())), datalists: vec![&datalist_item_classes], user: Some(user), ..Default::default() @@ -54,7 +65,7 @@ async fn get( title: "UUID", disabled: true, required: true, - value: Some(&item_class.id), + value: Some(&id), ..Default::default() }) (forms::InputGroup { @@ -62,7 +73,7 @@ async fn get( name: "name", title: "Name", required: true, - value: Some(&item_class.name), + value: Some(&form.name), ..Default::default() }) (forms::InputGroup { @@ -70,8 +81,8 @@ async fn get( name: "parent", title: "Parent", optional: true, - disabled: item_class.parent.is_none(), - value: item_class.parent.as_ref().map(|id| id as &dyn Display), + disabled: form.parent.is_none(), + value: form.parent.as_ref().map(|id| id as &dyn Display), datalist: Some(&datalist_item_classes), ..Default::default() }) @@ -79,7 +90,7 @@ async fn get( r#type: forms::InputType::Textarea, name: "description", title: "Description ", - value: Some(&item_class.description), + value: Some(&form.description), ..Default::default() }) @@ -93,14 +104,23 @@ async fn get( async fn post( pool: web::Data, path: web::Path, - data: web::Form, + data: web::Form, _user: Identity, ) -> actix_web::Result { let id = path.into_inner(); - let item_class = manage::item_class::update(&pool, id, data.into_inner()) - .await - .map_err(error::ErrorInternalServerError)?; + query!( + "UPDATE item_classes + SET name = $2, parent = $3, description = $4 + WHERE id = $1", + id, + data.name, + data.parent, + data.description + ) + .execute(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - Ok(web::Redirect::to("/item-class/".to_owned() + &item_class.id.to_string()).see_other()) + Ok(web::Redirect::to("/item-class/".to_owned() + &id.to_string()).see_other()) } diff --git a/src/frontend/item_class/list.rs b/src/frontend/item_class/list.rs index 9c9212c..498c397 100644 --- a/src/frontend/item_class/list.rs +++ b/src/frontend/item_class/list.rs @@ -5,29 +5,45 @@ use actix_identity::Identity; use actix_web::{error, get, web, Responder}; use maud::html; -use sqlx::PgPool; +use sqlx::{query, PgPool}; +use uuid::Uuid; use crate::frontend::templates::{ self, - helpers::{Colour, PageAction, PageActionGroup, PageActionMethod}, + helpers::{Colour, ItemClassPreview, PageAction, PageActionGroup, PageActionMethod}, TemplateConfig, }; -use crate::manage; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get); } +struct ItemClassListEntry { + id: Uuid, + name: String, + parent: Option, +} + #[get("/item-classes")] async fn get(pool: web::Data, user: Identity) -> actix_web::Result { - let item_classes_ids = sqlx::query_scalar!("SELECT id FROM item_classes ORDER BY created_at") - .fetch_all(pool.as_ref()) - .await - .map_err(error::ErrorInternalServerError)?; - - let item_classes = manage::item_class::get_all_as_map(&pool) - .await - .map_err(error::ErrorInternalServerError)?; + let item_classes = query!( + r#"SELECT class.id, class.name, class.parent, parent.name AS "parent_name?" + FROM item_classes AS "class" + LEFT JOIN item_classes AS "parent" + ON class.parent = parent.id + ORDER BY class.created_at + "# + ) + .map(|row| ItemClassListEntry { + id: row.id, + name: row.name, + parent: row + .parent + .map(|id| ItemClassPreview::new(id, row.parent_name.unwrap())), + }) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; Ok(templates::base( TemplateConfig { @@ -56,14 +72,12 @@ async fn get(pool: web::Data, user: Identity) -> actix_web::Result, +} + #[get("/item-class/{id}")] async fn get( pool: web::Data, @@ -27,26 +35,52 @@ async fn get( ) -> actix_web::Result { let id = path.into_inner(); - let item_class = manage::item_class::get(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; + let item_class = query!( + r#"SELECT + class.id, + class.name, + class.description, + class.parent, + parent.name AS "parent_name?" + FROM item_classes AS "class" + LEFT JOIN item_classes AS "parent" + ON class.parent = parent.id + WHERE class.id = $1"#, + id + ) + .map(|row| ItemClassDetails { + id: row.id, + name: row.name, + description: row.description, + parent: row + .parent + .map(|id| ItemClassPreview::new(id, row.parent_name.unwrap())), + }) + .fetch_one(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - // TODO: Once async closures are stable, use map_or on item_class.parent instead - let parent = match item_class.parent { - Some(id) => manage::item_class::get(&pool, id) - .await - .map(Some) - .map_err(error::ErrorInternalServerError)?, - None => None, - }; + let children = query_as!( + ItemClassPreview, + "SELECT id, name FROM item_classes WHERE parent = $1", + id + ) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; - let children = manage::item_class::children(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; - - let items = manage::item_class::items(&pool, id) - .await - .map_err(error::ErrorInternalServerError)?; + let items = query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN item_classes + ON items.class = item_classes.id + WHERE items.class = $1"#, + id + ) + .map(|row| ItemPreview::from_parts(row.id, row.name.as_ref(), &row.class_name)) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; let mut title = item_class.name.clone(); title.push_str(" – Item Details"); @@ -107,10 +141,10 @@ async fn get( th { "Name" } td { (item_class.name) } } - @if let Some(parent) = parent { + @if let Some(parent) = item_class.parent { tr { th { "Parent" } - td { a href={ "/item-class/" (parent.id) } { (parent.name) } } + td { (parent) } } } tr { @@ -124,9 +158,7 @@ async fn get( ul { @for child in children { - li { - a href={ "/item-class/" (child.id) } { (child.name) } - } + li { (child) } } } } @@ -145,9 +177,7 @@ async fn get( ul { @for item in items { - li { - (ItemPreview::new(item.id, ItemName::new(item.name.as_ref(), &item_class.name).terse())) - } + li { (item) } } } } diff --git a/src/frontend/jump.rs b/src/frontend/jump.rs index a803f08..2f8ab13 100644 --- a/src/frontend/jump.rs +++ b/src/frontend/jump.rs @@ -5,16 +5,19 @@ use actix_identity::Identity; use actix_web::{error, get, web, Responder}; use serde::Deserialize; -use sqlx::PgPool; +use sqlx::{query, query_scalar, PgPool}; use uuid::Uuid; -use crate::manage; -use crate::models::EntityType; - pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(get); } +#[derive(Deserialize)] +pub enum EntityType { + Item, + ItemClass, +} + #[derive(Deserialize)] struct JumpData { id: String, @@ -29,15 +32,29 @@ async fn get( let mut id = data.id.clone(); let entity_type = if let Ok(id) = Uuid::parse_str(&id) { - manage::query_entity_type(&pool, id) - .await - .map_err(error::ErrorInternalServerError)? + query!( + r#"SELECT type as "type!" + FROM (SELECT id, 'item' AS "type" FROM items + UNION ALL + SELECT id, 'item_class' AS "type" FROM item_classes) id_mapping + WHERE id = $1"#, + id + ) + .map(|row| match row.r#type.as_str() { + "item" => EntityType::Item, + "item_class" => EntityType::ItemClass, + _ => unreachable!("database returned impossible type"), + }) + .fetch_optional(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)? } else if let Ok(short_id) = id.parse::() { - if let Ok(item) = manage::item::get_by_short_id(&pool, short_id) + if let Ok(id_) = query_scalar!("SELECT id FROM items WHERE short_id = $1", short_id) + .fetch_one(pool.as_ref()) .await .map_err(error::ErrorInternalServerError) { - id = item.id.to_string(); + id = id_.to_string(); Some(EntityType::Item) } else { None diff --git a/src/frontend/labels.rs b/src/frontend/labels.rs index 7b02acc..e0d9796 100644 --- a/src/frontend/labels.rs +++ b/src/frontend/labels.rs @@ -7,12 +7,12 @@ use actix_web::{error, get, post, web, Responder}; use maud::html; use serde::Deserialize; use serde_variant::to_variant_name; -use sqlx::PgPool; +use sqlx::{query, PgPool}; use uuid::Uuid; -use super::templates::{self, datalist, helpers::ItemName, TemplateConfig}; +use super::templates::{self, datalist, TemplateConfig}; +use crate::frontend::templates::helpers::ItemPreview; use crate::label::{Label, LabelPreset}; -use crate::manage; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(generate_post) @@ -35,11 +35,9 @@ async fn generate(pool: &PgPool, params: GenerateParams) -> actix_web::Result, uuid::Error>>() .map_err(error::ErrorInternalServerError)?; - let items = manage::item::get_multiple(pool, &ids) + Label::for_items(pool, &ids, params.preset.clone().into()) .await - .map_err(error::ErrorInternalServerError)?; - - Ok(Label::for_items(&items, params.preset.clone().into())) + .map_err(error::ErrorInternalServerError) } #[post("/labels/generate")] @@ -62,13 +60,16 @@ async fn generate_get( #[get("/labels")] async fn form(pool: web::Data, user: Identity) -> actix_web::Result { - let items = manage::item::get_all(&pool) - .await - .map_err(error::ErrorInternalServerError)?; - - let item_classes = manage::item_class::get_all_as_map(&pool) - .await - .map_err(error::ErrorInternalServerError)?; + let items = query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN item_classes + ON items.class = item_classes.id"# + ) + .map(|row| ItemPreview::from_parts(row.id, row.name.as_ref(), &row.class_name)) + .fetch_all(pool.as_ref()) + .await + .map_err(error::ErrorInternalServerError)?; let datalist_items = datalist::items(&pool) .await @@ -120,8 +121,7 @@ async fn form(pool: web::Data, user: Identity) -> actix_web::Result Result { - let items = manage::item::get_all(pool).await?; - - let item_classes = manage::item_class::get_all_as_map(pool).await?; - Ok(Datalist { name: "items".to_string(), link_prefix: Some("/item/".to_string()), - options: items - .iter() - .map(|i| DatalistOption { - value: i.id.to_string(), - text: Box::new(ItemName::new( - i.name.as_ref(), - &item_classes.get(&i.class).unwrap().name, - )), - }) - .collect(), + options: query!( + r#"SELECT items.id, items.name, item_classes.name AS "class_name" + FROM items + JOIN item_classes + ON items.class = item_classes.id"# + ) + .fetch_all(pool) + .await? + .into_iter() + .map(|row| DatalistOption { + value: row.id.to_string(), + text: Box::new(ItemName::new(row.name.as_ref(), &row.class_name)), + }) + .collect(), }) } @@ -68,12 +67,13 @@ pub async fn item_classes(pool: &PgPool) -> Result { Ok(Datalist { name: "item-classes".to_string(), link_prefix: Some("/item-class/".to_string()), - options: manage::item_class::get_all(pool) + options: query!("SELECT id, name FROM item_classes") + .fetch_all(pool) .await? .into_iter() - .map(|ic| DatalistOption { - value: ic.id.to_string(), - text: Box::new(ic.name), + .map(|row| DatalistOption { + value: row.id.to_string(), + text: Box::new(row.name), }) .collect(), }) diff --git a/src/frontend/templates/helpers.rs b/src/frontend/templates/helpers.rs index 378bb3d..5462aeb 100644 --- a/src/frontend/templates/helpers.rs +++ b/src/frontend/templates/helpers.rs @@ -157,6 +157,25 @@ impl Render for ItemPreview { } } +pub struct ItemClassPreview { + pub id: Uuid, + pub name: String, +} + +impl ItemClassPreview { + pub fn new(id: Uuid, name: String) -> Self { + Self { id, name } + } +} + +impl Render for ItemClassPreview { + fn render(&self) -> Markup { + html! { + a href={ "/item-class/" (self.id) } { (self.name) } + } + } +} + pub enum PageActionMethod { Get, Post, diff --git a/src/label/mod.rs b/src/label/mod.rs index 941451c..3aae2d7 100644 --- a/src/label/mod.rs +++ b/src/label/mod.rs @@ -12,10 +12,10 @@ use barcode::{encode_code128, encode_data_matrix}; use pdf::{IndirectFontRef, PdfLayerReference}; use printpdf as pdf; use printpdf::{ImageTransform, Mm, PdfDocument, PdfDocumentReference, Pt, Px}; +use sqlx::{query_as, PgPool}; use thiserror::Error; use uuid::Uuid; -use crate::models::Item; pub use preset::LabelPreset; const FONT: Cursor<&[u8]> = Cursor::new(include_bytes!( @@ -247,16 +247,18 @@ impl Label { Ok(doc.ok_or(Error::NoPages)?.save_to_bytes()?) } - pub fn for_items(items: &[Item], config: LabelConfig) -> Self { - Label { - pages: items - .iter() - .map(|item| LabelPage { - id: Some(item.id), - short_id: Some(format!("{:06}", item.short_id)), - }) - .collect(), + pub async fn for_items(pool: &PgPool, ids: &[Uuid], config: LabelConfig) -> sqlx::Result { + Ok(Label { + pages: query_as!( + LabelPage, + r#"SELECT id AS "id?", to_char(short_id, '000000') AS "short_id?" + FROM items + WHERE id = ANY ($1)"#, + ids + ) + .fetch_all(pool) + .await?, config, - } + }) } } diff --git a/src/lib.rs b/src/lib.rs index 606f212..d9ec056 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,4 @@ pub mod frontend; pub mod label; -pub mod manage; pub mod middleware; -pub mod models; diff --git a/src/manage/item.rs b/src/manage/item.rs deleted file mode 100644 index 7bb35a2..0000000 --- a/src/manage/item.rs +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Simon Bruder -// -// SPDX-License-Identifier: AGPL-3.0-or-later - -use std::collections::HashMap; - -use sqlx::{query, query_as, query_scalar, PgPool}; -use uuid::Uuid; - -use crate::models::{Item, NewItem}; - -pub async fn add(pool: &PgPool, new_item: NewItem) -> Result { - query_as!( - Item, - "INSERT INTO items (name, parent, class, original_packaging, description) VALUES ($1, $2, $3, $4, $5) RETURNING *", - new_item.name, - new_item.parent, - new_item.class, - new_item.original_packaging, - new_item.description - ) - .fetch_one(pool) - .await -} - -pub async fn add_multiple( - pool: &PgPool, - new_item: NewItem, - quantity: usize, -) -> Result, sqlx::Error> { - query_as!( - Item, - r#"INSERT INTO items (name, parent, class, original_packaging, description) - SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[]) - RETURNING *"#, - &vec![new_item.name; quantity] as &[Option], - &vec![new_item.parent; quantity] as &[Option], - &vec![new_item.class; quantity], - &vec![new_item.original_packaging; quantity] as &[Option], - &vec![new_item.description; quantity] - ) - .fetch_all(pool) - .await -} - -pub async fn get(pool: &PgPool, id: Uuid) -> Result { - query_as!(Item, "SELECT * FROM items WHERE id = $1", id) - .fetch_one(pool) - .await -} - -pub async fn get_by_short_id(pool: &PgPool, short_id: i32) -> Result { - query_as!(Item, "SELECT * FROM items WHERE short_id = $1", short_id) - .fetch_one(pool) - .await -} - -pub async fn get_all(pool: &PgPool) -> Result, sqlx::Error> { - query_as!(Item, "SELECT * FROM items ORDER BY created_at") - .fetch_all(pool) - .await -} - -pub async fn get_multiple(pool: &PgPool, ids: &[Uuid]) -> Result, sqlx::Error> { - query_as!(Item, "SELECT * FROM items WHERE id = ANY ($1)", ids) - .fetch_all(pool) - .await -} - -pub async fn get_all_as_map(pool: &PgPool) -> Result, sqlx::Error> { - Ok(get_all(pool) - .await? - .into_iter() - .map(|i| (i.id, i)) - .collect()) -} - -pub async fn update(pool: &PgPool, id: Uuid, modified_item: NewItem) -> Result { - query_as!( - Item, - "UPDATE items SET name = $2, parent = $3, class = $4, original_packaging = $5, description = $6 WHERE id = $1 RETURNING *", - id, - modified_item.name, - modified_item.parent, - modified_item.class, - modified_item.original_packaging, - modified_item.description - ) - .fetch_one(pool) - .await -} - -pub async fn delete(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> { - let res = query!("DELETE FROM items WHERE id = $1", id) - .execute(pool) - .await?; - assert_eq!(res.rows_affected(), 1); - Ok(()) -} - -pub async fn get_parents(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - // force nullable is required for all columns in views - query_scalar!( - r#"SELECT unnest(parents) as "parents!" FROM item_tree WHERE id = $1"#, - id - ) - .fetch_all(pool) - .await -} - -pub async fn get_all_parents(pool: &PgPool) -> Result>, sqlx::Error> { - let mut parents = HashMap::new(); - for row in query!(r#"SELECT id as "id!", parents as "parents!" FROM item_tree"#) - .fetch_all(pool) - .await? - { - parents.insert(row.id, row.parents); - } - Ok(parents) -} - -pub async fn get_parents_details(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - query_as!( - Item, - "SELECT items.* - FROM items - INNER JOIN - unnest((SELECT parents FROM item_tree WHERE id = $1)) - WITH ORDINALITY AS parents(id, n) - ON items.id = parents.id - ORDER BY parents.n;", - id - ) - .fetch_all(pool) - .await -} - -pub async fn get_children(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - query_as!(Item, "SELECT * FROM items WHERE parent = $1", id) - .fetch_all(pool) - .await -} - -pub async fn original_packaging_contents( - pool: &PgPool, - id: Uuid, -) -> Result, sqlx::Error> { - query_as!( - Item, - "SELECT * FROM items WHERE original_packaging = $1", - id - ) - .fetch_all(pool) - .await -} diff --git a/src/manage/item_class.rs b/src/manage/item_class.rs deleted file mode 100644 index c8505c9..0000000 --- a/src/manage/item_class.rs +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Simon Bruder -// -// SPDX-License-Identifier: AGPL-3.0-or-later - -use std::collections::HashMap; - -use sqlx::{query, query_as, PgPool}; -use uuid::Uuid; - -use crate::models::{Item, ItemClass, NewItemClass}; - -pub async fn add(pool: &PgPool, new_item_class: NewItemClass) -> Result { - query_as!( - ItemClass, - "INSERT INTO item_classes (name, parent, description) VALUES ($1, $2, $3) RETURNING *", - new_item_class.name, - new_item_class.parent, - new_item_class.description - ) - .fetch_one(pool) - .await -} - -pub async fn get(pool: &PgPool, id: Uuid) -> Result { - query_as!(ItemClass, "SELECT * FROM item_classes WHERE id = $1", id) - .fetch_one(pool) - .await -} - -pub async fn get_all(pool: &PgPool) -> Result, sqlx::Error> { - query_as!(ItemClass, "SELECT * FROM item_classes ORDER BY created_at") - .fetch_all(pool) - .await -} - -pub async fn get_all_as_map(pool: &PgPool) -> Result, sqlx::Error> { - Ok(get_all(pool) - .await? - .into_iter() - .map(|ic| (ic.id, ic)) - .collect()) -} - -pub async fn update( - pool: &PgPool, - id: Uuid, - modified_item_class: NewItemClass, -) -> Result { - query_as!( - ItemClass, - "UPDATE item_classes SET name = $2, parent = $3, description = $4 WHERE id = $1 RETURNING *", - id, - modified_item_class.name, - modified_item_class.parent, - modified_item_class.description - ) - .fetch_one(pool) - .await -} - -pub async fn items(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - query_as!(Item, "SELECT * FROM items WHERE class = $1", id) - .fetch_all(pool) - .await -} - -pub async fn delete(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> { - let res = query!("DELETE FROM item_classes WHERE id = $1", id) - .execute(pool) - .await?; - assert_eq!(res.rows_affected(), 1); - Ok(()) -} - -pub async fn children(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - query_as!( - ItemClass, - "SELECT * FROM item_classes WHERE parent = $1", - id - ) - .fetch_all(pool) - .await -} diff --git a/src/manage/mod.rs b/src/manage/mod.rs deleted file mode 100644 index 35c0b31..0000000 --- a/src/manage/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Simon Bruder -// -// SPDX-License-Identifier: AGPL-3.0-or-later - -pub mod item; -pub mod item_class; - -use sqlx::{query, PgPool}; -use uuid::Uuid; - -use crate::models::EntityType; - -pub async fn query_entity_type(pool: &PgPool, id: Uuid) -> Result, sqlx::Error> { - Ok(query!( - r#"SELECT type as "type!" FROM - (SELECT id, 'item' AS "type" FROM items - UNION ALL - SELECT id, 'item_class' AS "type" FROM item_classes) id_mapping - WHERE id = $1"#, - id - ) - .fetch_optional(pool) - .await? - .map(|row| match row.r#type.as_str() { - "item" => EntityType::Item, - "item_class" => EntityType::ItemClass, - _ => unreachable!("database returned impossible type"), - })) -} diff --git a/src/models.rs b/src/models.rs deleted file mode 100644 index 9a0272c..0000000 --- a/src/models.rs +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Simon Bruder -// -// SPDX-License-Identifier: AGPL-3.0-or-later - -use serde::{Deserialize, Serialize}; -use time::OffsetDateTime; -use uuid::Uuid; - -#[derive(Deserialize)] -pub enum EntityType { - Item, - ItemClass, -} - -#[derive(Clone, Debug, Serialize, sqlx::FromRow)] -pub struct Item { - pub id: Uuid, - pub name: Option, - pub parent: Option, - pub class: Uuid, - #[serde(with = "time::serde::iso8601")] - pub created_at: OffsetDateTime, - pub short_id: i32, - pub original_packaging: Option, - pub description: String, -} - -#[derive(Debug, Deserialize)] -pub struct NewItem { - #[serde(default)] - pub name: Option, - #[serde(default)] - pub parent: Option, - pub class: Uuid, - pub original_packaging: Option, - pub description: String, -} - -#[derive(Clone, Debug, Serialize)] -pub struct ItemClass { - pub id: Uuid, - pub name: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub parent: Option, - #[serde(with = "time::serde::iso8601")] - pub created_at: OffsetDateTime, - pub description: String, -} - -#[derive(Debug, Deserialize)] -pub struct NewItemClass { - pub name: String, - #[serde(default)] - pub parent: Option, - pub description: String, -}