mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: add extra metadata to folders/resources/types + path prefix filtering for all + improve groups UI (#3936)
* feat: add extra metadata to folders/schedules/resources/types * feat: add path prefix filter to api * chore: updatee ref * feat: improve groups UI * fix: nit * fix: nit
This commit is contained in:
+12
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -34,6 +44,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+17
-5
@@ -35,31 +35,41 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_expired",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 9,
|
||||
"name": "is_refreshed",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 10,
|
||||
"name": "refresh_error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 11,
|
||||
"name": "is_linked",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 12,
|
||||
"name": "is_oauth?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 13,
|
||||
"name": "account",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
@@ -77,6 +87,8 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,11 @@
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "489a62b5943a7a21ce487aa7b72a63dfc6300dd93bc29f5ec4cb1bfc471ad0bf"
|
||||
"hash": "42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4"
|
||||
}
|
||||
+13
-1
@@ -32,6 +32,16 @@
|
||||
"ordinal": 5,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -45,7 +55,9 @@
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "45e4d13f5806122faecdb1d9ab18159555b652869a036b006f4a151e999b17b7"
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "584cb984ea6528baac48c5c437ad2ee3bef92e3fada73dcf519147964c0f4f4a"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5eb1e072a6cc6ace25a5806b5adfdc0ae5c89c556daf491f39e629951a912c6c"
|
||||
}
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,12 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e33d0bec7e08c0bb4e6f26c93a636707714699e8984b9f537085e773849c8b43"
|
||||
"hash": "5f3f1f1ca72b0392f227d22adef9ef795049e60a823e8ce754a3f9d91ef1b6bb"
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"name": "bool",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6e6d781fe63602541f7cb4806d5ddde0792e1861ea8761b4875677c3d0594a86"
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "726f77c2c1ef63eece7a1176bcbb916987fc29b04b889544a064dc7cd5eab36f"
|
||||
}
|
||||
+12
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -33,6 +43,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7ded28aae42230da6e3aa6fa952268e8e15cc8b16770c20ec6be2a9e82ab93c9"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "98c8e2c58b38beedcb474251c2554a2f1b768ec7ed1a419313f4842f26425e5d"
|
||||
}
|
||||
+21
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,21 @@
|
||||
"ordinal": 4,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "summary",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -41,8 +56,11 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8543f029d9784234e4c6a6dcd7b03e62d544b98be261334ee210594e0bb839f2"
|
||||
"hash": "9c6d44ffae63b4050ef3a66cb05d5a4ee50dea4b30093df2e0ceb684201ca4b0"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms)\n VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING",
|
||||
"query": "INSERT INTO folder\n (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Jsonb"
|
||||
"Jsonb",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f2aee3ae39c90e40dd1835befc339e5381cc9104933cf8e90d840a9bf638ff52"
|
||||
"hash": "ad3b0176e3d41a0c1d79a8eb59da2a9cc4fee3473b3f6ea69f397d91e9356ba1"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b7ac3383a058ab3bbd7280300cebc5f81eeb9e96622ad2557f810a8b91641b89"
|
||||
}
|
||||
+12
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -33,6 +43,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
|
||||
"query": "UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -13,5 +13,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8c0131a9cc61f2daa258d49767242bcaab6bb34a977ff7fb0c18aa9202d11f47"
|
||||
"hash": "be10033b37dc9cbc38cc56afa8ef93cd7dbe267525d78cbfea87bb7c7cbf8ec4"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE folder SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c2a6e8f38d2293c45ede26b89a976f1a32f5bc2323a28f640cf3301a299f1cf4"
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -8,10 +8,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad9ccde8d831461f1f312d530867714577a825cd40d6655ed61b0b343b5d4482"
|
||||
"hash": "c4ee16065fa021cf6443c2f29f1ca986b9730eae9df26dd33c3ea21e35df62c4"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e"
|
||||
}
|
||||
+21
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"query": "SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,21 @@
|
||||
"ordinal": 4,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "summary",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -40,8 +55,11 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3911bf3bbc82d87366a5297496fd2350a252bcd69f1c7c972bf566ee7eb28b0a"
|
||||
"hash": "cb20f04352364f112ec564617722354577dc5f77169fd4573317454e47361d65"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cccf9e216d84dfbb801b92d697496f44d93b344190a26c8393867b3a268b9aba"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource_type SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d1b508e1d8496e5e7aee9db555f9740128d87fe5183f9b7b72f550a5502e1cbb"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description)\n VALUES ($1, $2, $3, $4)",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -8,10 +8,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text"
|
||||
"Text",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5061c0d054bf4f028e7fe51a8f9389024c6ae4492755cadac0f7167e5300bda0"
|
||||
"hash": "d249343a1d9fce2f7ca9e6e447eb32d7d11b61acdab80dda24db4a9ba3e1f7e6"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d956b7525f83e6d03beadc4bb3ee2798f53d990b01b17bdbc044719d4908e3f4"
|
||||
}
|
||||
+12
@@ -22,6 +22,16 @@
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -31,6 +41,8 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f581f5ad5c386cef1037e3e8dcb759164e21a72ddc859304421ccb96233fa277"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"query": "UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -12,5 +12,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8876fa929ffb175cd976a2bca1195704aa9fe7215013ae29e49ef15cb201ba57"
|
||||
"hash": "fd790067c02d384aead5ff8f94efb37cc20d2f7a3749d21a1de5642e888b5f42"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
6aa8832322866e9f6ff90979db9b2b8f4bf6d4ce
|
||||
9ef2d9c4aebfee15935634274a2c7c685331a865
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add up migration script here
|
||||
alter table resource add column edited_at timestamptz, add column created_by varchar(50);
|
||||
alter table resource_type add column edited_at timestamptz, add column created_by varchar(50);
|
||||
alter table folder add column summary text, add column edited_at timestamptz, add column created_by varchar(50);
|
||||
@@ -2373,6 +2373,10 @@ paths:
|
||||
- variable
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: variable list
|
||||
@@ -2854,6 +2858,10 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: resource list
|
||||
@@ -6440,6 +6448,10 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: schedule list
|
||||
@@ -6917,6 +6929,8 @@ paths:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
owners:
|
||||
type: array
|
||||
items:
|
||||
@@ -6951,6 +6965,8 @@ paths:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
summary:
|
||||
type: string
|
||||
owners:
|
||||
type: array
|
||||
items:
|
||||
@@ -9842,6 +9858,11 @@ components:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- resource_type
|
||||
@@ -9875,6 +9896,11 @@ components:
|
||||
type: boolean
|
||||
account:
|
||||
type: number
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- path
|
||||
- resource_type
|
||||
@@ -9892,6 +9918,11 @@ components:
|
||||
schema: {}
|
||||
description:
|
||||
type: string
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- name
|
||||
|
||||
@@ -10126,6 +10157,13 @@ components:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
summary:
|
||||
type: string
|
||||
created_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- name
|
||||
- owners
|
||||
|
||||
@@ -279,6 +279,14 @@ async fn list_apps(
|
||||
sqlb.and_where_is_not_null("favorite.path");
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("app.path", path_start);
|
||||
}
|
||||
|
||||
if let Some(path_exact) = &lq.path_exact {
|
||||
sqlb.and_where_eq("app.path", "?".bind(path_exact));
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableApp>(&sql)
|
||||
|
||||
@@ -143,10 +143,10 @@ async fn list_flows(
|
||||
sqlb.and_where_eq("archived", lq.show_archived.unwrap_or(false));
|
||||
|
||||
if let Some(ps) = &lq.path_start {
|
||||
sqlb.and_where_like_left("path", "?".bind(ps));
|
||||
sqlb.and_where_like_left("o.path", ps);
|
||||
}
|
||||
if let Some(p) = &lq.path_exact {
|
||||
sqlb.and_where_eq("path", "?".bind(p));
|
||||
sqlb.and_where_eq("o.path", "?".bind(p));
|
||||
}
|
||||
if let Some(cb) = &lq.edited_by {
|
||||
sqlb.and_where_eq("edited_by", "?".bind(cb));
|
||||
|
||||
@@ -56,11 +56,15 @@ pub struct Folder {
|
||||
pub display_name: String,
|
||||
pub owners: Vec<String>,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub summary: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NewFolder {
|
||||
pub name: String,
|
||||
pub summary: Option<String>,
|
||||
pub display_name: Option<String>,
|
||||
pub owners: Option<Vec<String>>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
@@ -68,6 +72,7 @@ pub struct NewFolder {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdateFolder {
|
||||
pub summary: Option<String>,
|
||||
pub display_name: Option<String>,
|
||||
pub owners: Option<Vec<String>>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
@@ -90,7 +95,7 @@ async fn list_folders(
|
||||
|
||||
let rows = sqlx::query_as!(
|
||||
Folder,
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3",
|
||||
w_id,
|
||||
per_page as i64,
|
||||
offset as i64
|
||||
@@ -199,12 +204,14 @@ async fn create_folder(
|
||||
|
||||
sqlx::query_as!(
|
||||
Folder,
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, $2, $3, $4, $5)",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, now())",
|
||||
w_id,
|
||||
ng.name,
|
||||
ng.display_name.unwrap_or(ng.name.clone()),
|
||||
&owners,
|
||||
extra_perms,
|
||||
ng.summary,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -283,6 +290,12 @@ async fn update_folder(
|
||||
sqlb.set("display_name", "?".bind(&display_name));
|
||||
}
|
||||
|
||||
if let Some(summary) = ng.summary {
|
||||
sqlb.set("summary", "?".bind(&summary));
|
||||
}
|
||||
|
||||
sqlb.set("edited_at", "now()");
|
||||
|
||||
if !authed.is_admin {
|
||||
let prefixed_username = format!("u/{}", authed.username);
|
||||
if ng.owners.as_ref().is_some_and(|x| {
|
||||
@@ -380,7 +393,7 @@ pub async fn get_folderopt<'c>(
|
||||
) -> Result<Option<Folder>> {
|
||||
let folderopt = sqlx::query_as!(
|
||||
Folder,
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
"SELECT workspace_id, name, display_name, owners, extra_perms, summary, created_by, edited_at FROM folder WHERE name = $1 AND workspace_id = $2",
|
||||
name,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -102,6 +102,14 @@ async fn list_apps(
|
||||
sqlb.and_where_is_not_null("favorite.path");
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("app.path", path_start);
|
||||
}
|
||||
|
||||
if let Some(path_exact) = &lq.path_exact {
|
||||
sqlb.and_where_eq("app.path", "?".bind(path_exact));
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableApp>(&sql)
|
||||
|
||||
@@ -69,6 +69,8 @@ pub struct ResourceType {
|
||||
pub name: String,
|
||||
pub schema: Option<serde_json::Value>,
|
||||
pub description: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -92,6 +94,8 @@ pub struct Resource {
|
||||
pub description: Option<String>,
|
||||
pub resource_type: String,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize, Deserialize)]
|
||||
@@ -102,6 +106,8 @@ pub struct ListableResource {
|
||||
pub description: Option<String>,
|
||||
pub resource_type: String,
|
||||
pub extra_perms: serde_json::Value,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub is_linked: Option<bool>,
|
||||
pub is_refreshed: Option<bool>,
|
||||
pub is_oauth: Option<bool>,
|
||||
@@ -128,6 +134,7 @@ struct EditResource {
|
||||
pub struct ListResourceQuery {
|
||||
resource_type: Option<String>,
|
||||
resource_type_exclude: Option<String>,
|
||||
path_start: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, FromRow)]
|
||||
@@ -209,6 +216,8 @@ async fn list_resources(
|
||||
"variable.is_oauth",
|
||||
"variable.account",
|
||||
"account.refresh_error",
|
||||
"resource.created_by",
|
||||
"resource.edited_at",
|
||||
])
|
||||
.left()
|
||||
.join("variable")
|
||||
@@ -241,6 +250,10 @@ async fn list_resources(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path_start) = &lq.path_start {
|
||||
sqlb.and_where_like_left("resource.path", path_start);
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let rows = sqlx::query_as::<_, ListableResource>(&sql)
|
||||
@@ -642,14 +655,15 @@ async fn create_resource(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource
|
||||
(workspace_id, path, value, description, resource_type)
|
||||
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3, description = $4, resource_type = $5",
|
||||
(workspace_id, path, value, description, resource_type, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
|
||||
w_id,
|
||||
resource.path,
|
||||
raw_json as sqlx::types::Json<&RawValue>,
|
||||
resource.description,
|
||||
resource.resource_type,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -772,6 +786,8 @@ async fn update_resource(
|
||||
sqlb.set_str("description", ndesc);
|
||||
}
|
||||
|
||||
sqlb.set_str("edited_at", "now()");
|
||||
|
||||
sqlb.returning("path");
|
||||
let authed = maybe_refresh_folders(path, &w_id, authed, &db).await;
|
||||
|
||||
@@ -853,7 +869,7 @@ async fn update_resource_value(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE resource SET value = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"UPDATE resource SET value = $1, edited_at = now() WHERE path = $2 AND workspace_id = $3",
|
||||
nv.value,
|
||||
path,
|
||||
w_id
|
||||
@@ -979,12 +995,13 @@ async fn create_resource_type(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource_type
|
||||
(workspace_id, name, schema, description)
|
||||
VALUES ($1, $2, $3, $4)",
|
||||
(workspace_id, name, schema, description, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, now())",
|
||||
w_id,
|
||||
resource_type.name,
|
||||
resource_type.schema,
|
||||
resource_type.description,
|
||||
authed.username
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -1120,6 +1137,7 @@ async fn update_resource_type(
|
||||
if let Some(ndesc) = ns.description {
|
||||
sqlb.set_str("description", ndesc);
|
||||
}
|
||||
sqlb.set_str("edited_at", "now()");
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
|
||||
@@ -313,6 +313,7 @@ pub struct ListScheduleQuery {
|
||||
pub path: Option<String>,
|
||||
pub is_flow: Option<bool>,
|
||||
pub args: Option<String>,
|
||||
pub path_start: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_schedule(
|
||||
@@ -339,6 +340,9 @@ async fn list_schedule(
|
||||
if let Some(args) = &lsq.args {
|
||||
sqlb.and_where("args @> ?".bind(&args.replace("'", "''")));
|
||||
}
|
||||
if let Some(path_start) = &lsq.path_start {
|
||||
sqlb.and_where_like_left("path", path_start);
|
||||
}
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let rows = sqlx::query_as::<_, Schedule>(&sql)
|
||||
.fetch_all(&mut *tx)
|
||||
|
||||
@@ -242,10 +242,10 @@ async fn list_scripts(
|
||||
sqlb.and_where_eq("archived", false);
|
||||
}
|
||||
if let Some(ps) = &lq.path_start {
|
||||
sqlb.and_where_like_left("path", "?".bind(ps));
|
||||
sqlb.and_where_like_left("o.path", ps);
|
||||
}
|
||||
if let Some(p) = &lq.path_exact {
|
||||
sqlb.and_where_eq("path", "?".bind(p));
|
||||
sqlb.and_where_eq("o.path", "?".bind(p));
|
||||
}
|
||||
if let Some(cb) = &lq.created_by {
|
||||
sqlb.and_where_eq("created_by", "?".bind(cb));
|
||||
|
||||
@@ -81,10 +81,16 @@ async fn list_contextual_variables(
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ListVariableQuery {
|
||||
path_start: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_variables(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(lq): Query<ListVariableQuery>,
|
||||
) -> JsonResult<Vec<ListableVariable>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
@@ -97,10 +103,13 @@ async fn list_variables(
|
||||
from variable
|
||||
LEFT JOIN account ON variable.account = account.id AND account.workspace_id = $1
|
||||
LEFT JOIN resource ON resource.path = variable.path AND resource.workspace_id = $1
|
||||
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%' ORDER BY path",
|
||||
WHERE variable.workspace_id = $1 AND variable.path NOT LIKE 'u/' || $2 || '/secret_arg/%'
|
||||
AND variable.path LIKE $3 || '%'
|
||||
ORDER BY path",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.bind(&authed.username)
|
||||
.bind(&lq.path_start.unwrap_or_default())
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -523,7 +532,7 @@ async fn update_variable(
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE resource SET path = $1, value = $2 WHERE path = $3 AND workspace_id = $4",
|
||||
"UPDATE resource SET path = $1, value = $2, edited_at = now() WHERE path = $3 AND workspace_id = $4",
|
||||
npath,
|
||||
v,
|
||||
path,
|
||||
|
||||
@@ -1605,29 +1605,33 @@ async fn create_workspace(
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_themes', 'App Themes', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_custom', 'App Custom Components', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by, edited_at) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO resource (workspace_id, path, value, description, resource_type, created_by, edited_at) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme', $2, now()) ON CONFLICT DO NOTHING",
|
||||
nw.id,
|
||||
username,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -11,4 +11,6 @@ use serde::Deserialize;
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListAppQuery {
|
||||
pub starred_only: Option<bool>,
|
||||
pub path_exact: Option<String>,
|
||||
pub path_start: Option<String>,
|
||||
}
|
||||
|
||||
@@ -1568,13 +1568,14 @@ pub async fn save_in_cache(
|
||||
|
||||
if let Err(e) = sqlx::query!(
|
||||
"INSERT INTO resource
|
||||
(workspace_id, path, value, resource_type)
|
||||
VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3",
|
||||
(workspace_id, path, value, resource_type, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)
|
||||
DO UPDATE SET value = $3, edited_at = now()",
|
||||
job.workspace_id,
|
||||
cached_path,
|
||||
raw_json as sqlx::types::Json<CachedResource>,
|
||||
"cache"
|
||||
"cache",
|
||||
job.created_by
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
|
||||
@@ -94,18 +94,6 @@
|
||||
.Template-editor span.mtk20 {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(155, 155, 155, 0.5);
|
||||
border: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import Section from './Section.svelte'
|
||||
import { Eye, Plus } from 'lucide-svelte'
|
||||
import Label from './Label.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let name: string
|
||||
let can_write = false
|
||||
@@ -67,6 +70,7 @@
|
||||
async function loadFolder(): Promise<void> {
|
||||
try {
|
||||
folder = await FolderService.getFolder({ workspace: $workspaceStore!, name })
|
||||
summary = folder.summary ?? ''
|
||||
can_write =
|
||||
$userStore != undefined &&
|
||||
(folder?.owners.includes('u/' + $userStore.username) ||
|
||||
@@ -108,6 +112,7 @@
|
||||
let ownerKind: 'user' | 'group' = 'user'
|
||||
let groupCreated: string | undefined = undefined
|
||||
let newGroupName: string = ''
|
||||
let summary: string = ''
|
||||
|
||||
async function addGroup() {
|
||||
await GroupService.createGroup({
|
||||
@@ -119,6 +124,19 @@
|
||||
loadGroups()
|
||||
ownerItem = newGroupName
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function updateFolder() {
|
||||
await FolderService.updateFolder({
|
||||
workspace: $workspaceStore ?? '',
|
||||
name,
|
||||
requestBody: { summary }
|
||||
})
|
||||
sendUserToast('Folder summary updated')
|
||||
dispatch('update')
|
||||
loadFolder()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={newGroup}>
|
||||
@@ -148,6 +166,15 @@
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Section label="Metadata" class="mb-4">
|
||||
<Label label="Summary">
|
||||
<div class="flex flex-row gap-2">
|
||||
<input placeholder="Short summary to be displayed when listed" bind:value={summary} />
|
||||
<Button size="sm" on:click={updateFolder} disabled={!can_write}>Save</Button>
|
||||
</div>
|
||||
</Label>
|
||||
</Section>
|
||||
|
||||
<Section label={`Permissions (${perms?.length ?? 0})`}>
|
||||
<div class="flex flex-col gap-6">
|
||||
{#if can_write}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import AutoComplete from 'simple-svelte-autocomplete'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import autosize from '$lib/autosize'
|
||||
import { Button } from './common'
|
||||
import Skeleton from './common/skeleton/Skeleton.svelte'
|
||||
import TableCustom from './TableCustom.svelte'
|
||||
@@ -17,6 +16,8 @@
|
||||
import { canWrite } from '$lib/utils'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import Section from './Section.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let name: string
|
||||
let can_write = false
|
||||
@@ -27,6 +28,7 @@
|
||||
let members: { member_name: string; role: Role }[] | undefined = undefined
|
||||
let usernames: string[] | undefined = []
|
||||
let username: string = ''
|
||||
let summary = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -78,9 +80,11 @@
|
||||
role: getRole(x)
|
||||
}
|
||||
})
|
||||
summary = group.summary ?? ''
|
||||
} catch (e) {
|
||||
can_write = false
|
||||
members = []
|
||||
summary = ''
|
||||
group = {
|
||||
name
|
||||
}
|
||||
@@ -101,37 +105,28 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
<h1>{name}</h1>
|
||||
{#if group}
|
||||
<div class="flex flex-col gap-1">
|
||||
<textarea
|
||||
disabled={!can_write}
|
||||
rows="2"
|
||||
use:autosize
|
||||
bind:value={group.summary}
|
||||
placeholder="Summary of the group"
|
||||
/>
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
disabled={!can_write}
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
await GroupService.updateGroup({
|
||||
workspace: $workspaceStore ?? '',
|
||||
name,
|
||||
requestBody: { summary: group?.summary }
|
||||
})
|
||||
dispatch('update')
|
||||
sendUserToast('New summary saved')
|
||||
}}>Save Summary</Button
|
||||
>
|
||||
</div>
|
||||
<Section label="Metadata" class="mb-4">
|
||||
<Label label="Summary">
|
||||
<div class="flex flex-row gap-2">
|
||||
<input placeholder="Short summary to be displayed when listed" bind:value={summary} />
|
||||
<Button
|
||||
size="sm"
|
||||
on:click={async () => {
|
||||
await GroupService.updateGroup({
|
||||
workspace: $workspaceStore ?? '',
|
||||
name,
|
||||
requestBody: { summary }
|
||||
})
|
||||
dispatch('update')
|
||||
sendUserToast('Group summary updated')
|
||||
loadGroup()
|
||||
}}>Save</Button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<Skeleton layout={[[4]]} />
|
||||
{/if}
|
||||
<h2>Members ({members?.length ?? 0})</h2>
|
||||
</Label>
|
||||
</Section>
|
||||
|
||||
<Section label={`Members (${members?.length ?? 0})`}>
|
||||
{#if can_write}
|
||||
<div class="flex items-start">
|
||||
<AutoComplete required noInputStyles items={usernames} bind:selectedItem={username} />
|
||||
@@ -221,13 +216,6 @@
|
||||
label="Member"
|
||||
tooltip="A Member of a group can see everything the group can see, write to everything the group can write, and generally act on behalf of the group"
|
||||
/>
|
||||
|
||||
<!-- <ToggleButton position="center" value="manager" size="xs"
|
||||
>Manager <Tooltip
|
||||
>A manager of a group can manage the group, adding and removing users and
|
||||
change their roles. Being a manager does not make you a member.</Tooltip
|
||||
></ToggleButton
|
||||
> -->
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="admin"
|
||||
@@ -235,6 +223,15 @@
|
||||
label="Admin"
|
||||
tooltip="An admin of a group is a member of a group that can also add and remove members to the group, or make them admin."
|
||||
/>
|
||||
{#if role === 'manager'}
|
||||
<ToggleButton
|
||||
value="manager"
|
||||
size="xs"
|
||||
label="Manager"
|
||||
tooltip="A manager of a group can manage the group, adding and removing users and
|
||||
change their roles. Being a manager does not make you a member"
|
||||
/>
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -281,58 +278,6 @@
|
||||
</tbody>
|
||||
</TableCustom>
|
||||
{/if}
|
||||
|
||||
<!-- <h2 class="mt-10"
|
||||
>Groups managing this group <Tooltip>Any member of those groups can manage this group</Tooltip
|
||||
></h2
|
||||
>
|
||||
{#if can_write}
|
||||
<div class="flex items-start">
|
||||
<AutoComplete items={groups} bind:selectedItem={new_managing_group} />
|
||||
<Button
|
||||
variant="contained"
|
||||
color="blue"
|
||||
size="sm"
|
||||
btnClasses="!ml-4"
|
||||
on:click={addToManagingGroup}
|
||||
>
|
||||
Add group managing this group
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if managing_groups.length == 0}
|
||||
<p class="text-tertiary text-sm">No group is managing this group</p>
|
||||
{:else}
|
||||
<TableCustom>
|
||||
<tr slot="header-row">
|
||||
<th>group</th>
|
||||
<th />
|
||||
</tr>
|
||||
<tbody slot="body">
|
||||
{#each managing_groups as managing_group}<tr>
|
||||
<td>{managing_group.split('/')[1]}</td>
|
||||
<td>
|
||||
{#if can_write}
|
||||
<button
|
||||
class="ml-2 text-red-500"
|
||||
on:click={async () => {
|
||||
await GranularAclService.removeGranularAcls({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: name,
|
||||
kind: 'group_',
|
||||
requestBody: {
|
||||
owner: managing_group
|
||||
}
|
||||
})
|
||||
loadGroup()
|
||||
}}>remove</button
|
||||
>
|
||||
{/if}</td
|
||||
>
|
||||
</tr>{/each}
|
||||
</tbody>
|
||||
</TableCustom>
|
||||
{/if} -->
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#each new Array(6) as _}
|
||||
@@ -340,4 +285,4 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{#if members}
|
||||
<Popover
|
||||
><div class="inline-flex gap-1 items-end"
|
||||
><span class="text-tertiary text-xs mb-0.5">({members.length})</span>
|
||||
><span class="text-tertiary text-xs">({members.length})</span>
|
||||
<div class="max-w-xs truncate"
|
||||
><span class="text-tertiary text-xs">{members?.join(', ')}</span></div
|
||||
></div
|
||||
|
||||
@@ -279,8 +279,8 @@
|
||||
}}
|
||||
>
|
||||
{#if !folderCreated}
|
||||
<div class="flex flex-row">
|
||||
<input class="mr-2" placeholder="New folder name" bind:value={newFolderName} />
|
||||
<div class="flex flex-col gap-2">
|
||||
<input placeholder="New folder name" bind:value={newFolderName} />
|
||||
<Button size="md" startIcon={{ icon: Plus }} disabled={!newFolderName} on:click={addFolder}>
|
||||
New folder
|
||||
</Button>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
import { List, Loader2, Save } from 'lucide-svelte'
|
||||
import FlowRetries from './flows/content/FlowRetries.svelte'
|
||||
import WorkerTagPicker from './WorkerTagPicker.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
let optionTabSelected: 'error_handler' | 'recovery_handler' | 'retries' = 'error_handler'
|
||||
|
||||
@@ -441,16 +442,16 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex flex-col gap-12">
|
||||
<div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<h2 class="text-base font-semibold">Metadata</h2>
|
||||
<div class="w-full py-2">
|
||||
<h2 class="text-base font-semibold mb-2">Metadata</h2>
|
||||
<Label label="Summary">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Schedule summary"
|
||||
class="text-sm w-full font-semibold"
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
bind:value={summary}
|
||||
on:keyup={() => {
|
||||
if (!edit && summary?.length > 0 && !dirtyPath) {
|
||||
@@ -464,40 +465,42 @@
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
{#if !edit}
|
||||
<Path
|
||||
bind:dirty={dirtyPath}
|
||||
bind:this={pathC}
|
||||
checkInitialPathExistence
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="schedule"
|
||||
kind="schedule"
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex justify-start w-full">
|
||||
<Badge
|
||||
color="gray"
|
||||
class="center-center !bg-surface-secondary !text-tertiary !h-[24px] rounded-r-none border"
|
||||
>
|
||||
Schedule path (not editable)
|
||||
</Badge>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={path}
|
||||
size={path?.length || 50}
|
||||
class="font-mono !text-xs grow shrink overflow-x-auto !h-[24px] !py-0 !border-l-0 !rounded-l-none"
|
||||
on:focus={({ currentTarget }) => {
|
||||
currentTarget.select()
|
||||
}}
|
||||
<Label label="Path">
|
||||
{#if !edit}
|
||||
<Path
|
||||
bind:dirty={dirtyPath}
|
||||
bind:this={pathC}
|
||||
checkInitialPathExistence
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
namePlaceholder="schedule"
|
||||
kind="schedule"
|
||||
/>
|
||||
<!-- <span class="font-mono text-sm break-all">{path}</span> -->
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex justify-start w-full">
|
||||
<Badge
|
||||
color="gray"
|
||||
class="center-center !bg-surface-secondary !text-tertiary !h-[24px] rounded-r-none border"
|
||||
>
|
||||
Schedule path (not editable)
|
||||
</Badge>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={path}
|
||||
size={path?.length || 50}
|
||||
class="font-mono !text-xs grow shrink overflow-x-auto !h-[24px] !py-0 !border-l-0 !rounded-l-none"
|
||||
on:focus={({ currentTarget }) => {
|
||||
currentTarget.select()
|
||||
}}
|
||||
/>
|
||||
<!-- <span class="font-mono text-sm break-all">{path}</span> -->
|
||||
</div>
|
||||
{/if}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<Section label="Schedule">
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { Calendar } from 'lucide-svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { ScriptSchedule } from '$lib/scripts'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let initialPath: string
|
||||
export let schema: Record<string, any> | undefined
|
||||
@@ -37,15 +38,17 @@
|
||||
$: initialPath && loadSchedules()
|
||||
</script>
|
||||
|
||||
<div class="w-full py-2">
|
||||
<div class="w-full flex flex-col gap-4 mb-4">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Schedule summary"
|
||||
class="text-sm w-full font-semibold mb-4"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
<Label label="Summary">
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<CronInput bind:schedule={$schedule.cron} bind:timezone={$schedule.timezone} />
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { ScheduleService, type Schedule } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Calendar } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
|
||||
const { schedule, flowStore, initialPath } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
let schedules: Schedule[] | undefined = undefined
|
||||
@@ -33,15 +34,17 @@
|
||||
$: initialPath && loadSchedules()
|
||||
</script>
|
||||
|
||||
<div class="w-full py-2">
|
||||
<div class="w-full flex flex-col gap-4 mb-4">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Schedule summary"
|
||||
class="text-sm w-full font-semibold mb-4"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
<Label label="Summary">
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<CronInput bind:schedule={$schedule.cron} bind:timezone={$schedule.timezone} />
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
</tr>
|
||||
{/if}
|
||||
|
||||
{#each folders as { name, extra_perms, owners, canWrite } (name)}
|
||||
{#each folders as { name, extra_perms, owners, canWrite, summary } (name)}
|
||||
<Row
|
||||
hoverable
|
||||
on:click={() => {
|
||||
@@ -160,6 +160,10 @@
|
||||
>
|
||||
<Cell first>
|
||||
<span class="text-blue-500">{name}</span>
|
||||
{#if summary}
|
||||
<br />
|
||||
<span class="text-gray-500">{summary}</span>
|
||||
{/if}
|
||||
</Cell>
|
||||
<FolderUsageInfo {name} tabular />
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite } from '$lib/utils'
|
||||
import { Pen, Plus, Trash } from 'lucide-svelte'
|
||||
import DataTable from '$lib/components/table/DataTable.svelte'
|
||||
import Head from '$lib/components/table/Head.svelte'
|
||||
import Cell from '$lib/components/table/Cell.svelte'
|
||||
import Row from '$lib/components/table/Row.svelte'
|
||||
|
||||
type GroupW = Group & { canWrite: boolean }
|
||||
|
||||
@@ -110,15 +114,15 @@
|
||||
</PageHeader>
|
||||
|
||||
<div class="relative mb-20 pt-8">
|
||||
<TableCustom>
|
||||
<tr slot="header-row">
|
||||
<th class="!px-0" />
|
||||
<th>Name</th>
|
||||
<th>Summary</th>
|
||||
<th>Members</th>
|
||||
<th />
|
||||
</tr>
|
||||
<tbody slot="body">
|
||||
<DataTable>
|
||||
<Head>
|
||||
<tr>
|
||||
<Cell head first>Name</Cell>
|
||||
<Cell head>Members</Cell>
|
||||
<Cell head last />
|
||||
</tr>
|
||||
</Head>
|
||||
<tbody class="divide-y">
|
||||
{#if groups === undefined}
|
||||
{#each new Array(4) as _}
|
||||
<tr>
|
||||
@@ -129,23 +133,29 @@
|
||||
{/each}
|
||||
{:else}
|
||||
{#each groups as { name, summary, extra_perms, canWrite } (name)}
|
||||
<tr>
|
||||
<td class="!px-0 text-center">
|
||||
<SharedBadge {canWrite} extraPerms={extra_perms} />
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
href="#{name}"
|
||||
on:click={() => {
|
||||
editGroupName = name
|
||||
groupDrawer.openDrawer()
|
||||
}}
|
||||
>{name}
|
||||
</a>
|
||||
</td>
|
||||
<td>{summary ?? ''}</td>
|
||||
<td><GroupInfo {name} /></td>
|
||||
<td>
|
||||
<Row
|
||||
hoverable
|
||||
on:click={() => {
|
||||
editGroupName = name
|
||||
groupDrawer.openDrawer()
|
||||
}}
|
||||
>
|
||||
<Cell first>
|
||||
<div class="flex flex-row gap-2 justify-between">
|
||||
<div>
|
||||
<span class="text-blue-500">{name}</span>
|
||||
{#if summary}
|
||||
<br />
|
||||
<span class="text-gray-500">{summary}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<SharedBadge {canWrite} extraPerms={extra_perms} />
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<GroupInfo {name} />
|
||||
</Cell>
|
||||
<Cell>
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
@@ -170,12 +180,12 @@
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</Cell>
|
||||
</Row>
|
||||
{/each}
|
||||
{/if}
|
||||
</tbody>
|
||||
</TableCustom>
|
||||
</DataTable>
|
||||
</div>
|
||||
|
||||
{#if instanceGroups && instanceGroups.length > 0}
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
let preFilteredItems: typeof filteredItems | undefined = []
|
||||
let filter = ''
|
||||
let ownerFilter: string | undefined = undefined
|
||||
let nbDisplayed = 15
|
||||
|
||||
let filterEnabledDisabled: 'all' | 'enabled' | 'disabled' = 'all'
|
||||
|
||||
@@ -290,7 +291,7 @@
|
||||
<div class="text-center text-sm text-tertiary mt-2"> No schedules </div>
|
||||
{:else if items?.length}
|
||||
<div class="border rounded-md divide-y">
|
||||
{#each items as { path, error, summary, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, extra_perms, canWrite, args, marked, jobs } (path)}
|
||||
{#each items.slice(0, nbDisplayed) as { path, error, summary, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, extra_perms, canWrite, args, marked, jobs } (path)}
|
||||
{@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`}
|
||||
{@const avg_s = jobs
|
||||
? jobs.reduce((acc, x) => acc + x.duration_ms, 0) / jobs.length
|
||||
@@ -478,6 +479,12 @@
|
||||
<NoItemFound />
|
||||
{/if}
|
||||
</div>
|
||||
{#if items && items?.length > 15 && nbDisplayed < items.length}
|
||||
<span class="text-xs"
|
||||
>{nbDisplayed} items out of {items.length}
|
||||
<button class="ml-4" on:click={() => (nbDisplayed += 30)}>load 30 more</button></span
|
||||
>
|
||||
{/if}
|
||||
</CenteredPage>
|
||||
|
||||
<ShareModal
|
||||
|
||||
Reference in New Issue
Block a user