1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-10 18:10:49 +09:00

GO-2990 Merge remote-tracking branch 'origin/main' into GO-2990-cancel-join

This commit is contained in:
mcrakhman 2024-03-01 14:13:00 +01:00
commit 8d99e1a829
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
62 changed files with 3065 additions and 90 deletions

1
.gitattributes vendored
View file

@ -1,2 +1,3 @@
lib/lib.so filter=lfs diff=lfs merge=lfs -text
dist/lib.so filter=lfs diff=lfs merge=lfs -text
tests/integration/testdata/import/** linguist-generated=true

View file

@ -80,6 +80,7 @@ test-deps:
@go build -o deps github.com/vektra/mockery/v2
@go generate ./...
@$(DEPS_PATH)/mockery --disable-version-string
@go run ./cmd/testcase generate-json-helpers
clear-test-deps:
@echo 'Removing test mocks...'

115
cmd/testcase/main.go Normal file
View file

@ -0,0 +1,115 @@
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/proto"
"github.com/anyproto/anytype-heart/pb"
)
func run() error {
if len(os.Args) == 1 {
return fmt.Errorf("select command: generate-json-helpers")
}
if os.Args[1] == "generate-json-helpers" {
return generateJsonHelpers()
}
return nil
}
func generateJsonHelpers() error {
rootPath := "./tests/integration/testdata/import"
entries, err := os.ReadDir(rootPath)
if err != nil {
return fmt.Errorf("read dir: %w", err)
}
for _, entry := range entries {
if entry.IsDir() {
err := generateJsonHelpersForImportCase(filepath.Join(rootPath, entry.Name()))
if err != nil {
return fmt.Errorf("generate json helpers for dir %s: %w", entry.Name(), err)
}
}
}
return nil
}
func generateJsonHelpersForImportCase(dir string) error {
entries, err := os.ReadDir(dir)
if err != nil {
return fmt.Errorf("read dir: %w", err)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
// Remove old json files
if filepath.Ext(entry.Name()) == ".txt" {
path := filepath.Join(dir, entry.Name())
fmt.Println("delete old json file: ", path)
err := os.Remove(path)
if err != nil {
return fmt.Errorf("remove file: %w", err)
}
}
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
if filepath.Ext(entry.Name()) == ".pb" {
err = generateJsonHelper(dir, entry.Name())
if err != nil {
return fmt.Errorf("generate helper: %w", err)
}
}
}
return nil
}
func generateJsonHelper(dir string, pbFileName string) error {
f, err := os.Open(filepath.Join(dir, pbFileName))
if err != nil {
return fmt.Errorf("open file: %w", err)
}
defer f.Close()
snapshot := &pb.SnapshotWithType{}
data, err := io.ReadAll(f)
if err != nil {
return fmt.Errorf("read pb file: %w", err)
}
err = proto.Unmarshal(data, snapshot)
if err != nil {
return fmt.Errorf("unmarshal pb: %w", err)
}
jsonFilePath := filepath.Join(dir, pbFileName+".txt")
jsonFile, err := os.Create(jsonFilePath)
if err != nil {
return fmt.Errorf("create file: %w", err)
}
defer jsonFile.Close()
marshaler := &jsonpb.Marshaler{Indent: " "}
err = marshaler.Marshal(jsonFile, snapshot)
if err != nil {
return fmt.Errorf("marshal to json: %w", err)
}
fmt.Println("created json file: ", jsonFilePath)
return nil
}
func main() {
err := run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

View file

@ -60,7 +60,7 @@ type (
analytics, validate, creator bool
list, removeRelations, exclude bool
collectCustomUsageInfo bool
path, rules string
path, rules, spaceDashboardId string
}
)
@ -164,6 +164,7 @@ func getFlags() (*cliFlags, error) {
rules := flag.String("rules", "", "Path to file with processing rules")
exclude := flag.Bool("exclude", false, "Exclude objects that did not pass validation")
custom := flag.Bool("c", false, "Collect usage information about custom types and relations")
spaceDashboardId := flag.String("s", "", "Id of object to be set as Space Dashboard")
flag.Parse()
@ -181,6 +182,7 @@ func getFlags() (*cliFlags, error) {
rules: *rules,
exclude: *exclude,
collectCustomUsageInfo: *custom,
spaceDashboardId: *spaceDashboardId,
}, nil
}
@ -201,7 +203,7 @@ func collectUseCaseInfo(files []*zip.File, fileName string) (info *useCaseInfo,
continue
}
if strings.HasPrefix(f.Name, "files/") {
if strings.HasPrefix(f.Name, "files") {
continue
}
@ -290,6 +292,7 @@ func processFiles(files []*zip.File, zw *zip.Writer, info *useCaseInfo, flags *c
if err != nil {
if !(flags.exclude && errors.Is(err, errValidationFailed)) {
// just do not include object that failed validation
fmt.Println(f.Name)
incorrectFileFound = true
}
continue
@ -298,9 +301,9 @@ func processFiles(files []*zip.File, zw *zip.Writer, info *useCaseInfo, flags *c
continue
}
newFileName := f.Name
if strings.HasPrefix(newFileName, ".pb.json") {
if strings.HasSuffix(newFileName, ".pb.json") {
// output of usecase validator is always an archive with protobufs
newFileName = strings.TrimPrefix(newFileName, ".json")
newFileName = strings.TrimSuffix(newFileName, ".json")
}
nf, err := zw.Create(newFileName)
if err != nil {
@ -319,10 +322,10 @@ func processFiles(files []*zip.File, zw *zip.Writer, info *useCaseInfo, flags *c
func processRawData(data []byte, name string, info *useCaseInfo, flags *cliFlags) ([]byte, error) {
if name == constant.ProfileFile {
return processProfile(data, info)
return processProfile(data, info, flags.spaceDashboardId)
}
if strings.HasPrefix(name, "files/") {
if strings.HasPrefix(name, "files") {
return data, nil
}
@ -441,7 +444,8 @@ func removeAccountRelatedDetails(s *pb.ChangeSnapshot) {
bundle.RelationKeyRelationFormatObjectTypes.String(),
bundle.RelationKeySourceFilePath.String(),
bundle.RelationKeyLinks.String(),
bundle.RelationKeyBacklinks.String():
bundle.RelationKeyBacklinks.String(),
bundle.RelationKeyWorkspaceId.String():
delete(s.Data.Details.Fields, key)
}
@ -453,7 +457,7 @@ func insertCreatorInfo(s *pb.ChangeSnapshot) {
s.Data.Details.Fields[bundle.RelationKeyLastModifiedBy.String()] = pbtypes.String(addr.AnytypeProfileId)
}
func processProfile(data []byte, info *useCaseInfo) ([]byte, error) {
func processProfile(data []byte, info *useCaseInfo, spaceDashboardId string) ([]byte, error) {
profile := &pb.Profile{}
if err := profile.Unmarshal(data); err != nil {
e := fmt.Errorf("cannot unmarshal profile: %w", err)
@ -462,6 +466,12 @@ func processProfile(data []byte, info *useCaseInfo) ([]byte, error) {
}
profile.Name = ""
profile.ProfileId = ""
if spaceDashboardId != "" {
profile.SpaceDashboardId = spaceDashboardId
return profile.Marshal()
}
fmt.Println("spaceDashboardId = " + profile.SpaceDashboardId)
if _, found := info.objects[profile.SpaceDashboardId]; !found {
err := fmt.Errorf("failed to find Space Dashboard object '%s' among provided", profile.SpaceDashboardId)

View file

@ -63,6 +63,13 @@ func validateRelationBlocks(s *pb.SnapshotWithType, _ *useCaseInfo) (err error)
relLinks := pbtypes.RelationLinks(s.Snapshot.Data.GetRelationLinks())
for _, rk := range relKeys {
if !relLinks.Has(rk) {
if rel, errFound := bundle.GetRelation(domain.RelationKey(rk)); errFound == nil {
s.Snapshot.Data.RelationLinks = append(s.Snapshot.Data.RelationLinks, &model.RelationLink{
Key: rk,
Format: rel.Format,
})
continue
}
err = multierror.Append(err, fmt.Errorf("relation '%v' exists in relation block but not in relation links of object %s", rk, id))
}
}
@ -84,6 +91,13 @@ func validateDetails(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
if e != nil {
rel = getRelationLinkByKey(s.Snapshot.Data.RelationLinks, k)
if rel == nil {
if relation, errFound := bundle.GetRelation(domain.RelationKey(k)); errFound == nil {
s.Snapshot.Data.RelationLinks = append(s.Snapshot.Data.RelationLinks, &model.RelationLink{
Key: k,
Format: relation.Format,
})
continue
}
err = multierror.Append(err, fmt.Errorf("relation '%s' exists in details of object '%s', but not in relation links", k, id))
continue
}

View file

@ -65,7 +65,9 @@ func ExtractCustomState(st *state.State) (userState *state.State, err error) {
// remove the identity block
newState.Unlink(identityBlockId)
newState.CleanupBlock(identityBlockId)
newState.SetObjectTypeKey(bundle.TypeKeyPage)
// now cleanup the original state
rootBlock := st.Get(st.RootId())
rootBlock.Model().ChildrenIds = slices.DeleteFunc(rootBlock.Model().ChildrenIds, func(s string) bool {
return !slices.Contains(whitelistBlocks, s)

View file

@ -904,6 +904,7 @@ func TestProfileMigrationExtractCustomState(t *testing.T) {
for k, _ := range originalState.Details().GetFields() {
require.Contains(t, whitelistedDetailKeys, k, "old state should not contain %s", k)
}
require.Equal(t, bundle.TypeKeyPage, extractedState.ObjectTypeKey())
_, err = ExtractCustomState(originalState.NewState())
require.ErrorIsf(t, err, ErrNoCustomStateFound, "should return error on the second time call")

View file

@ -10,6 +10,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"github.com/anyproto/any-sync/app"
"github.com/globalsign/mgo/bson"
@ -138,12 +139,14 @@ func (e *export) Export(ctx context.Context, req pb.RpcObjectListExportRequest)
succeed = e.exportGraphJson(ctx, req, docs, succeed, wr, queue)
} else {
tasks := make([]process.Task, 0, len(docs))
succeed, tasks = e.exportDocs(ctx, req, docs, wr, queue, succeed, tasks)
var succeedAsync int64
tasks = e.exportDocs(ctx, req, docs, wr, queue, &succeedAsync, tasks)
err := queue.Wait(tasks...)
if err != nil {
e.cleanupFile(wr)
return "", 0, err
}
succeed += int(succeedAsync)
}
if err = queue.Finalize(); err != nil {
e.cleanupFile(wr)
@ -156,19 +159,19 @@ func (e *export) Export(ctx context.Context, req pb.RpcObjectListExportRequest)
return wr.Path(), succeed, nil
}
func (e *export) exportDocs(ctx context.Context, req pb.RpcObjectListExportRequest, docs map[string]*types.Struct, wr writer, queue process.Queue, succeed int, tasks []process.Task) (int, []process.Task) {
func (e *export) exportDocs(ctx context.Context, req pb.RpcObjectListExportRequest, docs map[string]*types.Struct, wr writer, queue process.Queue, succeed *int64, tasks []process.Task) []process.Task {
for docId := range docs {
did := docId
task := func() {
if werr := e.writeDoc(ctx, req.Format, wr, docs, queue, did, req.IncludeFiles, req.IsJson); werr != nil {
log.With("objectID", did).Warnf("can't export doc: %v", werr)
} else {
succeed++
atomic.AddInt64(succeed, 1)
}
}
tasks = append(tasks, task)
}
return succeed, tasks
return tasks
}
func (e *export) exportGraphJson(ctx context.Context, req pb.RpcObjectListExportRequest, docs map[string]*types.Struct, succeed int, wr writer, queue process.Queue) int {

View file

@ -46,7 +46,7 @@ func extractFileFromArchiveToTempDirectory(fileName string, rc io.ReadCloser, te
directoryWithFile := filepath.Dir(fileName)
if directoryWithFile != "" {
directoryWithFile = filepath.Join(tempDir, directoryWithFile)
if err := os.Mkdir(directoryWithFile, 0777); err != nil && !os.IsExist(err) {
if err := os.MkdirAll(directoryWithFile, 0777); err != nil && !os.IsExist(err) {
return "", oserror.TransformError(err)
}
}

View file

@ -119,7 +119,7 @@ func (oc *ObjectCreator) Create(dataObject *DataObject, sn *common.Snapshot) (*t
}
st.ModifyLinkedFilesInDetails(func(fileId string) string {
newFileId := oc.relationSyncer.Sync(spaceID, fileId, dataObject.createPayloads, origin)
newFileId := oc.relationSyncer.Sync(spaceID, fileId, dataObject.newIdsSet, origin)
if newFileId != fileId {
filesToDelete = append(filesToDelete, fileId)
}
@ -151,13 +151,12 @@ func (oc *ObjectCreator) Create(dataObject *DataObject, sn *common.Snapshot) (*t
oc.setArchived(snapshot, newID)
syncErr := oc.syncFilesAndLinks(dataObject.createPayloads, domain.FullID{SpaceID: spaceID, ObjectID: newID}, origin)
syncErr := oc.syncFilesAndLinks(dataObject.newIdsSet, domain.FullID{SpaceID: spaceID, ObjectID: newID}, origin)
if syncErr != nil {
if errors.Is(syncErr, common.ErrFileLoad) {
return respDetails, newID, syncErr
}
}
return respDetails, newID, nil
}
@ -400,7 +399,7 @@ func (oc *ObjectCreator) setArchived(snapshot *model.SmartBlockSnapshotBase, new
}
}
func (oc *ObjectCreator) syncFilesAndLinks(snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, id domain.FullID, origin objectorigin.ObjectOrigin) error {
func (oc *ObjectCreator) syncFilesAndLinks(newIdsSet map[string]struct{}, id domain.FullID, origin objectorigin.ObjectOrigin) error {
tasks := make([]func() error, 0)
// todo: rewrite it in order not to create state with URLs inside links
err := block.Do(oc.service, id.ObjectID, func(b smartblock.SmartBlock) error {
@ -410,7 +409,7 @@ func (oc *ObjectCreator) syncFilesAndLinks(snapshotPayloads map[string]treestora
if s != nil {
// We can't run syncer here because it will cause a deadlock, so we defer this operation
tasks = append(tasks, func() error {
err := s.Sync(id, snapshotPayloads, bl, origin)
err := s.Sync(id, newIdsSet, bl, origin)
if err != nil {
return err
}

View file

@ -17,6 +17,8 @@ type DataObject struct {
ctx context.Context
origin objectorigin.ObjectOrigin
spaceID string
newIdsSet map[string]struct{}
}
type Result struct {
@ -32,6 +34,10 @@ func NewDataObject(ctx context.Context,
origin objectorigin.ObjectOrigin,
spaceID string,
) *DataObject {
newIdsSet := make(map[string]struct{}, len(oldIDtoNew))
for _, newId := range oldIDtoNew {
newIdsSet[newId] = struct{}{}
}
return &DataObject{
oldIDtoNew: oldIDtoNew,
createPayloads: createPayloads,
@ -39,6 +45,7 @@ func NewDataObject(ctx context.Context,
ctx: ctx,
origin: origin,
spaceID: spaceID,
newIdsSet: newIdsSet,
}
}

View file

@ -39,7 +39,8 @@ func (o *fileObject) GetIDAndPayload(ctx context.Context, spaceId string, sn *co
encryptionKeys[key.Path] = key.Key
}
}
fileObjectId, err := uploadFile(ctx, o.blockService, spaceId, filePath, origin, encryptionKeys)
name := pbtypes.GetString(sn.Snapshot.Data.Details, bundle.RelationKeyName.String())
fileObjectId, err := uploadFile(ctx, o.blockService, spaceId, name, filePath, origin, encryptionKeys)
if err != nil {
log.Error("handling file object: upload file", zap.Error(err))
return id, payload, nil

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/gogo/protobuf/types"
"go.uber.org/zap"
"github.com/anyproto/anytype-heart/core/block"
@ -39,7 +40,8 @@ func (f *oldFile) GetIDAndPayload(ctx context.Context, spaceId string, sn *commo
filePath := pbtypes.GetString(sn.Snapshot.Data.Details, bundle.RelationKeySource.String())
if filePath != "" {
fileObjectId, err := uploadFile(ctx, f.blockService, spaceId, filePath, origin, filesKeys)
name := pbtypes.GetString(sn.Snapshot.Data.Details, bundle.RelationKeyName.String())
fileObjectId, err := uploadFile(ctx, f.blockService, spaceId, name, filePath, origin, filesKeys)
if err != nil {
log.Error("handling old file object: upload file", zap.Error(err))
}
@ -62,16 +64,20 @@ func (f *oldFile) GetIDAndPayload(ctx context.Context, spaceId string, sn *commo
return objectId, treestorage.TreeStorageCreatePayload{}, nil
}
func uploadFile(ctx context.Context, blockService *block.Service, spaceId string, filePath string, origin objectorigin.ObjectOrigin, encryptionKeys map[string]string) (string, error) {
func uploadFile(ctx context.Context, blockService *block.Service, spaceId string, name string, filePath string, origin objectorigin.ObjectOrigin, encryptionKeys map[string]string) (string, error) {
params := pb.RpcFileUploadRequest{
SpaceId: spaceId,
LocalPath: filePath,
SpaceId: spaceId,
Details: &types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(name),
},
},
}
if strings.HasPrefix(filePath, "http://") || strings.HasPrefix(filePath, "https://") {
params = pb.RpcFileUploadRequest{
SpaceId: spaceId,
Url: filePath,
}
params.Url = filePath
} else {
params.LocalPath = filePath
}
dto := block.FileUploadRequest{
RpcFileUploadRequest: params,

View file

@ -3,8 +3,6 @@ package syncer
import (
"fmt"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
@ -20,7 +18,7 @@ func NewBookmarkSyncer(service *block.Service) *BookmarkSyncer {
return &BookmarkSyncer{service: service}
}
func (bs *BookmarkSyncer) Sync(id domain.FullID, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, b simple.Block, origin objectorigin.ObjectOrigin) error {
func (bs *BookmarkSyncer) Sync(id domain.FullID, newIdsSet map[string]struct{}, b simple.Block, origin objectorigin.ObjectOrigin) error {
if b.Model().GetBookmark().TargetObjectId != "" {
return nil
}

View file

@ -4,8 +4,6 @@ import (
"fmt"
"strings"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -42,7 +40,7 @@ func NewFileSyncer(
}
}
func (s *FileSyncer) Sync(id domain.FullID, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, b simple.Block, origin objectorigin.ObjectOrigin) error {
func (s *FileSyncer) Sync(id domain.FullID, newIdsSet map[string]struct{}, b simple.Block, origin objectorigin.ObjectOrigin) error {
if targetObjectId := b.Model().GetFile().GetTargetObjectId(); targetObjectId != "" {
return nil
}

View file

@ -5,7 +5,6 @@ import (
"fmt"
"strings"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/ipfs/go-cid"
"github.com/anyproto/anytype-heart/core/block"
@ -44,9 +43,9 @@ func NewIconSyncer(service *block.Service, resolver idresolver.Resolver, fileSto
}
}
func (s *IconSyncer) Sync(id domain.FullID, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, b simple.Block, origin objectorigin.ObjectOrigin) error {
func (s *IconSyncer) Sync(id domain.FullID, newIdsSet map[string]struct{}, b simple.Block, origin objectorigin.ObjectOrigin) error {
iconImage := b.Model().GetText().GetIconImage()
newId, err := s.handleIconImage(id.SpaceID, snapshotPayloads, iconImage, origin)
newId, err := s.handleIconImage(id.SpaceID, newIdsSet, iconImage, origin)
if err != nil {
return fmt.Errorf("%w: %w", common.ErrFileLoad, err)
}
@ -71,8 +70,8 @@ func (s *IconSyncer) Sync(id domain.FullID, snapshotPayloads map[string]treestor
return nil
}
func (s *IconSyncer) handleIconImage(spaceId string, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, iconImage string, origin objectorigin.ObjectOrigin) (string, error) {
if _, ok := snapshotPayloads[iconImage]; ok {
func (s *IconSyncer) handleIconImage(spaceId string, newIdsSet map[string]struct{}, iconImage string, origin objectorigin.ObjectOrigin) (string, error) {
if _, ok := newIdsSet[iconImage]; ok {
return iconImage, nil
}
_, err := cid.Decode(iconImage)

View file

@ -4,7 +4,6 @@ import (
"context"
"strings"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/ipfs/go-cid"
"github.com/anyproto/anytype-heart/core/block"
@ -32,9 +31,9 @@ func NewFileRelationSyncer(service *block.Service, fileStore filestore.FileStore
}
}
func (fs *FileRelationSyncer) Sync(spaceID string, fileId string, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, origin objectorigin.ObjectOrigin) string {
func (fs *FileRelationSyncer) Sync(spaceID string, fileId string, newIdsSet map[string]struct{}, origin objectorigin.ObjectOrigin) string {
// If file is created during import, do nothing
if _, ok := snapshotPayloads[fileId]; ok {
if _, ok := newIdsSet[fileId]; ok {
return fileId
}

View file

@ -1,13 +1,11 @@
package syncer
import (
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
)
type Syncer interface {
Sync(id domain.FullID, snapshotPayloads map[string]treestorage.TreeStorageCreatePayload, b simple.Block, origin objectorigin.ObjectOrigin) error
Sync(id domain.FullID, newIdsSet map[string]struct{}, b simple.Block, origin objectorigin.ObjectOrigin) error
}

View file

@ -139,7 +139,7 @@ func (i *Import) Import(ctx context.Context,
var rootCollectionId string
if c, ok := i.converters[req.Type.String()]; ok {
rootCollectionId, returnedErr = i.importFromBuiltinConverter(ctx, req, c, progress, origin)
return rootCollectionId, "", returnedErr
return rootCollectionId, progress.Id(), returnedErr
}
if req.Type == model.Import_External {
returnedErr = i.importFromExternalSource(ctx, req, progress)

View file

@ -209,20 +209,25 @@ func (s *service) getObjectRestrictions(rh RestrictionHolder) (r ObjectRestricti
}
func GetRestrictionsForUniqueKey(uk domain.UniqueKey) (r ObjectRestrictions) {
r = objectRestrictionsBySBType[uk.SmartblockType()]
switch uk.SmartblockType() {
case smartblock.SmartBlockTypeObjectType:
key := uk.InternalKey()
if lo.Contains(bundle.SystemTypes, domain.TypeKey(key)) {
return sysTypesRestrictions
r = sysTypesRestrictions
}
if t, _ := bundle.GetType(domain.TypeKey(key)); t != nil && t.RestrictObjectCreation {
r = append(r, model.Restrictions_CreateObjectOfThisType)
}
return r
case smartblock.SmartBlockTypeRelation:
key := uk.InternalKey()
if lo.Contains(bundle.SystemRelations, domain.RelationKey(key)) {
return sysRelationsRestrictions
r = sysRelationsRestrictions
}
}
// we assume that all sb types exist in objectRestrictionsBySBType
return objectRestrictionsBySBType[uk.SmartblockType()]
return r
}
func GetDataviewRestrictionsForUniqueKey(uk domain.UniqueKey) DataviewRestrictions {

View file

@ -47,10 +47,17 @@ func TestService_ObjectRestrictionsById(t *testing.T) {
), ErrRestricted)
})
t.Run("system type restricted creation", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyParticipant)).Object.Check(
model.Restrictions_CreateObjectOfThisType,
), ErrRestricted)
})
t.Run("ordinary type", func(t *testing.T) {
assert.NoError(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
model.Restrictions_Details,
model.Restrictions_Delete,
model.Restrictions_CreateObjectOfThisType,
))
})

View file

@ -152,7 +152,7 @@ func (s *service) createCustomTemplateState(templateId string) (targetState *sta
return
}
targetState.RemoveDetail(bundle.RelationKeyTargetObjectType.String(), bundle.RelationKeyTemplateIsBundled.String())
targetState.RemoveDetail(bundle.RelationKeyTargetObjectType.String(), bundle.RelationKeyTemplateIsBundled.String(), bundle.RelationKeyOrigin.String())
targetState.SetDetailAndBundledRelation(bundle.RelationKeySourceObject, pbtypes.String(sb.Id()))
targetState.SetLocalDetails(nil)
return

View file

@ -144,13 +144,6 @@ func (s *service) createInSpace(ctx context.Context, space clientspace.Space, re
}
details := s.makeInitialDetails(req.FileId, req.ObjectOrigin)
if req.AdditionalDetails != nil {
for k, v := range req.AdditionalDetails.GetFields() {
if _, ok := details.Fields[k]; !ok {
details.Fields[k] = pbtypes.CopyVal(v)
}
}
}
payload, err := space.CreateTreePayload(ctx, payloadcreator.PayloadCreationParams{
Time: time.Now(),
@ -176,6 +169,12 @@ func (s *service) createInSpace(ctx context.Context, space clientspace.Space, re
}
}
if req.AdditionalDetails != nil {
for k, v := range req.AdditionalDetails.GetFields() {
createState.SetDetailAndBundledRelation(domain.RelationKey(k), v)
}
}
// Type will be changed after indexing, just use general type File for now
id, object, err = s.objectCreator.CreateSmartBlockFromStateInSpaceWithOptions(ctx, space, []domain.TypeKey{bundle.TypeKeyFile}, createState, objectcreator.WithPayload(&payload))
if err != nil {

View file

@ -26,7 +26,7 @@ import (
const (
// ForceObjectsReindexCounter reindex thread-based objects
ForceObjectsReindexCounter int32 = 15
ForceObjectsReindexCounter int32 = 16
// ForceFilesReindexCounter reindex ipfs-file-based objects
ForceFilesReindexCounter int32 = 11 //
@ -431,6 +431,7 @@ func (i *indexer) saveLatestChecksums(spaceID string) error {
FilesForceReindexCounter: ForceFilesReindexCounter,
IdxRebuildCounter: ForceIdxRebuildCounter,
FulltextRebuild: ForceFulltextIndexCounter,
FulltextErase: ForceFulltextEraseCounter,
BundledObjects: ForceBundledObjectsReindexCounter,
FilestoreKeysForceReindexCounter: ForceFilestoreKeysReindexCounter,
}

2
go.mod
View file

@ -282,7 +282,7 @@ replace github.com/dgraph-io/badger/v4 => github.com/anyproto/badger/v4 v4.2.1-0
replace github.com/dgraph-io/ristretto => github.com/anyproto/ristretto v0.1.2-0.20240206201651-43b6ac4c41bf
replace github.com/libp2p/zeroconf/v2 => github.com/anyproto/zeroconf/v2 v2.2.1-0.20230303151330-fa3ab41a4941
replace github.com/libp2p/zeroconf/v2 => github.com/anyproto/zeroconf/v2 v2.2.1-0.20240228113933-f90a5cc4439d
replace github.com/JohannesKaufmann/html-to-markdown => github.com/anyproto/html-to-markdown v0.0.0-20231025221133-830bf0a6f139

4
go.sum
View file

@ -111,8 +111,8 @@ github.com/anyproto/protobuf v1.3.3-0.20240201225420-6e325cf0ac38 h1:80jke82/c+b
github.com/anyproto/protobuf v1.3.3-0.20240201225420-6e325cf0ac38/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/anyproto/ristretto v0.1.2-0.20240206201651-43b6ac4c41bf h1:4M5223J0RSOelU4UPyaru8qEVTtdRoLwNd6ZVTPACq4=
github.com/anyproto/ristretto v0.1.2-0.20240206201651-43b6ac4c41bf/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20230303151330-fa3ab41a4941 h1:sXnKo0P45QWddNR4Gfh9CgbgdQXiKjry+KJjU8evoyw=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20230303151330-fa3ab41a4941/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20240228113933-f90a5cc4439d h1:5bj7nX/AS8sxGpTIrapE7PC4oPlhkHMwMqXlJbUHBlg=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20240228113933-f90a5cc4439d/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=

View file

@ -148,10 +148,10 @@ func (c *client) sendNextBatch(info amplitude.AppInfoProvider, batcher *mb.MB[am
}
func (c *client) recordAggregatedData() {
c.lock.RLock()
c.lock.Lock()
toSend := c.aggregatableMap
c.aggregatableMap = make(map[string]SamplableEvent)
c.lock.RUnlock()
c.lock.Unlock()
// итерейтим сразу старую мапу и скармливаем ГЦ
for _, ev := range toSend {
c.send(ev)

View file

@ -53,13 +53,14 @@ type MetricsService interface {
}
type service struct {
lock sync.RWMutex
appVersion string
startVersion string
userId string
deviceId string
platform string
clients [2]*client
lock sync.RWMutex
appVersion string
startVersion string
userId string
deviceId string
platform string
clients [2]*client
alreadyRunning bool
}
func (s *service) SendSampled(ev SamplableEvent) {
@ -161,6 +162,11 @@ func (s *service) GetStartVersion() string {
func (s *service) Run() {
s.lock.Lock()
defer s.lock.Unlock()
if s.alreadyRunning {
return
}
s.alreadyRunning = true
for _, c := range s.clients {
c.ctx, c.cancel = context.WithCancel(context.Background())
c.setBatcher(mb.New[amplitude.Event](0))
@ -171,10 +177,11 @@ func (s *service) Run() {
func (s *service) Close() {
s.lock.Lock()
defer s.lock.Unlock()
for _, c := range s.clients {
c.Close()
}
defer s.lock.Unlock()
s.alreadyRunning = false
}
func (s *service) Send(ev amplitude.Event) {

View file

@ -67,6 +67,7 @@ func HasObjectTypeID(id string) bool {
return exists
}
// GetTypeByUrl is deprecated, use GetType instead
func GetTypeByUrl(u string) (*model.ObjectType, error) {
if !strings.HasPrefix(u, TypePrefix) {
return nil, fmt.Errorf("invalid url with no bundled type prefix")
@ -81,6 +82,16 @@ func GetTypeByUrl(u string) (*model.ObjectType, error) {
return nil, ErrNotFound
}
func GetType(tk domain.TypeKey) (*model.ObjectType, error) {
if v, exists := types[tk]; exists {
t := pbtypes.CopyObjectType(v)
t.Key = tk.String()
return t, nil
}
return nil, ErrNotFound
}
// MustGetType returns built-in object type by predefined TypeKey constant
// PANICS IN CASE RELATION KEY IS NOT EXISTS DO NOT USE WITH ARBITRARY STRING
func MustGetType(tk domain.TypeKey) *model.ObjectType {

View file

@ -9,7 +9,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const RelationChecksum = "249cec71c86b081dea551089dba109ed91fcd35312c7fac7621846199ba85a9e"
const RelationChecksum = "15ce7e760c03abd6582ca7d20c4a04cc31f315cd6067149b32a9966e4f07b3fa"
const (
RelationKeyTag domain.RelationKey = "tag"
RelationKeyCamera domain.RelationKey = "camera"
@ -359,10 +359,10 @@ var (
Format: model.RelationFormat_object,
Id: "_brbacklinks",
Key: "backlinks",
Name: "Links to this object",
Name: "Backlinks",
ReadOnly: true,
ReadOnlyRelation: true,
Revision: 2,
Revision: 3,
Scope: model.Relation_type,
},
RelationKeyBudget: {
@ -1323,10 +1323,10 @@ var (
Format: model.RelationFormat_object,
Id: "_brlinks",
Key: "links",
Name: "Links from this object",
Name: "Links",
ReadOnly: true,
ReadOnlyRelation: true,
Revision: 2,
Revision: 3,
Scope: model.Relation_type,
},
RelationKeyLogic: {

View file

@ -1137,10 +1137,10 @@
"hidden": false,
"key": "links",
"maxCount": 0,
"name": "Links from this object",
"name": "Links",
"readonly": true,
"source": "derived",
"revision": 2
"revision": 3
},
{
"format": "number",
@ -1474,10 +1474,10 @@
"key": "backlinks",
"maxCount": 0,
"hidden": false,
"name": "Links to this object",
"name": "Backlinks",
"readonly": true,
"source": "local",
"revision": 2
"revision": 3
},
{
"description": "Relation that indicates document has been uninstalled",

View file

@ -297,7 +297,7 @@ func (s *space) migrationProfileObject(ctx context.Context) error {
extractedState.SetRootId(id)
return &smartblock.InitContext{
IsNewObject: true,
ObjectTypeKeys: []domain.TypeKey{bundle.TypeKeyDashboard},
ObjectTypeKeys: []domain.TypeKey{bundle.TypeKeyPage},
State: extractedState,
SpaceID: s.Id(),
}

View file

@ -38,6 +38,7 @@ func (s *service) getStatus(ctx context.Context, spaceId string) (ctrl spacecont
s.mu.Unlock()
return ctrl, nil
}
s.mu.Unlock()
return nil, ErrSpaceNotExists
}

View file

@ -115,6 +115,8 @@ func (p *peerStore) updatePeer(peerId string, oldIds, newIds []string) {
}
func (p *peerStore) AllLocalPeers() []string {
p.Lock()
defer p.Unlock()
return p.localPeerIds
}

View file

@ -0,0 +1,127 @@
package integration
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/require"
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/export"
"github.com/anyproto/anytype-heart/core/block/object/objectcreator"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
func createPageWithFileBlock(t *testing.T, app *testApplication, filePath string) string {
ctx := context.Background()
objectCreator := getService[objectcreator.Service](app)
id, _, err := objectCreator.CreateObject(ctx, app.personalSpaceId(), objectcreator.CreateObjectRequest{
ObjectTypeKey: bundle.TypeKeyPage,
Details: &types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("Page with file block"),
},
},
})
require.NoError(t, err)
blockService := getService[*block.Service](app)
sessionCtx := session.NewContext()
fileBlockId, err := blockService.CreateBlock(sessionCtx, pb.RpcBlockCreateRequest{
ContextId: id,
TargetId: id,
Position: model.Block_Inner,
Block: &model.Block{
Content: &model.BlockContentOfFile{
File: &model.BlockContentFile{},
},
},
})
require.NoError(t, err)
_, err = blockService.UploadFileBlock(id, block.UploadRequest{
RpcBlockUploadRequest: pb.RpcBlockUploadRequest{
ContextId: id,
BlockId: fileBlockId,
FilePath: filePath,
},
})
require.NoError(t, err)
return id
}
func TestExportFiles(t *testing.T) {
tempDir := t.TempDir()
ctx := context.Background()
app := createAccountAndStartApp(t, pb.RpcObjectImportUseCaseRequest_NONE)
t.Run("export protobuf", func(t *testing.T) {
id := createPageWithFileBlock(t, app, "./testdata/test_image.png")
exportService := getService[export.Export](app)
exportPath, _, err := exportService.Export(ctx, pb.RpcObjectListExportRequest{
SpaceId: app.personalSpaceId(),
Format: model.Export_Protobuf,
IncludeFiles: true,
IsJson: false,
Zip: false,
Path: tempDir,
ObjectIds: []string{id},
})
require.NoError(t, err)
entries, err := os.ReadDir(exportPath)
require.NoError(t, err)
var foundPbFiles int
for _, entry := range entries {
if filepath.Ext(entry.Name()) == ".pb" {
foundPbFiles++
}
}
// 4 objects total: Page object + Page type + File object + File object type
require.GreaterOrEqual(t, foundPbFiles, 4)
testImportObjectWithFileBlock(t, exportPath)
})
t.Run("export markdown", func(t *testing.T) {
id := createPageWithFileBlock(t, app, "./testdata/saturn.jpg")
exportService := getService[export.Export](app)
exportPath, _, err := exportService.Export(ctx, pb.RpcObjectListExportRequest{
SpaceId: app.personalSpaceId(),
Format: model.Export_Markdown,
IncludeFiles: true,
IsJson: false,
Zip: false,
Path: tempDir,
ObjectIds: []string{id},
})
require.NoError(t, err)
entries, err := os.ReadDir(exportPath)
require.NoError(t, err)
var foundMarkdownFiles int
for _, entry := range entries {
if filepath.Ext(entry.Name()) == ".md" {
foundMarkdownFiles++
}
}
// Only one markdown file
require.Equal(t, foundMarkdownFiles, 1)
testImportFileFromMarkdown(t, exportPath)
})
}

View file

@ -15,11 +15,11 @@ import (
func TestFiles(t *testing.T) {
ctx := context.Background()
app, acc := createAccountAndStartApp(t)
app := createAccountAndStartApp(t, pb.RpcObjectImportUseCaseRequest_GET_STARTED)
t.Run("upload image", func(t *testing.T) {
blockService := getService[*block.Service](app)
objectId, details, err := blockService.UploadFile(ctx, acc.Info.AccountSpaceId, block.FileUploadRequest{
objectId, details, err := blockService.UploadFile(ctx, app.personalSpaceId(), block.FileUploadRequest{
RpcFileUploadRequest: pb.RpcFileUploadRequest{
LocalPath: "./testdata/test_image.png",
},

View file

@ -0,0 +1,32 @@
package integration
import (
"golang.org/x/exp/constraints"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
func filterEqualsToString(key domain.RelationKey, value string) *model.BlockContentDataviewFilter {
return &model.BlockContentDataviewFilter{
RelationKey: key.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.String(value),
}
}
func filterNotEmpty(key domain.RelationKey) *model.BlockContentDataviewFilter {
return &model.BlockContentDataviewFilter{
RelationKey: key.String(),
Condition: model.BlockContentDataviewFilter_NotEmpty,
}
}
func filterEqualsToInteger[T constraints.Integer](key domain.RelationKey, value T) *model.BlockContentDataviewFilter {
return &model.BlockContentDataviewFilter{
RelationKey: key.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Int64(int64(value)),
}
}

View file

@ -0,0 +1,166 @@
package integration
import (
"context"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
importer "github.com/anyproto/anytype-heart/core/block/import"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/gateway"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
func TestImportFileFromRelation(t *testing.T) {
ctx := context.Background()
app := createAccountAndStartApp(t, pb.RpcObjectImportUseCaseRequest_NONE)
fileSub := newTestSubscription(t, app, []domain.RelationKey{bundle.RelationKeyId}, []*model.BlockContentDataviewFilter{
filterEqualsToInteger(bundle.RelationKeyFileIndexingStatus, model.FileIndexingStatus_Indexed),
filterEqualsToInteger(bundle.RelationKeyLayout, model.ObjectType_image),
filterEqualsToString(bundle.RelationKeyName, "Saturn"),
filterEqualsToString(bundle.RelationKeyFileMimeType, "image/jpeg"),
filterNotEmpty(bundle.RelationKeyFileId),
})
objectSub := newTestSubscription(t, app, []domain.RelationKey{bundle.RelationKeyId, bundle.RelationKeyIconImage}, []*model.BlockContentDataviewFilter{
filterNotEmpty(bundle.RelationKeyIconImage),
})
importerService := getService[importer.Importer](app)
_, processId, err := importerService.Import(ctx, &pb.RpcObjectImportRequest{
SpaceId: app.personalSpaceId(),
Mode: pb.RpcObjectImportRequest_IGNORE_ERRORS,
Type: model.Import_Pb,
Params: &pb.RpcObjectImportRequestParamsOfPbParams{
PbParams: &pb.RpcObjectImportRequestPbParams{
Path: []string{"./testdata/import/object with file relation/"},
},
},
}, objectorigin.Import(model.Import_Pb), nil)
require.NoError(t, err)
app.waitEventMessage(t, func(msg *pb.EventMessage) bool {
if v := msg.GetProcessDone(); v != nil {
return v.Process.Id == processId
}
return false
})
var fileObjectId string
fileSub.waitOneObjectDetailsSet(t, app, func(t *testing.T, msg *pb.EventObjectDetailsSet) {
fileObjectId = pbtypes.GetString(msg.Details, bundle.RelationKeyId.String())
assertImageAvailableInGateway(t, app, fileObjectId)
})
objectSub.waitObjectDetailsSetWithPredicate(t, app, func(t *testing.T, msg *pb.EventObjectDetailsSet) bool {
list := pbtypes.GetStringList(msg.Details, bundle.RelationKeyIconImage.String())
if len(list) > 0 {
return fileObjectId == list[0]
}
return false
})
}
func TestImportFileFromBlock(t *testing.T) {
testImportObjectWithFileBlock(t, "./testdata/import/object with file block/")
}
func TestImportFileFromMarkdown(t *testing.T) {
testImportFileFromMarkdown(t, "./testdata/import/markdown with files/")
}
func testImportFileFromMarkdown(t *testing.T, path string) {
ctx := context.Background()
app := createAccountAndStartApp(t, pb.RpcObjectImportUseCaseRequest_NONE)
fileSub := newTestSubscription(t, app, []domain.RelationKey{bundle.RelationKeyId}, []*model.BlockContentDataviewFilter{
filterEqualsToInteger(bundle.RelationKeyFileIndexingStatus, model.FileIndexingStatus_Indexed),
filterEqualsToInteger(bundle.RelationKeyLayout, model.ObjectType_image),
filterEqualsToString(bundle.RelationKeyName, "saturn"), // Name comes from file's name
filterEqualsToString(bundle.RelationKeyFileMimeType, "image/jpeg"),
filterNotEmpty(bundle.RelationKeyFileId),
})
importerService := getService[importer.Importer](app)
_, processId, err := importerService.Import(ctx, &pb.RpcObjectImportRequest{
SpaceId: app.personalSpaceId(),
Mode: pb.RpcObjectImportRequest_IGNORE_ERRORS,
Type: model.Import_Markdown,
Params: &pb.RpcObjectImportRequestParamsOfMarkdownParams{
MarkdownParams: &pb.RpcObjectImportRequestMarkdownParams{
Path: []string{path},
},
},
}, objectorigin.Import(model.Import_Markdown), nil)
require.NoError(t, err)
app.waitEventMessage(t, func(msg *pb.EventMessage) bool {
if v := msg.GetProcessDone(); v != nil {
return v.Process.Id == processId
}
return false
})
fileSub.waitOneObjectDetailsSet(t, app, func(t *testing.T, msg *pb.EventObjectDetailsSet) {
fileObjectId := pbtypes.GetString(msg.Details, bundle.RelationKeyId.String())
assertImageAvailableInGateway(t, app, fileObjectId)
})
}
func testImportObjectWithFileBlock(t *testing.T, path string) {
ctx := context.Background()
app := createAccountAndStartApp(t, pb.RpcObjectImportUseCaseRequest_NONE)
fileSub := newTestSubscription(t, app, []domain.RelationKey{bundle.RelationKeyId}, []*model.BlockContentDataviewFilter{
filterEqualsToInteger(bundle.RelationKeyFileIndexingStatus, model.FileIndexingStatus_Indexed),
filterEqualsToInteger(bundle.RelationKeyLayout, model.ObjectType_image),
filterEqualsToString(bundle.RelationKeyName, "test_image"),
filterEqualsToString(bundle.RelationKeyFileMimeType, "image/png"),
filterNotEmpty(bundle.RelationKeyFileId),
})
importerService := getService[importer.Importer](app)
_, processId, err := importerService.Import(ctx, &pb.RpcObjectImportRequest{
SpaceId: app.personalSpaceId(),
Mode: pb.RpcObjectImportRequest_IGNORE_ERRORS,
Type: model.Import_Pb,
Params: &pb.RpcObjectImportRequestParamsOfPbParams{
PbParams: &pb.RpcObjectImportRequestPbParams{
Path: []string{path},
},
},
}, objectorigin.Import(model.Import_Pb), nil)
require.NoError(t, err)
app.waitEventMessage(t, func(msg *pb.EventMessage) bool {
if v := msg.GetProcessDone(); v != nil {
return v.Process.Id == processId
}
return false
})
fileSub.waitOneObjectDetailsSet(t, app, func(t *testing.T, msg *pb.EventObjectDetailsSet) {
fileObjectId := pbtypes.GetString(msg.Details, bundle.RelationKeyId.String())
assertImageAvailableInGateway(t, app, fileObjectId)
})
}
func assertImageAvailableInGateway(t *testing.T, app *testApplication, fileObjectId string) {
gw := getService[gateway.Gateway](app)
host := gw.Addr()
resp, err := http.Get("http://" + host + "/image/" + fileObjectId)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
defer resp.Body.Close()
raw, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.True(t, len(raw) > 0)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,3 @@
Test markdown import with files
![saturn.jpg](files/saturn.jpg)

View file

@ -0,0 +1,285 @@
{
"sbType": "STType",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreidcjbn5axvbp4qrs43vfvtgmj3w5ahygfhjsbjm44gkaqms2iopny",
"childrenIds": [
"header"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708681936,
"creator": "_anytype_profile",
"description": "Auto-generated object from .jpg \u0026 .png files added to Anytype. A representation of the external form of a person or thing in art",
"featuredRelations": [
"backlinks",
"type"
],
"iconEmoji": "🏞",
"id": "bafyreidcjbn5axvbp4qrs43vfvtgmj3w5ahygfhjsbjm44gkaqms2iopny",
"internalFlags": [
],
"isArchived": false,
"isHidden": false,
"isReadonly": true,
"lastModifiedBy": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708681937,
"lastUsedDate": 1708679837,
"layout": 4,
"links": [
],
"name": "Image",
"origin": 7,
"recommendedLayout": 8,
"recommendedRelations": [
"bafyreiexomkqsmiquooblaiivjekszpxtktc4vwzpot7eagl4sm4xgvxsm",
"bafyreigpg65ux5iiouypryze4xe3zybzvzgjkukqhwt6ywceo2xyrbyzcq",
"bafyreifigx3ej4ryeolhg2z3kejvvqyoesc6oqay7fr5yqhb6c3qs42wwa",
"bafyreihzahmws7ikdemquxesbr33pjfjrefaqidjrpo3i4rt3uu4s6btra",
"bafyreif3deijdgfbsd6n3kmxqgsb2tnkt7n4jnngqm2icrpfyi37c7v47q",
"bafyreifpt7wvp72aax7c2bjd4fkoimimvqj4xofxlhayg5qklocuyxeucy",
"bafyreic7cas7aiytfos6gh7445glrygw7bbpgbjnhifmtjaly3axes4bue",
"bafyreigdgqsyw5qhov4n5prhbtbw3hszi3bbiunni456naw5sidvvqauc4",
"bafyreickzet3mkznzh4akug2p6qrnmhxzjjzctrt42qfl7gfpwap5a2dp4",
"bafyreihocceydestrxj42pplz2lv5moumaezgdgajcdhdhxr2xjt6fkn7q",
"bafyreibtadmiwpuj7ylqjtliukqpv6z6uqcspr5v4xerrsgeqjoeq5m7o4",
"bafyreiawn7wlwt6hzgkdpnaf6ap6q5s35xnnqpuvyo7fa6eatv6yptx4uy"
],
"restrictions": [
3,
6,
5,
7,
4,
1
],
"revision": 0,
"smartblockTypes": [
256
],
"snippet": "",
"sourceObject": "_otimage",
"spaceId": "bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4.3uxcyvge8is62",
"type": "bafyreibb45igjiprvuhe7j35hgfunh72oggyqhnoyxknmm5ebqfyvrwulq",
"uniqueKey": "ot-image"
},
"objectTypes": [
"ot-objectType"
],
"relationLinks": [
{
"key": "spaceId",
"format": "object"
},
{
"key": "description"
},
{
"key": "recommendedRelations",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "revision",
"format": "number"
},
{
"key": "origin",
"format": "number"
},
{
"key": "recommendedLayout",
"format": "number"
},
{
"key": "sourceObject",
"format": "object"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "isHidden",
"format": "checkbox"
},
{
"key": "smartblockTypes",
"format": "number"
},
{
"key": "layout",
"format": "number"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "id",
"format": "object"
},
{
"key": "uniqueKey"
},
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "fileId",
"format": "shorttext"
},
{
"key": "sizeInBytes",
"format": "number"
},
{
"key": "lastUsedDate",
"format": "date"
},
{
"key": "isArchived",
"format": "checkbox"
}
],
"key": "image"
}
}
}

View file

@ -0,0 +1,297 @@
{
"sbType": "FileObject",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreie5dsqow6vcoqh5cahq4epetrm3tnmue5txgbdbyjojcgpase5soy",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"header",
"file",
"widthInPixels",
"heightInPixels",
"sizeInBytes"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "file",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"file": {
"name": "test_image.png",
"type": "Image",
"mime": "image/png",
"size": "473831",
"targetObjectId": "bafyreie5dsqow6vcoqh5cahq4epetrm3tnmue5txgbdbyjojcgpase5soy",
"state": "Done"
}
},
{
"id": "widthInPixels",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "widthInPixels"
}
},
{
"id": "heightInPixels",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "heightInPixels"
}
},
{
"id": "sizeInBytes",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "sizeInBytes"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"addedDate": 1708689792,
"backlinks": [
],
"createdDate": 1708689792,
"creator": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"featuredRelations": [
"type"
],
"fileBackupStatus": 1,
"fileExt": "png",
"fileId": "bafybeigw3qcbjo32mum7uq2esdocm6oofh2ncx24p26rhwmfurvh3yq524",
"fileIndexingStatus": 1,
"fileMimeType": "image/png",
"fileSyncStatus": 1,
"heightInPixels": 512,
"iconImage": "bafyreie5dsqow6vcoqh5cahq4epetrm3tnmue5txgbdbyjojcgpase5soy",
"id": "bafyreie5dsqow6vcoqh5cahq4epetrm3tnmue5txgbdbyjojcgpase5soy",
"isReadonly": false,
"lastModifiedBy": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708689794,
"layout": 8,
"links": [
],
"name": "test_image",
"origin": 2,
"restrictions": [
3,
6,
5,
7,
8
],
"sizeInBytes": 473831,
"snippet": "",
"source": "files/test_image.png",
"spaceId": "bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4.3uxcyvge8is62",
"type": "bafyreidcjbn5axvbp4qrs43vfvtgmj3w5ahygfhjsbjm44gkaqms2iopny",
"widthInPixels": 512
},
"objectTypes": [
"ot-image"
],
"relationLinks": [
{
"key": "widthInPixels",
"format": "number"
},
{
"key": "fileExt"
},
{
"key": "sizeInBytes",
"format": "number"
},
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "layout",
"format": "number"
},
{
"key": "heightInPixels",
"format": "number"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "fileMimeType"
},
{
"key": "addedDate",
"format": "date"
},
{
"key": "fileIndexingStatus",
"format": "number"
},
{
"key": "fileId",
"format": "shorttext"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "spaceId",
"format": "object"
},
{
"key": "origin",
"format": "number"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "creator",
"format": "object"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "id",
"format": "object"
},
{
"key": "type",
"format": "object"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "fileBackupStatus",
"format": "number"
},
{
"key": "fileSyncStatus",
"format": "number"
},
{
"key": "source",
"format": "url"
}
],
"fileInfo": {
"fileId": "bafybeigw3qcbjo32mum7uq2esdocm6oofh2ncx24p26rhwmfurvh3yq524",
"encryptionKeys": [
{
"path": "/0/thumbnail/",
"key": "bb5zsn2rvmc24ekyav4ogwrtj6afvwya6siy644tepem64onjr4wa"
},
{
"path": "/0/exif/",
"key": "bw324fjytzv6ih6hxv44zkoukak6vxjnmixmu346xknuplqqoh4mq"
},
{
"path": "/0/original/",
"key": "boo64mttkxvjdtm4tovib2chfrte4odm6z5o5h72bjwtv6mp3j77q"
},
{
"path": "/0/large/",
"key": "boo64mttkxvjdtm4tovib2chfrte4odm6z5o5h72bjwtv6mp3j77q"
},
{
"path": "/0/small/",
"key": "bpentwpltji4e6zgscfge2fi6hvy5z4kvmznqlv3gd2kmelgx357q"
}
]
}
}
}
}

View file

@ -0,0 +1,275 @@
{
"sbType": "STType",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreiemifn6w6xsd7g6lltwtcatunjuycybb5wkxrzwp3ljfoid7z6b74",
"childrenIds": [
"header"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708681936,
"creator": "_anytype_profile",
"description": "Blank canvas with Title",
"featuredRelations": [
"backlinks",
"type"
],
"iconEmoji": "📄",
"id": "bafyreiemifn6w6xsd7g6lltwtcatunjuycybb5wkxrzwp3ljfoid7z6b74",
"internalFlags": [
],
"isArchived": false,
"isHidden": false,
"isReadonly": true,
"lastModifiedBy": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708681937,
"lastUsedDate": 1708689740,
"layout": 4,
"links": [
],
"name": "Page",
"origin": 7,
"recommendedLayout": 0,
"recommendedRelations": [
"bafyreictzawc6xtanufurxgn2ttmqmshzqzz2ve235tzovkh4swbztae3a",
"bafyreiawn7wlwt6hzgkdpnaf6ap6q5s35xnnqpuvyo7fa6eatv6yptx4uy"
],
"restrictions": [
3,
6,
5,
7,
4,
1
],
"revision": 0,
"smartblockTypes": [
16
],
"snippet": "",
"sourceObject": "_otpage",
"spaceId": "bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4.3uxcyvge8is62",
"type": "bafyreibb45igjiprvuhe7j35hgfunh72oggyqhnoyxknmm5ebqfyvrwulq",
"uniqueKey": "ot-page"
},
"objectTypes": [
"ot-objectType"
],
"relationLinks": [
{
"key": "spaceId",
"format": "object"
},
{
"key": "description"
},
{
"key": "recommendedLayout",
"format": "number"
},
{
"key": "recommendedRelations",
"format": "object"
},
{
"key": "layout",
"format": "number"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "isHidden",
"format": "checkbox"
},
{
"key": "revision",
"format": "number"
},
{
"key": "origin",
"format": "number"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "smartblockTypes",
"format": "number"
},
{
"key": "sourceObject",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "id",
"format": "object"
},
{
"key": "uniqueKey"
},
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "fileId",
"format": "shorttext"
},
{
"key": "sizeInBytes",
"format": "number"
},
{
"key": "lastUsedDate",
"format": "date"
},
{
"key": "isArchived",
"format": "checkbox"
}
],
"key": "page"
}
}
}

View file

@ -0,0 +1,229 @@
{
"sbType": "Page",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreigsuu2hfq2cyb7ae4fg6ogcinhqmojudvbb7ekhe5fhrp2rrsixl4",
"restrictions": {
},
"childrenIds": [
"header",
"65d889564ac3df30bcfe9576",
"65d889804ac3df30bcfe9578"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "65d889564ac3df30bcfe9576",
"restrictions": {
},
"text": {
"marks": {
}
}
},
{
"id": "65d889804ac3df30bcfe9578",
"restrictions": {
},
"file": {
"name": "test_image.png",
"type": "Image",
"mime": "image/png",
"size": "473831",
"addedAt": "1708689793",
"targetObjectId": "bafyreie5dsqow6vcoqh5cahq4epetrm3tnmue5txgbdbyjojcgpase5soy",
"state": "Done"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708689740,
"creator": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"featuredRelations": [
"type",
"backlinks"
],
"id": "bafyreigsuu2hfq2cyb7ae4fg6ogcinhqmojudvbb7ekhe5fhrp2rrsixl4",
"lastModifiedBy": "_participant_bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708689793,
"lastOpenedDate": 1708689789,
"layout": 0,
"links": [
],
"name": "Page with file block",
"restrictions": [
],
"snippet": "",
"spaceId": "bafyreieo7b7uqpcyrl5djfeezwhrycrexqv2l2giugf67drbg536naiav4.3uxcyvge8is62",
"type": "bafyreiemifn6w6xsd7g6lltwtcatunjuycybb5wkxrzwp3ljfoid7z6b74"
},
"objectTypes": [
"ot-page"
],
"relationLinks": [
{
"key": "id",
"format": "object"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "description"
},
{
"key": "snippet"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layout",
"format": "number"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "spaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "backlinks",
"format": "object"
}
]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

View file

@ -0,0 +1,220 @@
{
"sbType": "Page",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreianhlajbeopktynhpa3vl3eay67l3cjukngwanre72mtinz5nh7xa",
"restrictions": {
},
"childrenIds": [
"header",
"65dc68924ac3df3648e9b871"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "65dc68924ac3df3648e9b871",
"restrictions": {
},
"text": {
"marks": {
}
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708943497,
"creator": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"featuredRelations": [
"type",
"backlinks"
],
"iconImage": [
"bafyreieq4gxa3uhpk6dxgvbuzq6fbn3mm75fstmyrqzf4xr4mvowcoxv64"
],
"id": "bafyreianhlajbeopktynhpa3vl3eay67l3cjukngwanre72mtinz5nh7xa",
"lastModifiedBy": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708943991,
"lastOpenedDate": 1708944358,
"layout": 0,
"links": [
],
"name": "Page with image relation",
"restrictions": [
],
"snippet": "",
"spaceId": "bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya.3uxcyvge8is62",
"type": "bafyreifa6mvn6nhazik67die3i7ima3s64qhpeb6ei2k5kadv2ovgvm2ti"
},
"objectTypes": [
"ot-page"
],
"relationLinks": [
{
"key": "id",
"format": "object"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "description"
},
{
"key": "snippet"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layout",
"format": "number"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "spaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "65dc68a44ac3df3648e9b872",
"format": "file"
}
]
}
}
}

View file

@ -0,0 +1,278 @@
{
"sbType": "STType",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreiekbzgglqe257ir3q3ysj2secryslyb5vtw2e3ujgq65hxslklovq",
"childrenIds": [
"header"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708681936,
"creator": "_anytype_profile",
"description": "Auto-generated object from .jpg \u0026 .png files added to Anytype. A representation of the external form of a person or thing in art",
"featuredRelations": [
"backlinks",
"type"
],
"iconEmoji": "🏞",
"id": "bafyreiekbzgglqe257ir3q3ysj2secryslyb5vtw2e3ujgq65hxslklovq",
"internalFlags": [
],
"isArchived": false,
"isHidden": false,
"isReadonly": true,
"lastModifiedBy": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708943218,
"lastOpenedDate": 1708944333,
"lastUsedDate": 1708941118,
"layout": 4,
"links": [
],
"name": "Image",
"origin": 7,
"recommendedLayout": 8,
"recommendedRelations": [
"bafyreiecob33wsvbrwkqxguhyiqpyg26vyabf7hfmdr46skehskjrgh5wq",
"bafyreiew22bog65qtjco2eng7blewsijfdpbwc3576udbze2bda7ltdewy",
"bafyreie25mqmth436f5kobjlyouyyncp35dupigctczbycwo24okk2thhe",
"bafyreiejmhqpvubz4sf7nueklixt47hieqtamowykf6aymecgkieibqhke",
"bafyreif26it2llas66puonq3rrmu2lyjtvwsti4d4ncc5dszzjrnufkbw4",
"bafyreicc35pyhd3cnqpyqv2wp3fgyyqrvluc23wqdtowbo3iwdriqfbteu",
"bafyreiaixviwnl5dxjwv4decbrhkmccnn4zmqacez6de4qtp3brknsfbte",
"bafyreicjphrkfw23fgc2dp5ixdy6rel6ru3awdhj5jp73gzhfrl6xgnyre",
"bafyreifleyzmf4zc7glk45ds4v3ha3xcrxa5dquxhay3bcp7wsgk4qtkky",
"bafyreiduvs753mdimrpba5oftdmbcxmiqg7kpyuoa3nlfqmldqd5u7dc24",
"bafyreidi66k5aamafg4hvizvr4fxdkphvrfldf4gboir74exawkmx3w65e",
"bafyreienh34xl22nkkrnisff7nzi6ocd57knjupxhy6v62oosup2sjsfzy"
],
"restrictions": [
3,
6,
5,
7,
4,
1
],
"revision": 0,
"smartblockTypes": [
256
],
"snippet": "",
"sourceObject": "_otimage",
"spaceId": "bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya.3uxcyvge8is62",
"type": "bafyreiakntlkwsbc4sdfgtq24quh555pohxmjloqh4fjrjy25fopodvfqq",
"uniqueKey": "ot-image"
},
"objectTypes": [
"ot-objectType"
],
"relationLinks": [
{
"key": "spaceId",
"format": "object"
},
{
"key": "description"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "layout",
"format": "number"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "sourceObject",
"format": "object"
},
{
"key": "revision",
"format": "number"
},
{
"key": "origin",
"format": "number"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "smartblockTypes",
"format": "number"
},
{
"key": "recommendedRelations",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "recommendedLayout",
"format": "number"
},
{
"key": "isHidden",
"format": "checkbox"
},
{
"key": "id",
"format": "object"
},
{
"key": "uniqueKey"
},
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "lastUsedDate",
"format": "date"
},
{
"key": "isArchived",
"format": "checkbox"
}
],
"key": "image"
}
}
}

View file

@ -0,0 +1,305 @@
{
"sbType": "FileObject",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreieq4gxa3uhpk6dxgvbuzq6fbn3mm75fstmyrqzf4xr4mvowcoxv64",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"header",
"file",
"widthInPixels",
"heightInPixels",
"sizeInBytes"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "file",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"file": {
"name": "4399421398_87191e276a_w.jpg",
"type": "Image",
"mime": "image/jpeg",
"size": "25869",
"targetObjectId": "bafyreieq4gxa3uhpk6dxgvbuzq6fbn3mm75fstmyrqzf4xr4mvowcoxv64",
"state": "Done"
}
},
{
"id": "widthInPixels",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "widthInPixels"
}
},
{
"id": "heightInPixels",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "heightInPixels"
}
},
{
"id": "sizeInBytes",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"relation": {
"key": "sizeInBytes"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"addedDate": 1708943421,
"backlinks": [
],
"createdDate": 1708943421,
"creator": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"featuredRelations": [
"type"
],
"fileBackupStatus": 1,
"fileExt": "jpg",
"fileId": "bafybeigidjso2crpld44fik6m6bryaqdhxqgyzxybl5j7wpuxymncxtlvy",
"fileIndexingStatus": 1,
"fileMimeType": "image/jpeg",
"fileSyncStatus": 1,
"heightInPixels": 186,
"iconImage": "bafyreieq4gxa3uhpk6dxgvbuzq6fbn3mm75fstmyrqzf4xr4mvowcoxv64",
"id": "bafyreieq4gxa3uhpk6dxgvbuzq6fbn3mm75fstmyrqzf4xr4mvowcoxv64",
"isReadonly": false,
"lastModifiedBy": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708944355,
"lastOpenedDate": 1708944335,
"layout": 8,
"links": [
],
"name": "Saturn",
"restrictions": [
3,
6,
5,
7,
8
],
"sizeInBytes": 25869,
"snippet": "",
"source": "files/4399421398_87191e276a_w.jpg",
"spaceId": "bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya.3uxcyvge8is62",
"type": "bafyreiekbzgglqe257ir3q3ysj2secryslyb5vtw2e3ujgq65hxslklovq",
"widthInPixels": 400
},
"objectTypes": [
"ot-image"
],
"relationLinks": [
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "layout",
"format": "number"
},
{
"key": "widthInPixels",
"format": "number"
},
{
"key": "heightInPixels",
"format": "number"
},
{
"key": "addedDate",
"format": "date"
},
{
"key": "fileIndexingStatus",
"format": "number"
},
{
"key": "fileId",
"format": "shorttext"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "fileExt"
},
{
"key": "fileMimeType"
},
{
"key": "sizeInBytes",
"format": "number"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "spaceId",
"format": "object"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "creator",
"format": "object"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "id",
"format": "object"
},
{
"key": "type",
"format": "object"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "fileBackupStatus",
"format": "number"
},
{
"key": "fileSyncStatus",
"format": "number"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "links",
"format": "object"
},
{
"key": "source",
"format": "url"
}
],
"fileInfo": {
"fileId": "bafybeigidjso2crpld44fik6m6bryaqdhxqgyzxybl5j7wpuxymncxtlvy",
"encryptionKeys": [
{
"path": "/0/original/",
"key": "bwovwcuqms2o22jsipkf4r72r7nef2y57drqndn3pqrtwk5jlxwwq"
},
{
"path": "/0/large/",
"key": "bwovwcuqms2o22jsipkf4r72r7nef2y57drqndn3pqrtwk5jlxwwq"
},
{
"path": "/0/small/",
"key": "bai7phqac5ld6tvqallxbreqkolu62gwqlrztfmm642ltcokeaiyq"
},
{
"path": "/0/thumbnail/",
"key": "blr5zk4bx77tdrasgp7lp362m2dmucftddglixkvktnhz3p6byyea"
},
{
"path": "/0/exif/",
"key": "bprfpw2rw7b4iklfw26gq4leghmvl5kiw5z2jle7jo57sfgcbnzka"
}
]
}
}
}
}

View file

@ -0,0 +1,267 @@
{
"sbType": "STType",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreifa6mvn6nhazik67die3i7ima3s64qhpeb6ei2k5kadv2ovgvm2ti",
"childrenIds": [
"header"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708681936,
"creator": "_anytype_profile",
"description": "Blank canvas with Title",
"featuredRelations": [
"backlinks",
"type"
],
"iconEmoji": "📄",
"id": "bafyreifa6mvn6nhazik67die3i7ima3s64qhpeb6ei2k5kadv2ovgvm2ti",
"internalFlags": [
],
"isArchived": false,
"isHidden": false,
"isReadonly": true,
"lastModifiedBy": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708943218,
"lastUsedDate": 1708943500,
"layout": 4,
"links": [
],
"name": "Page",
"origin": 7,
"recommendedLayout": 0,
"recommendedRelations": [
"bafyreia5dbxjwepsyvnotslsbzkgoldojduyrnig3dj6dfts6twih35q5m",
"bafyreienh34xl22nkkrnisff7nzi6ocd57knjupxhy6v62oosup2sjsfzy"
],
"restrictions": [
3,
6,
5,
7,
4,
1
],
"revision": 0,
"smartblockTypes": [
16
],
"snippet": "",
"sourceObject": "_otpage",
"spaceId": "bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya.3uxcyvge8is62",
"type": "bafyreiakntlkwsbc4sdfgtq24quh555pohxmjloqh4fjrjy25fopodvfqq",
"uniqueKey": "ot-page"
},
"objectTypes": [
"ot-objectType"
],
"relationLinks": [
{
"key": "spaceId",
"format": "object"
},
{
"key": "recommendedRelations",
"format": "object"
},
{
"key": "recommendedLayout",
"format": "number"
},
{
"key": "description"
},
{
"key": "origin",
"format": "number"
},
{
"key": "smartblockTypes",
"format": "number"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "revision",
"format": "number"
},
{
"key": "isHidden",
"format": "checkbox"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "sourceObject",
"format": "object"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "layout",
"format": "number"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "id",
"format": "object"
},
{
"key": "uniqueKey"
},
{
"key": "isReadonly",
"format": "checkbox"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "creator",
"format": "object"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
},
{
"key": "lastUsedDate",
"format": "date"
},
{
"key": "isArchived",
"format": "checkbox"
}
],
"key": "page"
}
}
}

View file

@ -0,0 +1,222 @@
{
"sbType": "STRelation",
"snapshot": {
"data": {
"blocks": [
{
"id": "bafyreihgcpy6tmpf5zlohex5nris3lzybjn34eob74uny2ysvsbg7ocpdi",
"childrenIds": [
"header"
],
"smartblock": {
}
},
{
"id": "header",
"restrictions": {
"edit": true,
"remove": true,
"drag": true,
"dropOn": true
},
"childrenIds": [
"title",
"featuredRelations"
],
"layout": {
"style": "Header"
}
},
{
"id": "title",
"fields": {
"_detailsKey": [
"name",
"done"
]
},
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"text": {
"style": "Title",
"marks": {
}
}
},
{
"id": "featuredRelations",
"restrictions": {
"remove": true,
"drag": true,
"dropOn": true
},
"featuredRelations": {
}
}
],
"details": {
"backlinks": [
],
"createdDate": 1708943524,
"creator": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"featuredRelations": [
"type"
],
"id": "bafyreihgcpy6tmpf5zlohex5nris3lzybjn34eob74uny2ysvsbg7ocpdi",
"internalFlags": [
],
"lastModifiedBy": "_participant_bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya_3uxcyvge8is62_A6LrAEcm6LMpygR5LZ2yJ4izsNmzKrzGBJmYWKExtiJZbi7h",
"lastModifiedDate": 1708943524,
"layout": 5,
"links": [
],
"name": "Custom image relation",
"relationFormat": 5,
"relationFormatObjectTypes": [
],
"relationKey": "65dc68a44ac3df3648e9b872",
"restrictions": [
3,
6,
5,
7
],
"snippet": "",
"spaceId": "bafyreifcy5wy3wluu2r2ewcm7v46lpykqqyf5362mn54jsr4w67xn6e6ya.3uxcyvge8is62",
"type": "bafyreiabkcyd6d4yn23ercz7hgf6lt6qzifbangzalh2zlpno4feqylvti",
"uniqueKey": "rel-65dc68a44ac3df3648e9b872"
},
"objectTypes": [
"ot-relation"
],
"relationLinks": [
{
"key": "spaceId",
"format": "object"
},
{
"key": "internalFlags",
"format": "number"
},
{
"key": "relationFormatObjectTypes",
"format": "object"
},
{
"key": "id",
"format": "object"
},
{
"key": "relationFormat",
"format": "number"
},
{
"key": "layout",
"format": "number"
},
{
"key": "uniqueKey"
},
{
"key": "name",
"format": "shorttext"
},
{
"key": "relationKey"
},
{
"key": "creator",
"format": "object"
},
{
"key": "createdDate",
"format": "date"
},
{
"key": "snippet"
},
{
"key": "backlinks",
"format": "object"
},
{
"key": "description"
},
{
"key": "iconEmoji",
"format": "emoji"
},
{
"key": "iconImage",
"format": "file"
},
{
"key": "type",
"format": "object"
},
{
"key": "layoutAlign",
"format": "number"
},
{
"key": "coverId"
},
{
"key": "coverScale",
"format": "number"
},
{
"key": "coverType",
"format": "number"
},
{
"key": "coverX",
"format": "number"
},
{
"key": "coverY",
"format": "number"
},
{
"key": "lastModifiedDate",
"format": "date"
},
{
"key": "lastModifiedBy",
"format": "object"
},
{
"key": "lastOpenedDate",
"format": "date"
},
{
"key": "featuredRelations",
"format": "object"
},
{
"key": "isFavorite",
"format": "checkbox"
},
{
"key": "workspaceId",
"format": "object"
},
{
"key": "links",
"format": "object"
},
{
"key": "restrictions",
"format": "number"
}
],
"key": "65dc68a44ac3df3648e9b872"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
tests/integration/testdata/saturn.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -2,21 +2,49 @@ package integration
import (
"context"
"log"
"testing"
"time"
"github.com/anyproto/any-sync/app"
"github.com/cheggaaa/mb/v3"
"github.com/globalsign/mgo/bson"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
"github.com/anyproto/anytype-heart/core/application"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/core/subscription"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/builtinobjects"
)
func createAccountAndStartApp(t *testing.T) (*application.Service, *model.Account) {
type testApplication struct {
appService *application.Service
account *model.Account
eventQueue *mb.MB[*pb.EventMessage]
}
func (a *testApplication) personalSpaceId() string {
return a.account.Info.AccountSpaceId
}
func (a *testApplication) waitEventMessage(t *testing.T, pred func(msg *pb.EventMessage) bool) {
queueCond := a.eventQueue.NewCond().WithFilter(func(msg *pb.EventMessage) bool {
return pred(msg)
})
queueCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := queueCond.WaitOne(queueCtx)
require.NoError(t, err)
}
func createAccountAndStartApp(t *testing.T, defaultUsecase pb.RpcObjectImportUseCaseRequestUseCase) *testApplication {
repoDir := t.TempDir()
ctx := context.Background()
@ -33,8 +61,14 @@ func createAccountAndStartApp(t *testing.T) (*application.Service, *model.Accoun
})
t.Log(mnemonic)
eventQueue := mb.New[*pb.EventMessage](0)
sender := event.NewCallbackSender(func(event *pb.Event) {
t.Log(event)
for _, msg := range event.Messages {
err := eventQueue.Add(ctx, msg)
if err != nil {
log.Println("event queue error:", err)
}
}
})
app.SetEventSender(sender)
@ -46,8 +80,13 @@ func createAccountAndStartApp(t *testing.T) (*application.Service, *model.Accoun
})
require.NoError(t, err)
objCreator := getService[builtinobjects.BuiltinObjects](app)
_, err = objCreator.CreateObjectsForUseCase(session.NewContext(), acc.Info.AccountSpaceId, pb.RpcObjectImportUseCaseRequest_GET_STARTED)
testApp := &testApplication{
appService: app,
account: acc,
eventQueue: eventQueue,
}
objCreator := getService[builtinobjects.BuiltinObjects](testApp)
_, err = objCreator.CreateObjectsForUseCase(session.NewContext(), acc.Info.AccountSpaceId, defaultUsecase)
require.NoError(t, err)
t.Cleanup(func() {
@ -57,10 +96,55 @@ func createAccountAndStartApp(t *testing.T) (*application.Service, *model.Accoun
require.NoError(t, err)
})
return app, acc
return testApp
}
func getService[T any](appService *application.Service) T {
a := appService.GetApp()
func getService[T any](testApp *testApplication) T {
a := testApp.appService.GetApp()
return app.MustComponent[T](a)
}
type testSubscription struct {
subscriptionId string
}
func newTestSubscription(t *testing.T, app *testApplication, keys []domain.RelationKey, filters []*model.BlockContentDataviewFilter) *testSubscription {
keysConverted := make([]string, 0, len(keys))
for _, key := range keys {
keysConverted = append(keysConverted, key.String())
}
subscriptionId := bson.NewObjectId().Hex()
subscriptionService := getService[subscription.Service](app)
_, err := subscriptionService.Search(pb.RpcObjectSearchSubscribeRequest{
SubId: subscriptionId,
Keys: keysConverted,
Filters: filters,
})
require.NoError(t, err)
return &testSubscription{
subscriptionId: subscriptionId,
}
}
func (s *testSubscription) waitOneObjectDetailsSet(t *testing.T, app *testApplication, assertion func(t *testing.T, msg *pb.EventObjectDetailsSet)) {
app.waitEventMessage(t, func(msg *pb.EventMessage) bool {
if v := msg.GetObjectDetailsSet(); v != nil {
if slices.Contains(v.SubIds, s.subscriptionId) {
assertion(t, v)
return true
}
}
return false
})
}
func (s *testSubscription) waitObjectDetailsSetWithPredicate(t *testing.T, app *testApplication, assertion func(t *testing.T, msg *pb.EventObjectDetailsSet) bool) {
app.waitEventMessage(t, func(msg *pb.EventMessage) bool {
if v := msg.GetObjectDetailsSet(); v != nil {
if slices.Contains(v.SubIds, s.subscriptionId) {
return assertion(t, v)
}
}
return false
})
}