Skip to content

Commit d7ee44c

Browse files
committed
feat(api): implement file download and parsing functionality for artifacts, including new endpoint support and file content retrieval
1 parent c4e6ccd commit d7ee44c

10 files changed

Lines changed: 674 additions & 6 deletions

File tree

src/server/api/go/docs/docs.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ const docTemplate = `{
142142
"BearerAuth": []
143143
}
144144
],
145-
"description": "Get artifact information by path and filename. Optionally include a presigned URL for downloading.",
145+
"description": "Get artifact information by path and filename. Optionally include a presigned URL for downloading and parsed file content.",
146146
"consumes": [
147147
"application/json"
148148
],
@@ -176,6 +176,12 @@ const docTemplate = `{
176176
"name": "with_public_url",
177177
"in": "query"
178178
},
179+
{
180+
"type": "boolean",
181+
"description": "Whether to return parsed file content, default is true",
182+
"name": "with_content",
183+
"in": "query"
184+
},
179185
{
180186
"type": "integer",
181187
"description": "Expire time in seconds for presigned URL (default: 3600)",
@@ -2401,6 +2407,19 @@ const docTemplate = `{
24012407
}
24022408
},
24032409
"definitions": {
2410+
"fileparser.FileContent": {
2411+
"type": "object",
2412+
"properties": {
2413+
"raw": {
2414+
"description": "Raw text content",
2415+
"type": "string"
2416+
},
2417+
"type": {
2418+
"description": "\"text\", \"json\", \"csv\", \"code\"",
2419+
"type": "string"
2420+
}
2421+
}
2422+
},
24042423
"handler.ConnectToSpaceReq": {
24052424
"type": "object",
24062425
"required": [
@@ -2496,6 +2515,9 @@ const docTemplate = `{
24962515
"artifact": {
24972516
"$ref": "#/definitions/model.Artifact"
24982517
},
2518+
"content": {
2519+
"$ref": "#/definitions/fileparser.FileContent"
2520+
},
24992521
"public_url": {
25002522
"type": "string"
25012523
}
@@ -2888,6 +2910,13 @@ const docTemplate = `{
28882910
"$ref": "#/definitions/model.Space"
28892911
}
28902912
},
2913+
"tasks": {
2914+
"description": "Project \u003c-\u003e Task",
2915+
"type": "array",
2916+
"items": {
2917+
"$ref": "#/definitions/model.Task"
2918+
}
2919+
},
28912920
"updated_at": {
28922921
"type": "string"
28932922
}
@@ -3006,6 +3035,17 @@ const docTemplate = `{
30063035
"order": {
30073036
"type": "integer"
30083037
},
3038+
"project": {
3039+
"description": "Task \u003c-\u003e Project",
3040+
"allOf": [
3041+
{
3042+
"$ref": "#/definitions/model.Project"
3043+
}
3044+
]
3045+
},
3046+
"project_id": {
3047+
"type": "string"
3048+
},
30093049
"session": {
30103050
"description": "Task \u003c-\u003e Session",
30113051
"allOf": [

src/server/api/go/docs/swagger.json

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
"BearerAuth": []
140140
}
141141
],
142-
"description": "Get artifact information by path and filename. Optionally include a presigned URL for downloading.",
142+
"description": "Get artifact information by path and filename. Optionally include a presigned URL for downloading and parsed file content.",
143143
"consumes": [
144144
"application/json"
145145
],
@@ -173,6 +173,12 @@
173173
"name": "with_public_url",
174174
"in": "query"
175175
},
176+
{
177+
"type": "boolean",
178+
"description": "Whether to return parsed file content, default is true",
179+
"name": "with_content",
180+
"in": "query"
181+
},
176182
{
177183
"type": "integer",
178184
"description": "Expire time in seconds for presigned URL (default: 3600)",
@@ -2398,6 +2404,19 @@
23982404
}
23992405
},
24002406
"definitions": {
2407+
"fileparser.FileContent": {
2408+
"type": "object",
2409+
"properties": {
2410+
"raw": {
2411+
"description": "Raw text content",
2412+
"type": "string"
2413+
},
2414+
"type": {
2415+
"description": "\"text\", \"json\", \"csv\", \"code\"",
2416+
"type": "string"
2417+
}
2418+
}
2419+
},
24012420
"handler.ConnectToSpaceReq": {
24022421
"type": "object",
24032422
"required": [
@@ -2493,6 +2512,9 @@
24932512
"artifact": {
24942513
"$ref": "#/definitions/model.Artifact"
24952514
},
2515+
"content": {
2516+
"$ref": "#/definitions/fileparser.FileContent"
2517+
},
24962518
"public_url": {
24972519
"type": "string"
24982520
}
@@ -2885,6 +2907,13 @@
28852907
"$ref": "#/definitions/model.Space"
28862908
}
28872909
},
2910+
"tasks": {
2911+
"description": "Project \u003c-\u003e Task",
2912+
"type": "array",
2913+
"items": {
2914+
"$ref": "#/definitions/model.Task"
2915+
}
2916+
},
28882917
"updated_at": {
28892918
"type": "string"
28902919
}
@@ -3003,6 +3032,17 @@
30033032
"order": {
30043033
"type": "integer"
30053034
},
3035+
"project": {
3036+
"description": "Task \u003c-\u003e Project",
3037+
"allOf": [
3038+
{
3039+
"$ref": "#/definitions/model.Project"
3040+
}
3041+
]
3042+
},
3043+
"project_id": {
3044+
"type": "string"
3045+
},
30063046
"session": {
30073047
"description": "Task \u003c-\u003e Session",
30083048
"allOf": [

src/server/api/go/docs/swagger.yaml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
basePath: /api/v1
22
definitions:
3+
fileparser.FileContent:
4+
properties:
5+
raw:
6+
description: Raw text content
7+
type: string
8+
type:
9+
description: '"text", "json", "csv", "code"'
10+
type: string
11+
type: object
312
handler.ConnectToSpaceReq:
413
properties:
514
space_id:
@@ -65,6 +74,8 @@ definitions:
6574
properties:
6675
artifact:
6776
$ref: '#/definitions/model.Artifact'
77+
content:
78+
$ref: '#/definitions/fileparser.FileContent'
6879
public_url:
6980
type: string
7081
type: object
@@ -318,6 +329,11 @@ definitions:
318329
items:
319330
$ref: '#/definitions/model.Space'
320331
type: array
332+
tasks:
333+
description: Project <-> Task
334+
items:
335+
$ref: '#/definitions/model.Task'
336+
type: array
321337
updated_at:
322338
type: string
323339
type: object
@@ -393,6 +409,12 @@ definitions:
393409
type: array
394410
order:
395411
type: integer
412+
project:
413+
allOf:
414+
- $ref: '#/definitions/model.Project'
415+
description: Task <-> Project
416+
project_id:
417+
type: string
396418
session:
397419
allOf:
398420
- $ref: '#/definitions/model.Session'
@@ -549,7 +571,7 @@ paths:
549571
consumes:
550572
- application/json
551573
description: Get artifact information by path and filename. Optionally include
552-
a presigned URL for downloading.
574+
a presigned URL for downloading and parsed file content.
553575
parameters:
554576
- description: Disk ID
555577
example: 123e4567-e89b-12d3-a456-426614174000
@@ -567,6 +589,10 @@ paths:
567589
in: query
568590
name: with_public_url
569591
type: boolean
592+
- description: Whether to return parsed file content, default is true
593+
in: query
594+
name: with_content
595+
type: boolean
570596
- description: 'Expire time in seconds for presigned URL (default: 3600)'
571597
in: query
572598
name: expire

src/server/api/go/internal/infra/blob/s3.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,28 @@ func (u *S3Deps) DownloadJSON(ctx context.Context, key string, target interface{
271271

272272
return nil
273273
}
274+
275+
// DownloadFile downloads file content from S3 and returns the content as bytes
276+
func (u *S3Deps) DownloadFile(ctx context.Context, key string) ([]byte, error) {
277+
if key == "" {
278+
return nil, errors.New("key is empty")
279+
}
280+
281+
result, err := u.Client.GetObject(ctx, &s3.GetObjectInput{
282+
Bucket: &u.Bucket,
283+
Key: &key,
284+
})
285+
if err != nil {
286+
return nil, fmt.Errorf("get object from S3: %w", err)
287+
}
288+
defer result.Body.Close()
289+
290+
// Read the response body
291+
var buf bytes.Buffer
292+
_, err = buf.ReadFrom(result.Body)
293+
if err != nil {
294+
return nil, fmt.Errorf("read response body: %w", err)
295+
}
296+
297+
return buf.Bytes(), nil
298+
}

src/server/api/go/internal/modules/handler/artifact.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/memodb-io/Acontext/internal/modules/model"
1212
"github.com/memodb-io/Acontext/internal/modules/serializer"
1313
"github.com/memodb-io/Acontext/internal/modules/service"
14+
"github.com/memodb-io/Acontext/internal/pkg/utils/fileparser"
1415
"github.com/memodb-io/Acontext/internal/pkg/utils/path"
1516
)
1617

@@ -148,24 +149,27 @@ func (h *ArtifactHandler) DeleteArtifact(c *gin.Context) {
148149
type GetArtifactReq struct {
149150
FilePath string `form:"file_path" json:"file_path" binding:"required"` // File path including filename
150151
WithPublicURL bool `form:"with_public_url,default=true" json:"with_public_url" example:"true"`
152+
WithContent bool `form:"with_content,default=true" json:"with_content" example:"true"`
151153
Expire int `form:"expire,default=3600" json:"expire" example:"3600"` // Expire time in seconds for presigned URL
152154
}
153155

154156
type GetArtifactResp struct {
155-
Artifact *model.Artifact `json:"artifact"`
156-
PublicURL *string `json:"public_url,omitempty"`
157+
Artifact *model.Artifact `json:"artifact"`
158+
PublicURL *string `json:"public_url,omitempty"`
159+
Content *fileparser.FileContent `json:"content,omitempty"`
157160
}
158161

159162
// GetArtifact godoc
160163
//
161164
// @Summary Get artifact
162-
// @Description Get artifact information by path and filename. Optionally include a presigned URL for downloading.
165+
// @Description Get artifact information by path and filename. Optionally include a presigned URL for downloading and parsed file content.
163166
// @Tags artifact
164167
// @Accept json
165168
// @Produce json
166169
// @Param disk_id path string true "Disk ID" Format(uuid) Example(123e4567-e89b-12d3-a456-426614174000)
167170
// @Param file_path query string true "File path including filename" example:"/documents/report.pdf"
168171
// @Param with_public_url query boolean false "Whether to return public URL, default is true" example:"true"
172+
// @Param with_content query boolean false "Whether to return parsed file content, default is true" example:"true"
169173
// @Param expire query int false "Expire time in seconds for presigned URL (default: 3600)" example:"3600"
170174
// @Security BearerAuth
171175
// @Success 200 {object} serializer.Response{data=handler.GetArtifactResp}
@@ -210,6 +214,17 @@ func (h *ArtifactHandler) GetArtifact(c *gin.Context) {
210214
resp.PublicURL = &url
211215
}
212216

217+
// Parse file content if requested
218+
if req.WithContent {
219+
content, err := h.svc.GetFileContent(c.Request.Context(), diskID, filePath, filename)
220+
// Only set content if parsing succeeded
221+
// Unsupported file types (images, binaries, etc.) will not have content
222+
if err == nil && content != nil {
223+
resp.Content = content
224+
}
225+
// Don't return error for unsupported file types - just don't include content
226+
}
227+
213228
c.JSON(http.StatusOK, serializer.Response{Data: resp})
214229
}
215230

0 commit comments

Comments
 (0)