[client] WIP: fix actions

This commit is contained in:
Abhishek Shroff
2026-02-10 21:23:50 +05:30
parent a92d146610
commit b8bf7c8ee9
11 changed files with 85 additions and 39 deletions

View File

@@ -1,4 +1,6 @@
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -6,12 +8,14 @@ abstract class ResourceAction extends PhylumAction {
String _resourceId;
String get resourceId => _resourceId;
@override
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, ResourceResponse.fromResponse);
ResourceAction({required String resourceId}) : _resourceId = resourceId;
void setResourceId(String resourceId) {
_resourceId = resourceId;
}
@override
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return parseJsonMapResponse(response, ResourceResponse.fromResponse);
}
}

View File

@@ -1,7 +1,9 @@
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/action_resource_bookmark_remove.dart';
import 'package:phylum/libphylum/actions/action_resource_create.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -24,9 +26,6 @@ class ResourceBookmarkAddAction extends ResourceAction with JsonApiAction {
'name': bookmarkName,
};
@override
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, BookmarkResponse.fromResponse);
@override
List<LocalChange> get localChanges =>
[AddBookmarkChange(resourceId, bookmarkName: bookmarkName, dir: dir, createdMillis: createdMillis)];
@@ -62,6 +61,11 @@ class ResourceBookmarkAddAction extends ResourceAction with JsonApiAction {
);
}
@override
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return parseJsonMapResponse(response, BookmarkResponse.fromResponse);
}
@override
bool dependsOn(PhylumAction action) =>
action is ResourceAction &&

View File

@@ -1,6 +1,8 @@
import 'package:drift/drift.dart';
import 'package:http/src/streamed_response.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -24,9 +26,6 @@ class ResourceBookmarkRemoveAction extends ResourceAction with JsonApiAction {
'id': resourceId,
};
@override
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, EmptyResponse.fromResponse);
@override
List<LocalChange> get localChanges => [RemoveBookmarkChange(resourceId)];
@@ -50,6 +49,11 @@ class ResourceBookmarkRemoveAction extends ResourceAction with JsonApiAction {
);
}
@override
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return parseJsonMapResponse(response, EmptyResponse.fromResponse);
}
@override
bool dependsOn(PhylumAction action) =>
action is ResourceAction &&

View File

@@ -1,9 +1,11 @@
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/changes/delete_resource_change.dart';
import 'package:phylum/libphylum/actions/changes/update_resource_deleted_change.dart';
import 'package:phylum/libphylum/actions/changes/update_resource_modified_change.dart';
import 'package:phylum/libphylum/actions/changes/update_resource_trashed_change.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -89,6 +91,7 @@ class ResourceDeleteAction extends ResourceAction with JsonApiAction {
bool dependsOn(PhylumAction action) => action is ResourceAction && action.resourceId == resourceId;
@override
ResponseParser get parseResponse =>
permanent ? (_, response) => parseJsonMapResponse(response, EmptyResponse.fromResponse) : super.parseResponse;
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return permanent ? parseJsonMapResponse(response, EmptyResponse.fromResponse) : super.parseResponse(response);
}
}

View File

@@ -1,6 +1,8 @@
import 'package:http/src/streamed_response.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/action_resource_create.dart';
import 'package:phylum/libphylum/actions/changes/create_publink_change.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -26,9 +28,6 @@ class ResourcePublinkCreateAction extends ResourceAction with JsonApiAction {
if (accessLimit != 0) 'access_limit': accessLimit,
};
@override
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, PublinkResponse.fromResponse);
@override
List<LocalChange> get localChanges => [
CreatePublinkChange(
@@ -82,6 +81,11 @@ class ResourcePublinkCreateAction extends ResourceAction with JsonApiAction {
);
}
@override
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return parseJsonMapResponse(response, PublinkResponse.fromResponse);
}
@override
bool dependsOn(PhylumAction action) =>
action is ResourceAction &&

View File

