Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Delete Content #1692

Merged
merged 27 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e4ac2f4
wip(daemon): delete unreferenced entities
juligasa Apr 8, 2024
deca369
wip(daemon): prevent syncing back deleted documents
juligasa Apr 8, 2024
dd1f1cb
wip(daemon): list deleted documents
juligasa Apr 8, 2024
96007b6
wip(daemon): new delete entity
juligasa Apr 9, 2024
9664bfd
wip(daemos): tests
juligasa Apr 9, 2024
287a482
wip(daemon): tests
juligasa Apr 10, 2024
cc2d285
wip(daemon): proper testing
juligasa Apr 10, 2024
9115789
wiip(daemon): fix tests
juligasa Apr 11, 2024
c41c5a6
wip(daemon): update protos
juligasa Apr 11, 2024
50a64d8
wip(daemon): restore deleted entities
juligasa Apr 12, 2024
425e80c
wip(daemon): remove old deletePublication
juligasa Apr 15, 2024
345cbb7
wip(daemon): deletion with links tests
juligasa Apr 17, 2024
1ba3d47
wip(daemon): include comment tests
juligasa Apr 17, 2024
25628a6
fix(daemon): getting back comments
juligasa Apr 17, 2024
b18c286
Delete Dialog for all Entities
ericvicenti Apr 18, 2024
cf03427
fix(daemon): remove comments first
juligasa Apr 19, 2024
9cf6162
wip(daemon): list deleted entities test
juligasa Apr 22, 2024
6578f07
Improve delete workflow
ericvicenti Apr 22, 2024
fa3d17a
fix(deamon): lint
juligasa Apr 23, 2024
4786e0c
fix(deamon): remove unused params
juligasa Apr 23, 2024
9d0b0d4
fix(daemon): linting
juligasa Apr 23, 2024
bdb48b3
fix(daemon): rename + tests
juligasa Apr 23, 2024
e85f136
fix(daemon): not indexing deleted accounts
juligasa Apr 23, 2024
a77370f
wip(daemon): share connection
juligasa Apr 23, 2024
a423fa7
fix(daemon): remove nested connections
juligasa Apr 23, 2024
76fedc7
fix(daemon):lint
juligasa Apr 23, 2024
f852795
fix(daemon): remove non-unitary tests
juligasa Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions backend/daemon/api/documents/v1alpha/documents.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,21 +630,6 @@ func (api *Server) loadPublication(ctx context.Context, docid hyper.EntityID, ve
}, nil
}

// DeletePublication implements the corresponding gRPC method.
func (api *Server) DeletePublication(ctx context.Context, in *documents.DeletePublicationRequest) (*emptypb.Empty, error) {
if in.DocumentId == "" {
return nil, status.Errorf(codes.InvalidArgument, "must specify publication ID to delete")
}

eid := hyper.EntityID(in.DocumentId)

if err := api.blobs.DeleteEntity(ctx, eid); err != nil {
return nil, err
}

return &emptypb.Empty{}, nil
}

