Skip to content

Commit 6890584

Browse files
committed
feat(resources): add multi-source move, copy and flow-file promotion
- Add Sources []string to MoveResourceRequest, CopyResourceRequest and AddResourceFromFlowRequest; merged with Source, deduplicated; multi-source uses destination as base dir and runs in a single atomic DB transaction - Fix MoveResource response to return Added + Updated (not Updated only) so Apollo cache receives new parent directory entries alongside moved items - Add missing errResourceNotFound case in CopyResource (was 500 instead of 404) - Cover all new behaviour with table-driven tests (basename conflict, force overwrite, missing source, empty input, dir-into-itself guard, etc.)
1 parent 3a52079 commit 6890584

10 files changed

Lines changed: 1334 additions & 230 deletions

File tree

backend/pkg/server/docs/docs.go

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,7 +2455,7 @@ const docTemplate = `{
24552455
"tags": [
24562456
"FlowFiles"
24572457
],
2458-
"summary": "Promote a flow file or directory to user resources",
2458+
"summary": "Promote flow file(s) or directory/directories to user resources",
24592459
"parameters": [
24602460
{
24612461
"minimum": 0,
@@ -2466,7 +2466,7 @@ const docTemplate = `{
24662466
"required": true
24672467
},
24682468
{
2469-
"description": "source path, destination and force flag",
2469+
"description": "source(s), destination and force flag",
24702470
"name": "body",
24712471
"in": "body",
24722472
"required": true,
@@ -5417,7 +5417,7 @@ const docTemplate = `{
54175417
"tags": [
54185418
"Resources"
54195419
],
5420-
"summary": "Copy a resource (file or directory)",
5420+
"summary": "Copy resource(s) (files or directories)",
54215421
"parameters": [
54225422
{
54235423
"description": "copy request",
@@ -5633,7 +5633,7 @@ const docTemplate = `{
56335633
"tags": [
56345634
"Resources"
56355635
],
5636-
"summary": "Move or rename a resource (file or directory)",
5636+
"summary": "Move or rename resource(s) (files or directories)",
56375637
"parameters": [
56385638
{
56395639
"description": "move request",
@@ -5820,7 +5820,7 @@ const docTemplate = `{
58205820
{
58215821
"type": "integer",
58225822
"description": "role id",
5823-
"name": "id",
5823+
"name": "roleID",
58245824
"in": "path",
58255825
"required": true
58265826
}
@@ -7415,21 +7415,27 @@ const docTemplate = `{
74157415
"models.AddResourceFromFlowRequest": {
74167416
"type": "object",
74177417
"required": [
7418-
"destination",
7419-
"source"
7418+
"destination"
74207419
],
74217420
"properties": {
74227421
"destination": {
7423-
"description": "Destination is the virtual path the resource will have in the user's resource tree.",
7422+
"description": "Destination is the virtual path prefix in the user's resource tree.",
74247423
"type": "string"
74257424
},
74267425
"force": {
7427-
"description": "Force overwrites an existing resource at Destination if one already exists.",
7426+
"description": "Force overwrites existing resources at the target paths when true.",
74287427
"type": "boolean"
74297428
},
74307429
"source": {
7431-
"description": "Source is a relative path within the flow cache, e.g. \"container/work/result.md\" or \"uploads/task.md\" or \"uploads/work/\".",
7430+
"description": "Source is a single relative path within the flow cache (kept for backward\ncompatibility). Combined with Sources when both are provided.",
74327431
"type": "string"
7432+
},
7433+
"sources": {
7434+
"description": "Sources is a list of relative paths within the flow cache. Combined with\nSource when both are provided; duplicate entries are silently removed.",
7435+
"type": "array",
7436+
"items": {
7437+
"type": "string"
7438+
}
74337439
}
74347440
}
74357441
},
@@ -7810,18 +7816,27 @@ const docTemplate = `{
78107816
"models.CopyResourceRequest": {
78117817
"type": "object",
78127818
"required": [
7813-
"destination",
7814-
"source"
7819+
"destination"
78157820
],
78167821
"properties": {
78177822
"destination": {
7823+
"description": "Destination is the exact target path (single source) or base directory\n(multiple sources). Required.",
78187824
"type": "string"
78197825
},
78207826
"force": {
7827+
"description": "Force overwrites existing resources at the target paths when true.",
78217828
"type": "boolean"
78227829
},
78237830
"source": {
7831+
"description": "Source is kept for backward compatibility (single source).\nCombined with Sources when both are provided; duplicates are removed.",
78247832
"type": "string"
7833+
},
7834+
"sources": {
7835+
"description": "Sources is a list of virtual resource paths to copy.\nCombined with Source when both are provided; duplicates are removed.",
7836+
"type": "array",
7837+
"items": {
7838+
"type": "string"
7839+
}
78257840
}
78267841
}
78277842
},
@@ -8476,18 +8491,27 @@ const docTemplate = `{
84768491
"models.MoveResourceRequest": {
84778492
"type": "object",
84788493
"required": [
8479-
"destination",
8480-
"source"
8494+
"destination"
84818495
],
84828496
"properties": {
84838497
"destination": {
8498+
"description": "Destination is the exact target path (single source) or base directory\n(multiple sources). Required.",
84848499
"type": "string"
84858500
},
84868501
"force": {
8502+
"description": "Force overwrites existing resources at the target paths when true.",
84878503
"type": "boolean"
84888504
},
84898505
"source": {
8506+
"description": "Source is kept for backward compatibility (single source).\nCombined with Sources when both are provided; duplicates are removed.",
84908507
"type": "string"
8508+
},
8509+
"sources": {
8510+
"description": "Sources is a list of virtual resource paths to move.\nCombined with Source when both are provided; duplicates are removed.",
8511+
"type": "array",
8512+
"items": {
8513+
"type": "string"
8514+
}
84918515
}
84928516
}
84938517
},

backend/pkg/server/docs/swagger.json

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,7 @@
24472447
"tags": [
24482448
"FlowFiles"
24492449
],
2450-
"summary": "Promote a flow file or directory to user resources",
2450+
"summary": "Promote flow file(s) or directory/directories to user resources",
24512451
"parameters": [
24522452
{
24532453
"minimum": 0,
@@ -2458,7 +2458,7 @@
24582458
"required": true
24592459
},
24602460
{
2461-
"description": "source path, destination and force flag",
2461+
"description": "source(s), destination and force flag",
24622462
"name": "body",
24632463
"in": "body",
24642464
"required": true,
@@ -5409,7 +5409,7 @@
54095409
"tags": [
54105410
"Resources"
54115411
],
5412-
"summary": "Copy a resource (file or directory)",
5412+
"summary": "Copy resource(s) (files or directories)",
54135413
"parameters": [
54145414
{
54155415
"description": "copy request",
@@ -5625,7 +5625,7 @@
56255625
"tags": [
56265626
"Resources"
56275627
],
5628-
"summary": "Move or rename a resource (file or directory)",
5628+
"summary": "Move or rename resource(s) (files or directories)",
56295629
"parameters": [
56305630
{
56315631
"description": "move request",
@@ -5812,7 +5812,7 @@
58125812
{
58135813
"type": "integer",
58145814
"description": "role id",
5815-
"name": "id",
5815+
"name": "roleID",
58165816
"in": "path",
58175817
"required": true
58185818
}
@@ -7407,21 +7407,27 @@
74077407
"models.AddResourceFromFlowRequest": {
74087408
"type": "object",
74097409
"required": [
7410-
"destination",
7411-
"source"
7410+
"destination"
74127411
],
74137412
"properties": {
74147413
"destination": {
7415-
"description": "Destination is the virtual path the resource will have in the user's resource tree.",
7414+
"description": "Destination is the virtual path prefix in the user's resource tree.",
74167415
"type": "string"
74177416
},
74187417
"force": {
7419-
"description": "Force overwrites an existing resource at Destination if one already exists.",
7418+
"description": "Force overwrites existing resources at the target paths when true.",
74207419
"type": "boolean"
74217420
},
74227421
"source": {
7423-
"description": "Source is a relative path within the flow cache, e.g. \"container/work/result.md\" or \"uploads/task.md\" or \"uploads/work/\".",
7422+
"description": "Source is a single relative path within the flow cache (kept for backward\ncompatibility). Combined with Sources when both are provided.",
74247423
"type": "string"
7424+
},
7425+
"sources": {
7426+
"description": "Sources is a list of relative paths within the flow cache. Combined with\nSource when both are provided; duplicate entries are silently removed.",
7427+
"type": "array",
7428+
"items": {
7429+
"type": "string"
7430+
}
74257431
}
74267432
}
74277433
},
@@ -7802,18 +7808,27 @@
78027808
"models.CopyResourceRequest": {
78037809
"type": "object",
78047810
"required": [
7805-
"destination",
7806-
"source"
7811+
"destination"
78077812
],
78087813
"properties": {
78097814
"destination": {
7815+
"description": "Destination is the exact target path (single source) or base directory\n(multiple sources). Required.",
78107816
"type": "string"
78117817
},
78127818
"force": {
7819+
"description": "Force overwrites existing resources at the target paths when true.",
78137820
"type": "boolean"
78147821
},
78157822
"source": {
7823+
"description": "Source is kept for backward compatibility (single source).\nCombined with Sources when both are provided; duplicates are removed.",
78167824
"type": "string"
7825+
},
7826+
"sources": {
7827+
"description": "Sources is a list of virtual resource paths to copy.\nCombined with Source when both are provided; duplicates are removed.",
7828+
"type": "array",
7829+
"items": {
7830+
"type": "string"
7831+
}
78177832
}
78187833
}
78197834
},
@@ -8468,18 +8483,27 @@
84688483
"models.MoveResourceRequest": {
84698484
"type": "object",
84708485
"required": [
8471-
"destination",
8472-
"source"
8486+
"destination"
84738487
],
84748488
"properties": {
84758489
"destination": {
8490+
"description": "Destination is the exact target path (single source) or base directory\n(multiple sources). Required.",
84768491
"type": "string"
84778492
},
84788493
"force": {
8494+
"description": "Force overwrites existing resources at the target paths when true.",
84798495
"type": "boolean"
84808496
},
84818497
"source": {
8498+
"description": "Source is kept for backward compatibility (single source).\nCombined with Sources when both are provided; duplicates are removed.",
84828499
"type": "string"
8500+
},
8501+
"sources": {
8502+
"description": "Sources is a list of virtual resource paths to move.\nCombined with Source when both are provided; duplicates are removed.",
8503+
"type": "array",
8504+
"items": {
8505+
"type": "string"
8506+
}
84838507
}
84848508
}
84858509
},

0 commit comments

Comments
 (0)