1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-09 17:44:59 +09:00

WIP block service and object creation

This commit is contained in:
mcrakhman 2023-04-25 18:17:59 +02:00 committed by Mikhail Iudin
parent 8e229ee968
commit 33fdf3eb4c
No known key found for this signature in database
GPG key ID: FAAAA8BAABDFF1C0
9 changed files with 255 additions and 239 deletions

View file

@ -643,7 +643,7 @@ func (s *Service) ModifyLocalDetails(
// we set pending details if object is not in cache
// we do this under lock to prevent races if the object is created in parallel
// because in that case we can lose changes
err = s.cache.DoLockedIfNotExists(objectId, func() error {
err = s.cache.ObjectCache().DoLockedIfNotExists(objectId, func() error {
objectDetails, err := s.objectStore.GetPendingLocalDetails(objectId)
if err != nil && err != ds.ErrNotFound {
return err

View file

@ -231,9 +231,11 @@ func (sb *smartBlock) Init(ctx *InitContext) (err error) {
}
sb.source = ctx.Source
// TODO: [MR] rewrite this so it would be obvious
// we are doing this because we expecting cache to have objectTrees inside for smartblocks
sb.ObjectTree = sb.source.(objecttree.ObjectTree)
if _, ok := sb.source.(objecttree.ObjectTree); ok {
// TODO: [MR] rewrite this so it would be obvious
// we are doing this because we expecting cache to have objectTrees inside for smartblocks
sb.ObjectTree = sb.source.(objecttree.ObjectTree)
}
sb.undo = undo.NewHistory(0)
sb.restrictions = ctx.App.MustComponent(restriction.CName).(restriction.Service).RestrictionsByObj(sb)
sb.relationService = ctx.App.MustComponent(relation2.CName).(relation2.Service)

View file

@ -3,6 +3,7 @@ package object
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/space/clientcache"
"time"
"github.com/gogo/protobuf/types"
@ -21,7 +22,6 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
coresb "github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/objectstore"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
@ -74,9 +74,10 @@ func (c *Creator) Name() (name string) {
// TODO Temporarily
type BlockService interface {
StateFromTemplate(templateID, name string) (st *state.State, err error)
Cache() clientcache.Cache
}
func (c *Creator) CreateSmartBlockFromTemplate(ctx context.Context, sbType coresb.SmartBlockType, details *types.Struct, templateID string) (id string, newDetails *types.Struct, err error) {
func (c *Creator) CreateSmartBlockFromTemplate(ctx context.Context, sbType coresb.SmartBlockType, details *types.Struct, relationIds []string, templateID string) (id string, newDetails *types.Struct, err error) {
var createState *state.State
if templateID != "" {
if createState, err = c.blockService.StateFromTemplate(templateID, pbtypes.GetString(details, bundle.RelationKeyName.String())); err != nil {
@ -85,12 +86,10 @@ func (c *Creator) CreateSmartBlockFromTemplate(ctx context.Context, sbType cores
} else {
createState = state.NewDoc("", nil).NewState()
}
return c.CreateSmartBlockFromState(ctx, sbType, details, createState)
return c.CreateSmartBlockFromState(ctx, sbType, details, relationIds, createState)
}
// CreateSmartBlockFromState create new object from the provided `createState` and `details`. If you pass `details` into the function, it will automatically add missing relationLinks and override the details from the `createState`
// It will return error if some of the relation keys in `details` not installed in the workspace.
func (c *Creator) CreateSmartBlockFromState(ctx context.Context, sbType coresb.SmartBlockType, details *types.Struct, createState *state.State) (id string, newDetails *types.Struct, err error) {
func (c *Creator) CreateSmartBlockFromState(ctx context.Context, sbType coresb.SmartBlockType, details *types.Struct, relationIds []string, createState *state.State) (id string, newDetails *types.Struct, err error) {
if createState == nil {
createState = state.NewDoc("", nil).(*state.State)
}
@ -110,18 +109,11 @@ func (c *Creator) CreateSmartBlockFromState(ctx context.Context, sbType coresb.S
}
}
var relationKeys []string
var workspaceID string
if details != nil && details.Fields != nil {
for k, v := range details.Fields {
relId := addr.RelationKeyToIdPrefix + k
if _, err2 := c.objectStore.GetRelationById(relId); err != nil {
// check if installed
err = fmt.Errorf("failed to get installed relation %s: %w", relId, err2)
return
}
relationKeys = append(relationKeys, k)
createState.SetDetail(k, v)
// TODO: add relations to relationIds
}
detailsWorkspaceID := details.Fields[bundle.RelationKeyWorkspaceId.String()]
@ -132,6 +124,7 @@ func (c *Creator) CreateSmartBlockFromState(ctx context.Context, sbType coresb.S
// if we don't have anything in details then check the object store
if workspaceID == "" {
// TODO: [MR] think about predefined ids, how should we create them in current circumstances
workspaceID = c.anytype.PredefinedBlocks().Account
}
@ -141,48 +134,44 @@ func (c *Creator) CreateSmartBlockFromState(ctx context.Context, sbType coresb.S
createState.SetDetailAndBundledRelation(bundle.RelationKeyCreatedDate, pbtypes.Int64(time.Now().Unix()))
createState.SetDetailAndBundledRelation(bundle.RelationKeyCreator, pbtypes.String(c.anytype.ProfileID()))
var tid = thread.Undef
id = pbtypes.GetString(createState.CombinedDetails(), bundle.RelationKeyId.String())
sbt, _ := coresb.SmartBlockTypeFromID(id)
if sbt == coresb.SmartBlockTypeSubObject {
return c.CreateSubObjectInWorkspace(createState.CombinedDetails(), workspaceID)
} else if id != "" {
tid, err = thread.Decode(id)
if err != nil {
log.Errorf("failed to decode thread id from the state: %s", err.Error())
}
}
ev := &metrics.CreateObjectEvent{
SetDetailsMs: time.Since(startTime).Milliseconds(),
}
ctx = context.WithValue(ctx, eventCreate, ev)
// TODO: [MR] what should we do here with our current object creation?
// in which situation we call this condition
//if raw := pbtypes.GetString(createState.CombinedDetails(), bundle.RelationKeyId.String()); raw != "" {
// tid, err = thread.Decode(raw)
// if err != nil {
// log.Errorf("failed to decode thread id from the state: %s", err.Error())
// }
//}
//csm, err := c.CreateObjectInWorkspace(ctx, workspaceID, tid, sbType)
//if err != nil {
// err = fmt.Errorf("anytype.CreateBlock error: %v", err)
// return
//}
cache := c.blockService.Cache()
sb, release, err := cache.CreateTreeObject(sbType, func(id string) *smartblock.InitContext {
createState.SetRootId(id)
createState.SetObjectTypes(objectTypes)
createState.InjectDerivedDetails()
csm, err := c.CreateObjectInWorkspace(ctx, workspaceID, tid, sbType)
return &smartblock.InitContext{
ObjectTypeUrls: objectTypes,
State: createState,
RelationIds: relationIds,
}
})
if err != nil {
err = fmt.Errorf("anytype.CreateBlock error: %v", err)
return
}
id = csm.ID()
createState.SetRootId(id)
createState.SetObjectTypes(objectTypes)
createState.InjectDerivedDetails()
initCtx := &smartblock.InitContext{
ObjectTypeUrls: objectTypes,
State: createState,
RelationKeys: relationKeys,
}
var sb smartblock.SmartBlock
if sb, err = c.objectFactory.InitObject(id, initCtx); err != nil {
return id, nil, err
}
defer release()
ev.SmartblockCreateMs = time.Since(startTime).Milliseconds() - ev.SetDetailsMs - ev.WorkspaceCreateMs - ev.GetWorkspaceBlockWaitMs
ev.SmartblockType = int(sbType)
ev.ObjectId = id
metrics.SharedClient.RecordEvent(*ev)
return id, sb.CombinedDetails(), sb.Close()
return id, sb.CombinedDetails(), nil
}
// todo: rewrite with options
@ -231,29 +220,26 @@ func (c *Creator) CreateSet(req *pb.RpcObjectCreateSetRequest) (setID string, ne
var dvContent model.BlockContentOfDataview
var dvSchema schema.Schema
// TODO remove it, when schema will be refactored
source := req.Source
if len(req.Source) > 0 {
req.Details.Fields[bundle.RelationKeySetOf.String()] = pbtypes.StringList(req.Source)
}
if len(source) == 0 {
source = []string{bundle.TypeKeyPage.URL()}
}
if dvContent, dvSchema, err = dataview.DataviewBlockBySource(c.objectStore, source); err != nil {
return
if len(req.Source) != 0 {
if dvContent, dvSchema, err = dataview.DataviewBlockBySource(c.objectStore, req.Source); err != nil {
return
}
}
newState := state.NewDoc("", nil).NewState()
name := pbtypes.GetString(req.Details, bundle.RelationKeyName.String())
icon := pbtypes.GetString(req.Details, bundle.RelationKeyIconEmoji.String())
tmpls := []template.StateTransformer{
template.WithForcedDetail(bundle.RelationKeyName, pbtypes.String(name)),
template.WithForcedDetail(bundle.RelationKeyIconEmoji, pbtypes.String(icon)),
template.WithRequiredRelations(),
}
var blockContent *model.BlockContentOfDataview
if dvSchema != nil {
blockContent = &dvContent
}
if blockContent != nil {
for i, view := range blockContent.Dataview.Views {
if view.Relations == nil {
@ -261,6 +247,7 @@ func (c *Creator) CreateSet(req *pb.RpcObjectCreateSetRequest) (setID string, ne
}
}
tmpls = append(tmpls,
template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList(blockContent.Dataview.Source)),
template.WithDataview(*blockContent, false),
)
}
@ -270,7 +257,7 @@ func (c *Creator) CreateSet(req *pb.RpcObjectCreateSetRequest) (setID string, ne
}
// TODO: here can be a deadlock if this is somehow created from workspace (as set)
return c.CreateSmartBlockFromState(context.TODO(), coresb.SmartBlockTypeSet, req.Details, newState)
return c.CreateSmartBlockFromState(context.TODO(), coresb.SmartBlockTypeSet, nil, nil, newState)
}
// TODO: it must be in another component
@ -299,19 +286,11 @@ func (c *Creator) CreateSubObjectsInWorkspace(details []*types.Struct) (ids []st
// ObjectCreateBookmark creates a new Bookmark object for provided URL or returns id of existing one
func (c *Creator) ObjectCreateBookmark(req *pb.RpcObjectCreateBookmarkRequest) (objectID string, newDetails *types.Struct, err error) {
source := pbtypes.GetString(req.Details, bundle.RelationKeySource.String())
var res bookmark.ContentFuture
if source != "" {
u, err := uri.NormalizeURI(source)
if err != nil {
return "", nil, fmt.Errorf("process uri: %w", err)
}
res = c.bookmark.FetchBookmarkContent(u)
} else {
res = func() *model.BlockContentBookmark {
return nil
}
u, err := uri.ProcessURI(pbtypes.GetString(req.Details, bundle.RelationKeySource.String()))
if err != nil {
return "", nil, fmt.Errorf("process uri: %w", err)
}
res := c.bookmark.FetchBookmarkContent(u)
return c.bookmark.CreateBookmarkObject(req.Details, res)
}
@ -327,48 +306,46 @@ func (c *Creator) CreateObject(req block.DetailsGetter, forcedType bundle.TypeKe
details = internalflag.PutToDetails(details, internalFlags)
}
var templateID string
if v, ok := req.(block.TemplateIDGetter); ok {
templateID = v.GetTemplateId()
objectType, err := bundle.TypeKeyFromUrl(pbtypes.GetString(details, bundle.RelationKeyType.String()))
if err != nil && forcedType == "" {
return "", nil, fmt.Errorf("invalid type in details: %w", err)
}
var objectType string
if forcedType != "" {
objectType = forcedType.URL()
} else if objectType = pbtypes.GetString(details, bundle.RelationKeyType.String()); objectType == "" {
return "", nil, fmt.Errorf("missing type in details or in forcedType")
objectType = forcedType
details.Fields[bundle.RelationKeyType.String()] = pbtypes.String(objectType.URL())
}
details.Fields[bundle.RelationKeyType.String()] = pbtypes.String(objectType)
var sbType = coresb.SmartBlockTypePage
switch objectType {
case bundle.TypeKeyBookmark.URL():
case bundle.TypeKeyBookmark:
return c.ObjectCreateBookmark(&pb.RpcObjectCreateBookmarkRequest{
Details: details,
})
case bundle.TypeKeySet.URL():
details.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Float64(float64(model.ObjectType_set))
case bundle.TypeKeySet:
return c.CreateSet(&pb.RpcObjectCreateSetRequest{
Details: details,
InternalFlags: internalFlags,
Source: pbtypes.GetStringList(details, bundle.RelationKeySetOf.String()),
})
case bundle.TypeKeyObjectType.URL():
case bundle.TypeKeyObjectType:
details.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Float64(float64(model.ObjectType_objectType))
return c.CreateSubObjectInWorkspace(details, c.anytype.PredefinedBlocks().Account)
case bundle.TypeKeyRelation.URL():
case bundle.TypeKeyRelation:
details.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Float64(float64(model.ObjectType_relation))
return c.CreateSubObjectInWorkspace(details, c.anytype.PredefinedBlocks().Account)
case bundle.TypeKeyRelationOption.URL():
case bundle.TypeKeyRelationOption:
details.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Float64(float64(model.ObjectType_relationOption))
return c.CreateSubObjectInWorkspace(details, c.anytype.PredefinedBlocks().Account)
case bundle.TypeKeyTemplate.URL():
case bundle.TypeKeyTemplate:
sbType = coresb.SmartBlockTypeTemplate
}
return c.CreateSmartBlockFromTemplate(context.TODO(), sbType, details, templateID)
var templateID string
if v, ok := req.(block.TemplateIDGetter); ok {
templateID = v.GetTemplateId()
}
return c.CreateSmartBlockFromTemplate(context.TODO(), sbType, details, nil, templateID)
}

View file

@ -5,6 +5,8 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/object/treegetter"
"github.com/anytypeio/go-anytype-middleware/space/clientcache"
"net/url"
"strings"
"time"
@ -13,7 +15,6 @@ import (
"github.com/gogo/protobuf/types"
"github.com/hashicorp/go-multierror"
"github.com/ipfs/go-datastore/query"
"github.com/textileio/go-threads/core/thread"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/ocache"
@ -124,11 +125,11 @@ type Service struct {
doc doc.Service
app *app.App
source source.Service
cache ocache.OCache
objectStore objectstore.ObjectStore
restriction restriction.Service
bookmark bookmarksvc.Service
relationService relation.Service
cache clientcache.Cache
objectCreator objectCreator
objectFactory *editor.ObjectFactory
@ -152,8 +153,8 @@ func (s *Service) Init(a *app.App) (err error) {
s.relationService = a.MustComponent(relation.CName).(relation.Service)
s.objectCreator = a.MustComponent("objectCreator").(objectCreator)
s.objectFactory = app.MustComponent[*editor.ObjectFactory](a)
s.cache = a.MustComponent(treegetter.CName).(clientcache.Cache)
s.app = a
s.cache = ocache.New(s.loadSmartblock)
return
}
@ -224,7 +225,7 @@ func (s *Service) OpenBlock(
ctx *session.Context, id string, includeRelationsAsDependentObjects bool,
) (obj *model.ObjectView, err error) {
startTime := time.Now()
ob, err := s.getSmartblock(context.WithValue(context.TODO(), metrics.CtxKeyRequest, "object_open"), id)
ob, release, err := s.getSmartblock(context.WithValue(context.TODO(), metrics.CtxKeyRequest, "object_open"), id)
if err != nil {
return nil, err
}
@ -232,11 +233,11 @@ func (s *Service) OpenBlock(
ob.EnabledRelationAsDependentObjects()
}
afterSmartBlockTime := time.Now()
defer s.cache.Release(id)
defer release()
ob.Lock()
defer ob.Unlock()
ob.SetEventFunc(s.sendEvent)
if v, hasOpenListner := ob.SmartBlock.(smartblock.SmartObjectOpenListner); hasOpenListner {
if v, hasOpenListner := ob.(smartblock.SmartObjectOpenListner); hasOpenListner {
v.SmartObjectOpened(ctx)
}
afterDataviewTime := time.Now()
@ -251,23 +252,24 @@ func (s *Service) OpenBlock(
return
}
afterShowTime := time.Now()
if tid := ob.threadId; tid != thread.Undef && s.status != nil {
var (
fList = func() []string {
ob.Lock()
defer ob.Unlock()
bs := ob.NewState()
return bs.GetAllFileHashes(ob.FileRelationKeys(bs))
}
)
if newWatcher := s.status.Watch(tid, fList); newWatcher {
ob.AddHook(func(_ smartblock.ApplyInfo) error {
s.status.Unwatch(tid)
return nil
}, smartblock.HookOnClose)
}
}
// TODO: [MR] add status logic somewhere
//if tid := ob.threadId; tid != thread.Undef && s.status != nil {
// var (
// fList = func() []string {
// ob.Lock()
// defer ob.Unlock()
// bs := ob.NewState()
// return bs.GetAllFileHashes(ob.FileRelationKeys(bs))
// }
// )
//
// if newWatcher := s.status.Watch(tid, fList); newWatcher {
// ob.AddHook(func(_ smartblock.ApplyInfo) error {
// s.status.Unwatch(tid)
// return nil
// }, smartblock.HookOnClose)
// }
//}
afterHashesTime := time.Now()
tp, _ := coresb.SmartBlockTypeFromID(id)
metrics.SharedClient.RecordEvent(metrics.OpenBlockEvent{
@ -307,17 +309,14 @@ func (s *Service) OpenBreadcrumbsBlock(ctx *session.Context) (obj *model.ObjectV
}); err != nil {
return
}
bs.Lock()
defer bs.Unlock()
bs.SetEventFunc(s.sendEvent)
ob := newOpenedBlock(bs)
s.cache.Add(bs.Id(), ob)
// workaround to increase ref counter
if _, err = s.cache.Get(context.Background(), bs.Id()); err != nil {
_, _, err = s.cache.PutObject(bs.Id(), bs)
if err != nil {
return
}
bs.Lock()
defer bs.Unlock()
bs.SetEventFunc(s.sendEvent)
obj, err = bs.Show(ctx)
if err != nil {
return
@ -326,15 +325,12 @@ func (s *Service) OpenBreadcrumbsBlock(ctx *session.Context) (obj *model.ObjectV
}
func (s *Service) CloseBlock(id string) error {
var (
isDraft bool
workspaceId string
)
var isDraft bool
err := s.Do(id, func(b smartblock.SmartBlock) error {
b.ObjectClose()
s := b.NewState()
isDraft = internalflag.NewFromState(s).Has(model.InternalFlag_editorDeleteEmpty)
workspaceId = pbtypes.GetString(s.LocalDetails(), bundle.RelationKeyWorkspaceId.String())
//workspaceId = pbtypes.GetString(s.LocalDetails(), bundle.RelationKeyWorkspaceId.String())
return nil
})
if err != nil {
@ -342,23 +338,30 @@ func (s *Service) CloseBlock(id string) error {
}
if isDraft {
_, _ = s.cache.Remove(id)
if err = s.DeleteObjectFromWorkspace(workspaceId, id); err != nil {
if err = s.cache.DeleteObject(id); err != nil {
log.Errorf("error while block delete: %v", err)
} else {
s.sendOnRemoveEvent(id)
}
//
//_, _ = s.cache.Remove(id)
//if err = s.DeleteObjectFromWorkspace(workspaceId, id); err != nil {
// log.Errorf("error while block delete: %v", err)
//} else {
// s.sendOnRemoveEvent(id)
//}
}
return nil
}
func (s *Service) CloseBlocks() {
s.cache.ForEach(func(v ocache.Object) (isContinue bool) {
ob := v.(*openedBlock)
// TODO: [MR] provide maybe new logic in clientcache to call ObjectClose()
s.cache.ObjectCache().ForEach(func(v ocache.Object) (isContinue bool) {
ob := v.(smartblock.SmartBlock)
ob.Lock()
ob.ObjectClose()
ob.Unlock()
s.cache.Reset(ob.Id())
s.cache.ObjectCache().Reset(ob.Id())
return true
})
}
@ -718,38 +721,43 @@ func (s *Service) AddCreatorInfoIfNeeded(workspaceId string) error {
}
func (s *Service) DeleteObject(id string) (err error) {
var (
fileHashes []string
workspaceId string
isFavorite bool
)
// TODO: [MR] Fix deletion logic
err = s.Do(id, func(b smartblock.SmartBlock) error {
if err = b.Restrictions().Object.Check(model.Restrictions_Delete); err != nil {
return err
}
b.ObjectClose()
st := b.NewState()
fileHashes = st.GetAllFileHashes(b.FileRelationKeys(st))
workspaceId, err = s.anytype.GetWorkspaceIdForObject(id)
if workspaceId == "" {
workspaceId = s.anytype.PredefinedBlocks().Account
}
isFavorite = pbtypes.GetBool(st.LocalDetails(), bundle.RelationKeyIsFavorite.String())
if isFavorite {
_ = s.SetPageIsFavorite(pb.RpcObjectSetIsFavoriteRequest{IsFavorite: false, ContextId: id})
}
if err = s.DeleteObjectFromWorkspace(workspaceId, id); err != nil {
return err
}
b.SetIsDeleted()
return nil
})
if err != nil {
return
}
return s.cache.DeleteObject(id)
}
if err != nil && err != ErrBlockNotFound {
func (s *Service) OnDelete(b smartblock.SmartBlock) (err error) {
var (
fileHashes []string
workspaceId string
isFavorite bool
id = b.Id()
)
b.ObjectClose()
st := b.NewState()
fileHashes = st.GetAllFileHashes(b.FileRelationKeys(st))
workspaceId, err = s.anytype.GetWorkspaceIdForObject(id)
if workspaceId == "" {
workspaceId = s.anytype.PredefinedBlocks().Account
}
isFavorite = pbtypes.GetBool(st.LocalDetails(), bundle.RelationKeyIsFavorite.String())
if isFavorite {
_ = s.SetPageIsFavorite(pb.RpcObjectSetIsFavoriteRequest{IsFavorite: false, ContextId: id})
}
if err = s.DeleteObjectFromWorkspace(workspaceId, id); err != nil {
return err
}
_, _ = s.cache.Remove(id)
b.SetIsDeleted()
for _, fileHash := range fileHashes {
inboundLinks, err := s.Anytype().ObjectStore().GetOutboundLinksById(fileHash)
@ -856,33 +864,17 @@ func (s *Service) ProcessCancel(id string) (err error) {
}
func (s *Service) Close(ctx context.Context) (err error) {
return s.cache.Close()
//return s.cache.Close()
return
}
// PickBlock returns opened smartBlock or opens smartBlock in silent mode
func (s *Service) PickBlock(ctx context.Context, id string) (sb smartblock.SmartBlock, release func(), err error) {
ob, err := s.getSmartblock(ctx, id)
if err != nil {
return
}
return ob.SmartBlock, func() {
s.cache.Release(id)
}, nil
return s.getSmartblock(ctx, id)
}
func (s *Service) getSmartblock(ctx context.Context, id string) (ob *openedBlock, err error) {
val, err := s.cache.Get(ctx, id)
if err != nil {
return
}
var ok bool
ob, ok = val.(*openedBlock)
if !ok {
return nil, fmt.Errorf("got unexpected object from cache: %t", val)
} else if ob == nil {
return nil, fmt.Errorf("got nil object from cache")
}
return ob, nil
func (s *Service) getSmartblock(ctx context.Context, id string) (sb smartblock.SmartBlock, release func(), err error) {
return s.cache.GetObject(ctx, id)
}
func (s *Service) StateFromTemplate(templateID string, name string) (st *state.State, err error) {
@ -1223,38 +1215,38 @@ func (s *Service) ObjectToBookmark(id string, url string) (objectId string, err
}
func (s *Service) loadSmartblock(ctx context.Context, id string) (value ocache.Object, err error) {
sbt, _ := coresb.SmartBlockTypeFromID(id)
if sbt == coresb.SmartBlockTypeSubObject {
workspaceId := s.anytype.PredefinedBlocks().Account
if value, err = s.cache.Get(ctx, workspaceId); err != nil {
return
}
var ok bool
var ob *openedBlock
if ob, ok = value.(*openedBlock); !ok {
return nil, fmt.Errorf("invalid id path '%s': '%s' not implement openedBlock", id, workspaceId)
}
var sbOpener SmartblockOpener
if sbOpener, ok = ob.SmartBlock.(SmartblockOpener); !ok {
return nil, fmt.Errorf("invalid id path '%s': '%s' not implement SmartblockOpener", id, workspaceId)
}
var sb smartblock.SmartBlock
if sb, err = sbOpener.Open(id); err != nil {
return
}
return newOpenedBlock(sb), nil
}
sb, err := s.objectFactory.InitObject(id, &smartblock.InitContext{
Ctx: ctx,
})
if err != nil {
return
}
value = newOpenedBlock(sb)
//sbt, _ := coresb.SmartBlockTypeFromID(id)
//if sbt == coresb.SmartBlockTypeSubObject {
// workspaceId := s.anytype.PredefinedBlocks().Account
// if value, err = s.cache.Get(ctx, workspaceId); err != nil {
// return
// }
//
// var ok bool
// var ob *openedBlock
// if ob, ok = value.(*openedBlock); !ok {
// return nil, fmt.Errorf("invalid id path '%s': '%s' not implement openedBlock", id, workspaceId)
// }
//
// var sbOpener SmartblockOpener
// if sbOpener, ok = ob.SmartBlock.(SmartblockOpener); !ok {
// return nil, fmt.Errorf("invalid id path '%s': '%s' not implement SmartblockOpener", id, workspaceId)
// }
//
// var sb smartblock.SmartBlock
// if sb, err = sbOpener.Open(id); err != nil {
// return
// }
// return newOpenedBlock(sb), nil
//}
//
//sb, err := s.objectFactory.InitObject(id, &smartblock.InitContext{
// Ctx: ctx,
//})
//if err != nil {
// return
//}
//value = newOpenedBlock(sb)
return
}

View file

@ -126,7 +126,7 @@ func (mw *Middleware) doAccountService(f func(a account.Service) error) (err err
// Stop stops the anytype node and HTTP gateway
func (mw *Middleware) stop() error {
if mw != nil && mw.app != nil {
err := mw.app.Close()
err := mw.app.Close(context.Background())
if err != nil {
log.Warnf("error while stop anytype: %v", err)
}

2
go.mod
View file

@ -102,7 +102,7 @@ require (
github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2 // indirect
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104133926-9b59e6029ddc // indirect
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104140756-02e92a0db0e4 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect

2
go.sum
View file

@ -90,6 +90,8 @@ github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-2023010
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230103175543-ac84febce98d/go.mod h1:2vafONFWOYl9XTv9vW5KzOmuqdxhC6PAlRfkT7TseZA=
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104133926-9b59e6029ddc h1:G5ojRhFsJ9Wi06yR2TNDa3K0m1MhPvsBlJKrpWbsl40=
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104133926-9b59e6029ddc/go.mod h1:2vafONFWOYl9XTv9vW5KzOmuqdxhC6PAlRfkT7TseZA=
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104140756-02e92a0db0e4 h1:AiohMyvPL8jlMTpHqwkbHOIXftsP1aA8Oe5Z46wCe1M=
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-20230104140756-02e92a0db0e4/go.mod h1:2vafONFWOYl9XTv9vW5KzOmuqdxhC6PAlRfkT7TseZA=
github.com/anytypeio/go-ds-badger3 v0.3.1-0.20221103102622-3233d4e13cb8 h1:LC9w0M0SbA5VuZeBtUdq+uR4mdjbJhxurNtovmRiOrU=
github.com/anytypeio/go-ds-badger3 v0.3.1-0.20221103102622-3233d4e13cb8/go.mod h1:R5tqrpzflXnpE1v91BNZrz62AVox66+rWM51LXNoNik=
github.com/anytypeio/go-gelf v0.0.0-20210418191311-774bd5b016e7 h1:YBmcug6mOdwjJcmzA/Fpjtl2oo78+fMlr5Xj0dzwg78=

View file

@ -12,8 +12,8 @@ import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/object/treegetter"
"github.com/anytypeio/go-anytype-middleware/core/block/editor"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
coresb "github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/space"
"go.uber.org/zap"
"time"
)
@ -27,51 +27,47 @@ const (
treeCreateKey
)
type treeCache struct {
type cache struct {
gcttl int
cache ocache.OCache
account accountservice.Service
clientService space.Service
objectFactory *editor.ObjectFactory
objectDeleter ObjectDeleter
}
type TreeCache interface {
type Cache interface {
treegetter.TreeGetter
// TODO: remove this
treegetter.TreePutter
GetObject(ctx context.Context, id string) (sb smartblock.SmartBlock, release func(), err error)
CreateTreeObject(tp coresb.SmartBlockType, initFunc InitFunc) (sb smartblock.SmartBlock, release func(), err error)
PutObject(id string, obj smartblock.SmartBlock) (sb smartblock.SmartBlock, release func(), err error)
DeleteObject(id string) (err error)
ObjectCache() ocache.OCache
}
type updateListener struct {
type ObjectDeleter interface {
OnDelete(b smartblock.SmartBlock) (err error)
}
func (u *updateListener) Update(tree objecttree.ObjectTree) {
log.With(
zap.Strings("heads", tree.Heads()),
zap.String("tree id", tree.Id())).
Debug("updating tree")
}
type InitFunc func(id string) *smartblock.InitContext
func (u *updateListener) Rebuild(tree objecttree.ObjectTree) {
log.With(
zap.Strings("heads", tree.Heads()),
zap.String("tree id", tree.Id())).
Debug("rebuilding tree")
}
func New(ttl int) TreeCache {
return &treeCache{
func New(ttl int) Cache {
return &cache{
gcttl: ttl,
}
}
func (c *treeCache) Run(ctx context.Context) (err error) {
func (c *cache) Run(ctx context.Context) (err error) {
return nil
}
func (c *treeCache) Close(ctx context.Context) (err error) {
func (c *cache) Close(ctx context.Context) (err error) {
return c.cache.Close()
}
func (c *treeCache) Init(a *app.App) (err error) {
func (c *cache) Init(a *app.App) (err error) {
c.clientService = a.MustComponent(space.CName).(space.Service)
c.account = a.MustComponent(accountservice.CName).(accountservice.Service)
c.objectFactory = app.MustComponent[*editor.ObjectFactory](a)
@ -85,7 +81,7 @@ func (c *treeCache) Init(a *app.App) (err error) {
// creating tree if needed
createPayload, exists := ctx.Value(treeCreateKey).(treestorage.TreeStorageCreatePayload)
if exists {
ot, err := spc.PutTree(ctx, createPayload, &updateListener{})
ot, err := spc.PutTree(ctx, createPayload, nil)
if err != nil {
return
}
@ -96,17 +92,30 @@ func (c *treeCache) Init(a *app.App) (err error) {
})
},
ocache.WithLogger(log.Sugar()),
ocache.WithRefCounter(true),
ocache.WithGCPeriod(time.Minute),
ocache.WithTTL(time.Duration(c.gcttl)*time.Second),
)
c.objectDeleter = app.MustComponent[ObjectDeleter](a)
return nil
}
func (c *treeCache) Name() (name string) {
func (c *cache) Name() (name string) {
return treegetter.CName
}
func (c *treeCache) GetTree(ctx context.Context, spaceId, id string) (tr objecttree.ObjectTree, err error) {
func (c *cache) GetObject(ctx context.Context, id string) (sb smartblock.SmartBlock, release func(), err error) {
ctx = context.WithValue(ctx, spaceKey, c.account)
v, err := c.cache.Get(ctx, id)
if err != nil {
return
}
return v.(smartblock.SmartBlock), func() {
c.cache.Release(id)
}, nil
}
func (c *cache) GetTree(ctx context.Context, spaceId, id string) (tr objecttree.ObjectTree, err error) {
ctx = context.WithValue(ctx, spaceKey, spaceId)
v, err := c.cache.Get(ctx, id)
if err != nil {
@ -115,7 +124,7 @@ func (c *treeCache) GetTree(ctx context.Context, spaceId, id string) (tr objectt
return v.(objecttree.ObjectTree), nil
}
func (c *treeCache) PutTree(ctx context.Context, spaceId string, payload treestorage.TreeStorageCreatePayload) (ot objecttree.ObjectTree, err error) {
func (c *cache) PutTree(ctx context.Context, spaceId string, payload treestorage.TreeStorageCreatePayload) (ot objecttree.ObjectTree, err error) {
ctx = context.WithValue(ctx, spaceKey, spaceId)
ctx = context.WithValue(ctx, treeCreateKey, payload)
v, err := c.cache.Get(ctx, payload.RootRawChange.Id)
@ -125,15 +134,40 @@ func (c *treeCache) PutTree(ctx context.Context, spaceId string, payload treesto
return v.(objecttree.ObjectTree), nil
}
func (c *treeCache) DeleteTree(ctx context.Context, spaceId, treeId string) (err error) {
tr, err := c.GetTree(ctx, spaceId, treeId)
func (c *cache) DeleteTree(ctx context.Context, spaceId, treeId string) (err error) {
tr, _, err := c.GetObject(ctx, treeId)
if err != nil {
return
}
err = tr.Delete()
err = c.objectDeleter.OnDelete(tr)
if err != nil {
return
}
err = tr.(objecttree.ObjectTree).Delete()
if err != nil {
return
}
_, err = c.cache.Remove(treeId)
return
}
func (c *cache) CreateTreeObject(tp coresb.SmartBlockType, initFunc InitFunc) (sb smartblock.SmartBlock, release func(), err error) {
// create tree payload
// put tree payload in context
// call get method with tree payload
// put
panic("not implemented")
}
func (c *cache) PutObject(id string, obj smartblock.SmartBlock) (sb smartblock.SmartBlock, release func(), err error) {
panic("not implemented")
}
func (c *cache) DeleteObject(id string) (err error) {
panic("not implemented")
}
func (c *cache) ObjectCache() ocache.OCache {
return c.cache
}

View file

@ -2,6 +2,7 @@ package space
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/accountservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/ocache"
@ -32,6 +33,7 @@ type service struct {
conf commonspace.Config
spaceCache ocache.OCache
commonSpace commonspace.SpaceService
account accountservice.Service
spaceStorageProvider spacestorage.SpaceStorageProvider
accountId string
}
@ -39,14 +41,21 @@ type service struct {
func (s *service) Init(a *app.App) (err error) {
s.conf = a.MustComponent("config").(commonspace.ConfigGetter).GetSpace()
s.commonSpace = a.MustComponent(commonspace.CName).(commonspace.SpaceService)
s.account = a.MustComponent(accountservice.CName).(accountservice.Service)
s.spaceStorageProvider = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorageProvider)
// TODO: add account id
s.spaceCache = ocache.New(
s.loadSpace,
ocache.WithLogger(log.Sugar()),
ocache.WithGCPeriod(time.Minute),
ocache.WithTTL(time.Duration(s.conf.GCTTL)*time.Second),
)
s.accountId, err = s.commonSpace.DeriveSpace(context.Background(), commonspace.SpaceDerivePayload{
SigningKey: s.account.Account().SignKey,
EncryptionKey: s.account.Account().EncKey,
})
if err != nil {
return
}
return spacesyncproto.DRPCRegisterSpaceSync(a.MustComponent(server.CName).(server.DRPCServer), &rpcHandler{s})
}