feat: list references upon renaming a script or a flow (#5487)
* Refactored flow_workspace_runnables to more generic workspace_runnable_dependencies
* list flows referencing an item upon renaming it
* Refactor with two exclusive columns to avoid breaking FK constraints
* Show apps depending on item upon renaming
* sqlx prepare
* list-disc instead of •
* on delete and on update cascade
* displayPathChangedWarning oneOf check instead noneOf
* combine migrations + add "on update cascade" to flow fk
* unique index on app dependencies to avoid duplicates
* create new workspace_runnable_dependencies instead of renaming old table
* Add "looking for references" loading msg
* Revert "create new workspace_runnable_dependencies instead of renaming old table"
This reverts commit 015c38ca8f.
* flow_workspace_runnables view for backwards compatibility
* Add warning for script imports on rename
* support import dependency tracking in deno
* number of using scripts / flows / apps tooltip
* forgot sqlx prepare
* delete app-related rows in down migration
This commit is contained in:
17
backend/.sqlx/query-01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8.json
generated
Normal file
17
backend/.sqlx/query-01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8"
|
||||
}
|
||||
16
backend/.sqlx/query-09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68.json
generated
Normal file
16
backend/.sqlx/query-09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68"
|
||||
}
|
||||
24
backend/.sqlx/query-171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f.json
generated
Normal file
24
backend/.sqlx/query-171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT a.path\n FROM workspace_runnable_dependencies wru \n JOIN app a\n ON wru.app_path = a.path AND wru.workspace_id = a.workspace_id\n WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f"
|
||||
}
|
||||
15
backend/.sqlx/query-1ea8c2ce29da8d1be19dfd3ecc7f84fef7557f3e3046e7188ee06f6c3a2b2add.json
generated
Normal file
15
backend/.sqlx/query-1ea8c2ce29da8d1be19dfd3ecc7f84fef7557f3e3046e7188ee06f6c3a2b2add.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM workspace_runnable_usage WHERE resource_path = $1 AND workspace_id = $2 and resource_kind = 'flow'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1ea8c2ce29da8d1be19dfd3ecc7f84fef7557f3e3046e7188ee06f6c3a2b2add"
|
||||
}
|
||||
16
backend/.sqlx/query-44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb.json
generated
Normal file
16
backend/.sqlx/query-44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_dependencies SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb"
|
||||
}
|
||||
24
backend/.sqlx/query-56a2fe44b73728c9aba10f3d1c7707bd1c48c58aaa5b2ae7560cbed3c8d46329.json
generated
Normal file
24
backend/.sqlx/query-56a2fe44b73728c9aba10f3d1c7707bd1c48c58aaa5b2ae7560cbed3c8d46329.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT f.path\n FROM workspace_runnable_usage wru \n JOIN flow f\n ON wru.resource_path = f.path AND wru.workspace_id = f.workspace_id\n WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "56a2fe44b73728c9aba10f3d1c7707bd1c48c58aaa5b2ae7560cbed3c8d46329"
|
||||
}
|
||||
23
backend/.sqlx/query-6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5.json
generated
Normal file
23
backend/.sqlx/query-6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT importer_path FROM dependency_map \n WHERE workspace_id = $1 AND imported_path = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "importer_path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5"
|
||||
}
|
||||
15
backend/.sqlx/query-7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01.json
generated
Normal file
15
backend/.sqlx/query-7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM workspace_runnable_dependencies WHERE flow_path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01"
|
||||
}
|
||||
24
backend/.sqlx/query-808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245.json
generated
Normal file
24
backend/.sqlx/query-808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT f.path\n FROM workspace_runnable_dependencies wru \n JOIN flow f\n ON wru.flow_path = f.path AND wru.workspace_id = f.workspace_id\n WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245"
|
||||
}
|
||||
17
backend/.sqlx/query-894e5080e83b098839a050bb472359d6d9fdfdbffb40a2464a45056660a3a2d4.json
generated
Normal file
17
backend/.sqlx/query-894e5080e83b098839a050bb472359d6d9fdfdbffb40a2464a45056660a3a2d4.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_runnable_usage (resource_path, runnable_path, script_hash, runnable_is_flow, workspace_id, resource_kind) VALUES ($1, $2, $3, FALSE, $4, 'flow') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "894e5080e83b098839a050bb472359d6d9fdfdbffb40a2464a45056660a3a2d4"
|
||||
}
|
||||
16
backend/.sqlx/query-9c956541e068154193243795d96cc62a0fc9d4ba56014f3e554cc2b50ce1da06.json
generated
Normal file
16
backend/.sqlx/query-9c956541e068154193243795d96cc62a0fc9d4ba56014f3e554cc2b50ce1da06.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_usage SET resource_path = REGEXP_REPLACE(resource_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE resource_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9c956541e068154193243795d96cc62a0fc9d4ba56014f3e554cc2b50ce1da06"
|
||||
}
|
||||
15
backend/.sqlx/query-a28f8697da75d7ac2c6e13dda394ee12e8cffe925bd7cc4e9d02dc90ffe200f3.json
generated
Normal file
15
backend/.sqlx/query-a28f8697da75d7ac2c6e13dda394ee12e8cffe925bd7cc4e9d02dc90ffe200f3.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM workspace_runnable_usage WHERE resource_path = $1 AND workspace_id = $2 and resource_kind = 'app'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a28f8697da75d7ac2c6e13dda394ee12e8cffe925bd7cc4e9d02dc90ffe200f3"
|
||||
}
|
||||
16
backend/.sqlx/query-ab9783a48e0f6cacd5dd86cfa21ba8354d4cd733335db3d76155b663f668b9d7.json
generated
Normal file
16
backend/.sqlx/query-ab9783a48e0f6cacd5dd86cfa21ba8354d4cd733335db3d76155b663f668b9d7.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_runnable_usage (resource_path, runnable_path, runnable_is_flow, workspace_id, resource_kind) VALUES ($1, $2, TRUE, $3, 'flow') ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ab9783a48e0f6cacd5dd86cfa21ba8354d4cd733335db3d76155b663f668b9d7"
|
||||
}
|
||||
17
backend/.sqlx/query-aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec.json
generated
Normal file
17
backend/.sqlx/query-aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec"
|
||||
}
|
||||
15
backend/.sqlx/query-af2f780da994befd09959d3b1b077eb9b71337a9aa2a64cd2db031e64e929251.json
generated
Normal file
15
backend/.sqlx/query-af2f780da994befd09959d3b1b077eb9b71337a9aa2a64cd2db031e64e929251.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_usage SET workspace_id = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "af2f780da994befd09959d3b1b077eb9b71337a9aa2a64cd2db031e64e929251"
|
||||
}
|
||||
15
backend/.sqlx/query-b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2.json
generated
Normal file
15
backend/.sqlx/query-b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM workspace_runnable_dependencies WHERE app_path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2"
|
||||
}
|
||||
16
backend/.sqlx/query-baf63525ca210c22d3ad8c0b197dc6b39dabab77ae7810f553f1773995db51f1.json
generated
Normal file
16
backend/.sqlx/query-baf63525ca210c22d3ad8c0b197dc6b39dabab77ae7810f553f1773995db51f1.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_usage SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "baf63525ca210c22d3ad8c0b197dc6b39dabab77ae7810f553f1773995db51f1"
|
||||
}
|
||||
16
backend/.sqlx/query-c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952.json
generated
Normal file
16
backend/.sqlx/query-c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_dependencies SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952"
|
||||
}
|
||||
15
backend/.sqlx/query-e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8.json
generated
Normal file
15
backend/.sqlx/query-e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_dependencies SET workspace_id = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8"
|
||||
}
|
||||
16
backend/.sqlx/query-f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886.json
generated
Normal file
16
backend/.sqlx/query-f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_runnable_dependencies SET app_path = REGEXP_REPLACE(app_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE app_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
DROP VIEW flow_workspace_runnables;
|
||||
|
||||
DELETE FROM workspace_runnable_dependencies WHERE flow_path IS NULL;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
DROP CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
ADD CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey
|
||||
FOREIGN KEY (flow_path, workspace_id) REFERENCES flow (path, workspace_id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
DROP CONSTRAINT workspace_runnable_dependencies_path_exclusive;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
DROP CONSTRAINT fk_workspace_runnable_dependencies_app_path;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies DROP COLUMN app_path;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies ALTER flow_path SET NOT NULL;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
RENAME TO flow_workspace_runnables;
|
||||
@@ -0,0 +1,42 @@
|
||||
-- flow_workspace_runnables only stored runnable usages by
|
||||
-- flows although apps can also use runnables
|
||||
|
||||
ALTER TABLE flow_workspace_runnables
|
||||
RENAME TO workspace_runnable_dependencies;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies ALTER flow_path DROP NOT NULL;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
ADD COLUMN app_path VARCHAR(255);
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
ADD CONSTRAINT workspace_runnable_dependencies_path_exclusive CHECK (
|
||||
(flow_path IS NOT NULL AND app_path IS NULL) OR
|
||||
(flow_path IS NULL AND app_path IS NOT NULL)
|
||||
);
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
ADD CONSTRAINT fk_workspace_runnable_dependencies_app_path
|
||||
FOREIGN KEY (app_path, workspace_id) REFERENCES app (path, workspace_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
DROP CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey;
|
||||
|
||||
ALTER TABLE workspace_runnable_dependencies
|
||||
ADD CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey
|
||||
FOREIGN KEY (flow_path, workspace_id) REFERENCES flow (path, workspace_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX app_workspace_without_hash_unique_idx ON workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) WHERE script_hash IS NULL;
|
||||
CREATE UNIQUE INDEX app_workspace_with_hash_unique_idx ON workspace_runnable_dependencies (app_path, runnable_path, script_hash, runnable_is_flow, workspace_id) WHERE script_hash IS NOT NULL;
|
||||
|
||||
|
||||
-- This is to maintain compatibility with old workers
|
||||
CREATE VIEW flow_workspace_runnables AS
|
||||
SELECT flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id
|
||||
FROM workspace_runnable_dependencies;
|
||||
@@ -4774,6 +4774,25 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/ScriptHistory"
|
||||
|
||||
/w/{workspace}/scripts/list_paths_from_workspace_runnable/{path}:
|
||||
get:
|
||||
summary: list script paths using provided script as a relative import
|
||||
operationId: listScriptPathsFromWorkspaceRunnable
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: list of script paths
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/scripts/get_latest_version/{path}:
|
||||
get:
|
||||
summary: get scripts's latest version (hash)
|
||||
@@ -5934,6 +5953,26 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AppHistory"
|
||||
|
||||
/w/{workspace}/apps/list_paths_from_workspace_runnable/{runnable_kind}/{path}:
|
||||
get:
|
||||
summary: list app paths from workspace runnable
|
||||
operationId: listAppPathsFromWorkspaceRunnable
|
||||
tags:
|
||||
- app
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/RunnableKind"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: list of app paths
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/history_update/a/{id}/v/{version}:
|
||||
post:
|
||||
summary: update app history
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
resources::get_resource_value_interpolated_internal,
|
||||
users::{require_owner_of_path, OptAuthed},
|
||||
utils::WithStarredInfoQuery,
|
||||
utils::{RunnableKind, WithStarredInfoQuery},
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
HTTP_CLIENT,
|
||||
};
|
||||
@@ -89,6 +89,10 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/history/p/*path", get(get_app_history))
|
||||
.route("/get_latest_version/*path", get(get_latest_version))
|
||||
.route("/history_update/a/:id/v/:version", post(update_app_history))
|
||||
.route(
|
||||
"/list_paths_from_workspace_runnable/:runnable_kind/*path",
|
||||
get(list_paths_from_workspace_runnable),
|
||||
)
|
||||
.route("/custom_path_exists/*custom_path", get(custom_path_exists))
|
||||
}
|
||||
|
||||
@@ -719,6 +723,28 @@ async fn get_secret_id(
|
||||
Ok(hx)
|
||||
}
|
||||
|
||||
async fn list_paths_from_workspace_runnable(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, StripPath)>,
|
||||
) -> JsonResult<Vec<String>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let runnables = sqlx::query_scalar!(
|
||||
r#"SELECT a.path
|
||||
FROM workspace_runnable_dependencies wru
|
||||
JOIN app a
|
||||
ON wru.app_path = a.path AND wru.workspace_id = a.workspace_id
|
||||
WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3"#,
|
||||
path.to_path(),
|
||||
matches!(runnable_kind, RunnableKind::Flow),
|
||||
w_id
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Json(runnables))
|
||||
}
|
||||
|
||||
async fn create_app(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -336,10 +336,10 @@ async fn list_paths_from_workspace_runnable(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let runnables = sqlx::query_scalar!(
|
||||
r#"SELECT f.path
|
||||
FROM flow_workspace_runnables fwr
|
||||
JOIN flow f
|
||||
ON fwr.flow_path = f.path AND fwr.workspace_id = f.workspace_id
|
||||
WHERE fwr.runnable_path = $1 AND fwr.runnable_is_flow = $2 AND fwr.workspace_id = $3"#,
|
||||
FROM workspace_runnable_dependencies wru
|
||||
JOIN flow f
|
||||
ON wru.flow_path = f.path AND wru.workspace_id = f.workspace_id
|
||||
WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3"#,
|
||||
path.to_path(),
|
||||
matches!(runnable_kind, RunnableKind::Flow),
|
||||
w_id
|
||||
|
||||
@@ -147,6 +147,10 @@ pub fn workspaced_service() -> Router {
|
||||
)
|
||||
.route("/history/p/*path", get(get_script_history))
|
||||
.route("/get_latest_version/*path", get(get_latest_version))
|
||||
.route(
|
||||
"/list_paths_from_workspace_runnable/*path",
|
||||
get(list_paths_from_workspace_runnable),
|
||||
)
|
||||
.route(
|
||||
"/history_update/h/:hash/p/*path",
|
||||
post(update_script_history),
|
||||
@@ -447,6 +451,24 @@ async fn create_snapshot_script(
|
||||
return Ok((StatusCode::CREATED, format!("{}", script_hash.unwrap())));
|
||||
}
|
||||
|
||||
async fn list_paths_from_workspace_runnable(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<String>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let runnables = sqlx::query_scalar!(
|
||||
r#"SELECT importer_path FROM dependency_map
|
||||
WHERE workspace_id = $1 AND imported_path = $2"#,
|
||||
w_id,
|
||||
path.to_path(),
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Json(runnables))
|
||||
}
|
||||
|
||||
async fn create_script(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -2538,7 +2538,7 @@ async fn update_username_in_workpsace<'c>(
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"UPDATE flow_workspace_runnables SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
r#"UPDATE workspace_runnable_dependencies SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
@@ -2546,7 +2546,15 @@ async fn update_username_in_workpsace<'c>(
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"UPDATE flow_workspace_runnables SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
r#"UPDATE workspace_runnable_dependencies SET app_path = REGEXP_REPLACE(app_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE app_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
).execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"UPDATE workspace_runnable_dependencies SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
|
||||
new_username,
|
||||
old_username,
|
||||
w_id
|
||||
|
||||
@@ -190,7 +190,7 @@ pub(crate) async fn change_workspace_id(
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE flow_workspace_runnables SET workspace_id = $1 WHERE workspace_id = $2",
|
||||
"UPDATE workspace_runnable_dependencies SET workspace_id = $1 WHERE workspace_id = $2",
|
||||
&rw.new_id,
|
||||
&old_id
|
||||
)
|
||||
|
||||
@@ -177,7 +177,7 @@ fn try_normalize(path: &Path) -> Option<PathBuf> {
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn parse_bun_relative_imports(raw_code: &str, script_path: &str) -> error::Result<Vec<String>> {
|
||||
fn parse_ts_relative_imports(raw_code: &str, script_path: &str) -> error::Result<Vec<String>> {
|
||||
let mut relative_imports = vec![];
|
||||
let r = parse_expr_for_imports(raw_code)?;
|
||||
for import in r {
|
||||
@@ -209,8 +209,8 @@ pub fn extract_relative_imports(
|
||||
match language {
|
||||
#[cfg(feature = "python")]
|
||||
Some(ScriptLang::Python3) => parse_relative_imports(&raw_code, script_path).ok(),
|
||||
Some(ScriptLang::Bun) | Some(ScriptLang::Bunnative) => {
|
||||
parse_bun_relative_imports(&raw_code, script_path).ok()
|
||||
Some(ScriptLang::Bun) | Some(ScriptLang::Bunnative) | Some(ScriptLang::Deno) => {
|
||||
parse_ts_relative_imports(&raw_code, script_path).ok()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -644,7 +644,7 @@ pub async fn handle_flow_dependency_job(
|
||||
tx = clear_dependency_parent_path(&parent_path, &job_path, &job.workspace_id, "flow", tx)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"DELETE FROM flow_workspace_runnables WHERE flow_path = $1 AND workspace_id = $2",
|
||||
"DELETE FROM workspace_runnable_dependencies WHERE flow_path = $1 AND workspace_id = $2",
|
||||
job_path,
|
||||
job.workspace_id
|
||||
)
|
||||
@@ -1013,7 +1013,7 @@ async fn lock_modules<'c>(
|
||||
}
|
||||
FlowModuleValue::Script { path, hash, .. } if !path.starts_with("hub/") => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO flow_workspace_runnables (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING",
|
||||
job_path,
|
||||
path,
|
||||
hash.map(|h| h.0),
|
||||
@@ -1024,10 +1024,10 @@ async fn lock_modules<'c>(
|
||||
}
|
||||
FlowModuleValue::Flow { path, .. } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO flow_workspace_runnables (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING",
|
||||
"INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING",
|
||||
job_path,
|
||||
path,
|
||||
job.workspace_id
|
||||
job.workspace_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
@@ -1439,6 +1439,22 @@ async fn lock_modules_app(
|
||||
) -> Result<Value> {
|
||||
match value {
|
||||
Value::Object(mut m) => {
|
||||
if let (Some(Value::String(ref run_type)), Some(path), Some("runnableByPath")) = (
|
||||
m.get("runType"),
|
||||
m.get("path").and_then(|s| s.as_str()),
|
||||
m.get("type").and_then(|s| s.as_str()),
|
||||
) {
|
||||
// No script_hash because apps don't supports script version locks yet
|
||||
sqlx::query!(
|
||||
"INSERT INTO workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING",
|
||||
job_path,
|
||||
path,
|
||||
run_type == "flow",
|
||||
job.workspace_id
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
}
|
||||
if m.contains_key("inlineScript") {
|
||||
let v = m.get_mut("inlineScript").unwrap();
|
||||
if let Some(v) = v.as_object_mut() {
|
||||
@@ -1595,6 +1611,15 @@ pub async fn handle_app_dependency_job(
|
||||
.clone()
|
||||
.ok_or_else(|| Error::internal_err("App Dependency requires script hash".to_owned()))?
|
||||
.0;
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM workspace_runnable_dependencies WHERE app_path = $1 AND workspace_id = $2",
|
||||
job_path,
|
||||
job.workspace_id
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
|
||||
let record = sqlx::query!("SELECT app_id, value FROM app_version WHERE id = $1", id)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<script lang="ts">
|
||||
import { pathToMeta, type Meta } from '$lib/common'
|
||||
|
||||
import { localeConcatAnd, pluralize } from '$lib/utils'
|
||||
import {
|
||||
AppService,
|
||||
FlowService,
|
||||
@@ -30,7 +31,8 @@
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import FolderEditor from './FolderEditor.svelte'
|
||||
import { random_adj } from './random_positive_adjetive'
|
||||
import { Eye, Folder, Plus, SearchCode, User } from 'lucide-svelte'
|
||||
import { Eye, Folder, Loader2, Plus, SearchCode, User } from 'lucide-svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
|
||||
type PathKind =
|
||||
| 'resource'
|
||||
@@ -330,6 +332,37 @@
|
||||
const openSearchWithPrefilledText: (t?: string) => void = getContext(
|
||||
'openSearchWithPrefilledText'
|
||||
)
|
||||
|
||||
$: displayPathChangedWarning =
|
||||
(['flow', 'script', 'resource', 'variable'] as PathKind[]).includes(kind) &&
|
||||
initialPath &&
|
||||
initialPath !== path
|
||||
|
||||
$: pathUsageInFlowsPromise =
|
||||
(kind == 'script' || kind == 'flow') &&
|
||||
!!$workspaceStore &&
|
||||
FlowService.listFlowPathsFromWorkspaceRunnable({
|
||||
workspace: $workspaceStore,
|
||||
path: initialPath,
|
||||
runnableKind: kind
|
||||
})
|
||||
|
||||
$: pathUsageInAppsPromise =
|
||||
(kind == 'script' || kind == 'flow') &&
|
||||
!!$workspaceStore &&
|
||||
AppService.listAppPathsFromWorkspaceRunnable({
|
||||
workspace: $workspaceStore,
|
||||
path: initialPath,
|
||||
runnableKind: kind
|
||||
})
|
||||
|
||||
$: pathUsageInScriptsPromise =
|
||||
kind == 'script' &&
|
||||
!!$workspaceStore &&
|
||||
ScriptService.listScriptPathsFromWorkspaceRunnable({
|
||||
workspace: $workspaceStore,
|
||||
path: initialPath
|
||||
})
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={newFolder}>
|
||||
@@ -503,7 +536,65 @@
|
||||
<div class="text-red-600 dark:text-red-400 text-2xs mt-1.5">{error}</div>
|
||||
</div>
|
||||
|
||||
{#if kind != 'app' && kind != 'schedule' && kind != 'http_trigger' && kind != 'websocket_trigger' && initialPath != '' && initialPath != undefined && initialPath != path}
|
||||
{#if pathUsageInFlowsPromise || pathUsageInAppsPromise || pathUsageInScriptsPromise}
|
||||
{#await Promise.all( [pathUsageInAppsPromise, pathUsageInFlowsPromise, pathUsageInScriptsPromise] )}
|
||||
<Loader2 class="animate-spin" size={16} />
|
||||
{:then [apps, flows, scripts]}
|
||||
{#if (apps && apps.length) || (flows && flows.length) || (scripts && scripts.length)}
|
||||
<p class="text-xs">
|
||||
Used by {localeConcatAnd([
|
||||
...(scripts && scripts.length ? [pluralize(scripts.length, 'script')] : []),
|
||||
...(flows && flows.length ? [pluralize(flows.length, 'flow')] : []),
|
||||
...(apps && apps.length ? [pluralize(apps.length, 'app')] : [])
|
||||
])}
|
||||
<Tooltip>
|
||||
<ul>
|
||||
{#each scripts || [] as path}
|
||||
<li><a target="_blank" href="/scripts/edit/{path}">{path}</a></li>
|
||||
{/each}
|
||||
{#each flows || [] as path}
|
||||
<li><a target="_blank" href="/flows/edit/{path}">{path}</a></li>
|
||||
{/each}
|
||||
{#each apps || [] as path}
|
||||
<li><a target="_blank" href="/apps/edit/{path}">{path}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Tooltip>
|
||||
</p>
|
||||
{#if displayPathChangedWarning}
|
||||
<Alert
|
||||
type="warning"
|
||||
class="mt-4"
|
||||
title="Moving this item will break the following referencing it:"
|
||||
>
|
||||
<ul class="list-disc">
|
||||
{#each scripts || [] as scriptPath}
|
||||
<li>
|
||||
<a href={`/scripts/edit/${scriptPath}`} class="text-blue-400" target="_blank">
|
||||
{scriptPath}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
{#each flows || [] as flowPath}
|
||||
<li>
|
||||
<a href={`/flows/edit/${flowPath}`} class="text-blue-400" target="_blank">
|
||||
{flowPath}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
{#each apps || [] as appPath}
|
||||
<li>
|
||||
<a href={`/apps/edit/${appPath}`} class="text-blue-400" target="_blank">
|
||||
{appPath}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
{/await}
|
||||
{:else if displayPathChangedWarning}
|
||||
<Alert type="warning" class="mt-4" title="Moving may break other items relying on it">
|
||||
You are renaming an item that may be depended upon by other items. This may break apps, flows
|
||||
or resources. Find if it used elsewhere using the content search. Note that linked variables
|
||||
|
||||
@@ -1163,3 +1163,9 @@ export function isObjectTooBig(obj: any): boolean {
|
||||
const { totalItems, maxDepth } = analyze(obj)
|
||||
return maxDepth > MAX_DEPTH || totalItems > MAX_ITEMS
|
||||
}
|
||||
|
||||
export function localeConcatAnd(items: string[]) {
|
||||
if (!items.length) return ''
|
||||
if (items.length === 1) return items[0]
|
||||
return items.slice(0, -1).join(', ') + ' and ' + items[items.length - 1]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user