// PushPublication implements the corresponding gRPC method.
func (api *Server) PushPublication(ctx context.Context, in *documents.PushPublicationRequest) (*emptypb.Empty, error) {
if in.DocumentId == "" {
Expand Down
38 changes: 0 additions & 38 deletions backend/daemon/api/documents/v1alpha/documents_bugs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,44 +254,6 @@ func TestBug_MoveBockWithoutReplacement(t *testing.T) {
require.Len(t, dlist.Documents, 1)
}

func TestBug_MissingLinkTarget(t *testing.T) {
t.Parallel()

api := newTestDocsAPI(t, "alice")
ctx := context.Background()

draft, err := api.CreateDraft(ctx, &CreateDraftRequest{})
require.NoError(t, err)
updated := updateDraft(ctx, t, api, draft.Id, []*DocumentChange{
{Op: &DocumentChange_SetTitle{SetTitle: "My new document title"}},
{Op: &DocumentChange_MoveBlock_{MoveBlock: &DocumentChange_MoveBlock{BlockId: "b1"}}},
{Op: &DocumentChange_ReplaceBlock{ReplaceBlock: &Block{
Id: "b1",
Type: "statement",
Text: "Hello world!",
Annotations: []*Annotation{
{
Type: "link",
Attributes: map[string]string{
"url": "mtt://bafy2bzaceaemtzyq7gj6fa5jn4xhfq6yp657j5dpoqvh6bio4kk4bi2wmoroy/baeaxdiheaiqfsiervpfvbohhvjgnkcto3f5p4alwe4k46fr334vlw4n5jaknnqa/MIWneLC1",
},
Starts: []int32{0},
Ends: []int32{5},
},
},
}}},
})
require.NoError(t, err)
require.NotNil(t, updated)
published, err := api.PublishDraft(ctx, &PublishDraftRequest{DocumentId: draft.Id})
require.NoError(t, err)
require.NotNil(t, published)

linked, err := api.GetPublication(ctx, &GetPublicationRequest{DocumentId: "bafy2bzaceaemtzyq7gj6fa5jn4xhfq6yp657j5dpoqvh6bio4kk4bi2wmoroy"})
require.Error(t, err)
require.Nil(t, linked)
}

func TestBug_BlockRevisionMustUpdate(t *testing.T) {
t.Parallel()

Expand Down
36 changes: 0 additions & 36 deletions backend/daemon/api/documents/v1alpha/documents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -742,42 +742,6 @@ func TestGetPublicationWithDraftID(t *testing.T) {
require.Nil(t, published, "draft is not a publication")
}

func TestAPIDeletePublication(t *testing.T) {
api := newTestDocsAPI(t, "alice")
ctx := context.Background()

doc, err := api.CreateDraft(ctx, &documents.CreateDraftRequest{})
require.NoError(t, err)
doc = updateDraft(ctx, t, api, doc.Id, []*documents.DocumentChange{
{Op: &documents.DocumentChange_SetTitle{SetTitle: "My new document title"}}},
)

_, err = api.PublishDraft(ctx, &documents.PublishDraftRequest{DocumentId: doc.Id})
require.NoError(t, err)

list, err := api.ListPublications(ctx, &documents.ListPublicationsRequest{})
require.NoError(t, err)
require.Len(t, list.Publications, 1)

deleted, err := api.DeletePublication(ctx, &documents.DeletePublicationRequest{DocumentId: doc.Id})
require.NoError(t, err)
require.NotNil(t, deleted)

list, err = api.ListPublications(ctx, &documents.ListPublicationsRequest{})
require.NoError(t, err)
require.Len(t, list.Publications, 0)

pub, err := api.GetPublication(ctx, &documents.GetPublicationRequest{DocumentId: doc.Id})
require.Error(t, err, "must fail to get deleted publication")
_ = pub

// TODO: fix status codes.
// s, ok := status.FromError(err)
// require.True(t, ok)
// require.Nil(t, pub)
// require.Equal(t, codes.NotFound, s.Code())
}

func TestPublisherAndEditors(t *testing.T) {
t.Parallel()

Expand Down
161 changes: 156 additions & 5 deletions backend/daemon/api/entities/v1alpha/entities.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math"
"mintter/backend/core"
Expand All @@ -27,6 +28,7 @@ import (
"golang.org/x/exp/slices"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)

Expand Down Expand Up @@ -369,17 +371,13 @@ func (api *Server) SearchEntities(ctx context.Context, in *entities.SearchEntiti
var owners []string
const limit = 30
if err := api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
err := sqlitex.Exec(conn, qGetEntityTitles(), func(stmt *sqlite.Stmt) error {
return sqlitex.Exec(conn, qGetEntityTitles(), func(stmt *sqlite.Stmt) error {
titles = append(titles, stmt.ColumnText(0))
iris = append(iris, stmt.ColumnText(1))
ownerID := core.Principal(stmt.ColumnBytes(2)).String()
owners = append(owners, ownerID)
return nil
})
if err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
Expand All @@ -400,6 +398,159 @@ func (api *Server) SearchEntities(ctx context.Context, in *entities.SearchEntiti
return &entities.SearchEntitiesResponse{Entities: matchingEntities}, nil
}

// DeleteEntity implements the corresponding gRPC method.
func (api *Server) DeleteEntity(ctx context.Context, in *entities.DeleteEntityRequest) (*emptypb.Empty, error) {
var meta string
var qGetResourceMetadata = dqb.Str(`
SELECT meta from meta_view
WHERE iri = :eid
`)

if in.Id == "" {
return nil, status.Errorf(codes.InvalidArgument, "must specify entity ID to delete")
}

eid := hyper.EntityID(in.Id)

err := api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.Exec(conn, qGetResourceMetadata(), func(stmt *sqlite.Stmt) error {
meta = stmt.ColumnText(0)
return nil
}, in.Id)
})
if err != nil {
return nil, err
}
_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return api.blobs.ForEachComment(ctx, eid.String(), func(c cid.Cid, cmt hyper.Comment) error {
referencedDocument := strings.Split(cmt.Target, "?v=")[0]
if referencedDocument == eid.String() {
var qEmptyComments = dqb.Str(`
UPDATE blobs
SET data = NULL, size = -1
WHERE multihash = :hash
`)
_, err = hypersql.BlobsDelete(conn, c.Hash())
if err != nil {
_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.Exec(conn, qEmptyComments(), func(_ *sqlite.Stmt) error {
return nil
}, c.Hash())
})
if err != nil {
return err
}
juligasa marked this conversation as resolved.
Show resolved Hide resolved
}
if cmt.RepliedComment.String() != "" {
_, err = hypersql.BlobsDelete(conn, cmt.RepliedComment.Hash())
if err != nil {
_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.Exec(conn, qEmptyComments(), func(_ *sqlite.Stmt) error {
return nil
}, cmt.RepliedComment.Hash())
})
if err != nil {
return err
}
}
}

return nil
}
return nil
})
})
err = api.blobs.DeleteEntity(ctx, eid)
if err != nil {
if errors.Is(err, hyper.ErrEntityNotFound) {
return nil, err
}

var qEmptyBlobs = dqb.Str(`
UPDATE blobs
SET data = NULL, size = -1
WHERE id in (
SELECT blob_id from structural_blobs_view where resource = :eid
)
`)

_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.Exec(conn, qEmptyBlobs(), func(_ *sqlite.Stmt) error {
return nil
}, in.Id)
})
if err != nil {
return &emptypb.Empty{}, err
}
var qDeleteStructuralBlobs = dqb.Str(`
DELETE from structural_blobs
WHERE id in (
SELECT blob_id from structural_blobs_view where resource = :eid
)
`)
_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.Exec(conn, qDeleteStructuralBlobs(), func(_ *sqlite.Stmt) error {
return nil
}, in.Id)
})
if err != nil {
return &emptypb.Empty{}, err
}
}
_, err = &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return sqlitex.WithTx(conn, func() error {
res, err := hypersql.EntitiesInsertRemovedRecord(conn, eid.String(), in.Reason, meta)
if err != nil {
return err
}
if res.ResourceEID != eid.String() {
return fmt.Errorf("%w: %s", hyper.ErrEntityNotFound, eid)
}

return nil
})
})
return &emptypb.Empty{}, err
}