@@ -1,12 +1,12 @@
import 'dart:nativewrappers/_internal/vm/lib/math_patch.dart';
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/action_resource.dart';
import 'package:phylum/libphylum/actions/action_resource_create.dart';
import 'package:phylum/libphylum/actions/changes/create_resource_change.dart';
import 'package:phylum/libphylum/actions/changes/create_resource_version_change.dart';
import 'package:phylum/libphylum/actions/changes/update_resource_deleted_change.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
import 'package:phylum/libphylum/util/uuid.dart';
import 'package:phylum/util/permissions.dart';
@@ -187,7 +187,7 @@ class ResourceUploadAction extends ResourceCreateAction {
// 'sha256': '', // TODO: compute and send
};
final request = MultipartRequest('PUT', uriBuilder.build());
final length = min(chunkSize, data.length - uploaded);
final length = chunkSize < data.length - uploaded ? chunkSize : data.length - uploaded;
request.files.add(MultipartFile(
'contents',
ByteStream(data.getStream(start: uploaded, end: uploaded + length)),

View File

@@ -1,6 +1,8 @@
import 'package:http/src/streamed_response.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/actions/action_resource_share.dart';
import 'package:phylum/libphylum/actions/changes/create_user_change.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_api_types.dart';
import 'package:phylum/libphylum/responses/responses.dart';
@@ -58,7 +60,9 @@ class UserInviteAction extends PhylumAction with JsonApiAction {
bool dependsOn(PhylumAction action) => false;
@override
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, UserResponse.fromResponse);
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
return parseJsonMapResponse(response, UserResponse.fromResponse);
}
@override
bool processResponse(ApiResponse response) {

View File

@@ -30,7 +30,7 @@ func SetupRoutes(r *gin.RouterGroup) {
group.PUT("/upload", handleUploadRequest)
partialUploadsGroup := group.Group("/partial_uploads")
partialUploadsGroup.POST("/start", handlePartialUploadsCreateRequest)
partialUploadsGroup.POST("/start", handlePartialUploadsStartRequest)
partialUploadsGroup.GET("/query", handlePartialUploadsQueryRequest)
partialUploadsGroup.PUT("/upload", handlePartialUploadsUploadRequest)
partialUploadsGroup.POST("/finalize", handlePartialUploadsFinalizeRequest)

View File

@@ -12,7 +12,8 @@ import (
"github.com/google/uuid"
)
type partialUploadsCreateParams struct {
type partialUploadsStartParams struct {
ID string `json:"id" form:"id" binding:"omitempty,uuid"`
Size int64 `json:"size" form:"size" binding:"required"`
SHA256 string `json:"sha256" form:"sha256"`
}
@@ -36,8 +37,8 @@ type partialUploadsFinalizeParams struct {
Conflict core.ResourceBindConflictResolution `json:"conflict" form:"conflict"`
}
func handlePartialUploadsCreateRequest(c *gin.Context) {
var params partialUploadsCreateParams
func handlePartialUploadsStartRequest(c *gin.Context) {
var params partialUploadsStartParams
err := c.ShouldBind(&params)
if err != nil {
err = c.BindQuery(&params)
@@ -48,13 +49,23 @@ func handlePartialUploadsCreateRequest(c *gin.Context) {
userID := authenticator.GetAuth(c).UserID()
f := authenticator.GetFileSystem(c)
uploadID, err := f.CreatePartialUpload(userID, params.Size, params.SHA256)
var id uuid.UUID
if params.ID != "" {
id, err = uuid.Parse(params.ID)
if err != nil {
panic(err)
}
} else {
id, _ = uuid.NewV7()
}
err = f.PartialUploadCreate(userID, id, params.Size, params.SHA256)
if err != nil {
panic(err)
}
c.JSON(200, gin.H{
"upload_id": uploadID,
"id": id,
})
}
@@ -75,7 +86,7 @@ func handlePartialUploadsQueryRequest(c *gin.Context) {
userID := authenticator.GetAuth(c).UserID()
f := authenticator.GetFileSystem(c)
size, uploaded, err := f.QueryPartialUpload(userID, id)
size, uploaded, err := f.PartialUploadQuery(userID, id)
if err != nil {
panic(err)
}
@@ -102,6 +113,7 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
}
f := authenticator.GetFileSystem(c)
offset := params.Offset
err = func() error {
file, err := c.FormFile("contents")
if err != nil {
@@ -117,15 +129,16 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
}
defer src.Close()
out, err := f.OpenPartialUpload(authenticator.GetAuth(c).UserID(), id, params.Offset)
out, err := f.PartialUploadOpenWrite(authenticator.GetAuth(c).UserID(), id, offset)
if err != nil {
return err
}
if _, err := io.Copy(out, src); err != nil {
if written, err := io.Copy(out, src); err != nil {
out.Close()
return err
} else {
offset += written
return out.Close()
}
}()
@@ -133,7 +146,9 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
panic(err)
}
c.Status(200)
c.JSON(200, gin.H{
"offset": offset,
})
}
func handlePartialUploadsFinalizeRequest(c *gin.Context) {
@@ -168,7 +183,7 @@ func handlePartialUploadsFinalizeRequest(c *gin.Context) {
f := authenticator.GetFileSystem(c)
err = func() error {
// TODO: #perf disk I/O in tx
src, err := f.ReadPartialUpload(authenticator.GetAuth(c).UserID(), uploadID)
src, err := f.PartialUploadOpenRead(authenticator.GetAuth(c).UserID(), uploadID)
if err != nil {
return err
}

View File

@@ -58,4 +58,5 @@ var (
ErrUploadChecksumMismatch = NewError(http.StatusBadRequest, "upload_checksum_mismatch", "Upload Checksum Mismatch")
ErrUploadOffsetExceeded = NewError(http.StatusBadRequest, "upload_offset_exceeded", "Upload Offset Exceeded")
ErrUploadIncomplete = NewError(http.StatusBadRequest, "upload_incomplete", "Upload Incomplete")
ErrUploadNotFound = NewError(http.StatusBadRequest, "upload_not_found", "Upload Not Found")
)

View File

@@ -16,11 +16,9 @@ import (
"github.com/jackc/pgx/v5"
)
func (f *FileSystem) CreatePartialUpload(user int32, size int64, checksum string) (uuid.UUID, error) {
id, _ := uuid.NewV7()
func (f *FileSystem) PartialUploadCreate(user int32, id uuid.UUID, size int64, checksum string) error {
path := uploadFilePath(user, id)
return id, f.db.RunInTx(func(db db.TxHandler) error {
return f.db.RunInTx(func(db db.TxHandler) error {
if err := insertPartialUpload(db, id, user, size, checksum); err != nil {
return err
}
@@ -32,13 +30,16 @@ func (f *FileSystem) CreatePartialUpload(user int32, size int64, checksum string
})
}
func (f *FileSystem) OpenPartialUpload(user int32, id uuid.UUID, offset int64) (io.WriteCloser, error) {
func (f *FileSystem) PartialUploadOpenWrite(user int32, id uuid.UUID, offset int64) (io.WriteCloser, error) {
owner, size, uploaded, checksum, err := queryPartialUpload(f.db, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
err = ErrUploadNotFound
}
return nil, err
}
if user != owner {
return nil, ErrResourceNotFound
return nil, ErrUploadNotFound
}
path := uploadFilePath(user, id)
@@ -71,14 +72,17 @@ func (f *FileSystem) OpenPartialUpload(user int32, id uuid.UUID, offset int64) (
return c, nil
}
func (f *FileSystem) QueryPartialUpload(user int32, id uuid.UUID) (size, uploaded int64, err error) {
func (f *FileSystem) PartialUploadQuery(user int32, id uuid.UUID) (size, uploaded int64, err error) {
var owner int32
owner, size, uploaded, _, err = queryPartialUpload(f.db, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
err = ErrUploadNotFound
}
return
}
if user != owner {
err = ErrResourceNotFound
err = ErrUploadNotFound
return
}
@@ -99,13 +103,16 @@ func (f *FileSystem) QueryPartialUpload(user int32, id uuid.UUID) (size, uploade
return
}
func (f *FileSystem) ReadPartialUpload(user int32, id uuid.UUID) (io.ReadCloser, error) {
func (f *FileSystem) PartialUploadOpenRead(user int32, id uuid.UUID) (io.ReadCloser, error) {
owner, size, uploaded, _, err := queryPartialUpload(f.db, id)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrUploadNotFound
}
return nil, err
}
if user != owner {
return nil, ErrResourceNotFound
return nil, ErrUploadNotFound
}
path := uploadFilePath(user, id)