// RestoreEntity implements the corresponding gRPC method.
func (api *Server) RestoreEntity(ctx context.Context, in *entities.RestoreEntityRequest) (*emptypb.Empty, error) {
if in.Id == "" {
return nil, status.Errorf(codes.InvalidArgument, "must specify entity ID to restore")
}

eid := hyper.EntityID(in.Id)

return &emptypb.Empty{}, api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
return hypersql.EntitiesDeleteRemovedRecord(conn, eid.String())
})
}

// ListDeletedEntities implements the corresponding gRPC method.
func (api *Server) ListDeletedEntities(ctx context.Context, _ *entities.ListDeletedEntitiesRequest) (*entities.ListDeletedEntitiesResponse, error) {
resp := &entities.ListDeletedEntitiesResponse{
DeletedEntities: make([]*entities.DeletedEntity, 0),
}

err := api.blobs.Query(ctx, func(conn *sqlite.Conn) error {
list, err := hypersql.EntitiesListRemovedRecords(conn)
if err != nil {
return err
}
for _, entity := range list {
resp.DeletedEntities = append(resp.DeletedEntities, &entities.DeletedEntity{
Id: entity.DeletedResourcesIRI,
DeleteTime: &timestamppb.Timestamp{Seconds: entity.DeletedResourcesDeleteTime},
DeletedReason: entity.DeletedResourcesReason,
Metadata: entity.DeletedResourcesMeta,
})
}
return nil
})

return resp, err
}

var qGetEntityTitles = dqb.Str(`
SELECT meta, iri, principal
FROM meta_view;`)
Expand Down
Loading
Loading