1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-10 18:10:49 +09:00
This commit is contained in:
Sergey Cherepanov 2021-03-15 10:16:24 +03:00
commit 02fc8b7a48
No known key found for this signature in database
GPG key ID: 085319C64294F576
108 changed files with 11694 additions and 1695 deletions

View file

@ -393,7 +393,7 @@ jobs:
cp ./pb/protos/service/*.proto protobuf/protos
cp ./pkg/lib/pb/model/protos/*.proto protobuf/protos
protoc -I ./ --swift_opt=FileNaming=DropPath --swift_opt=Visibility=Internal --swift_out=./protobuf pb/protos/*.proto pkg/lib/pb/model/protos/*.proto
protoc -I ./ --swift_opt=FileNaming=DropPath --swift_opt=Visibility=Public --swift_out=./protobuf pb/protos/*.proto pkg/lib/pb/model/protos/*.proto
- run:
name: collect artifacts
command: |

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ package-lock.json
/vendor/
*_mock.go
lib-server/service.pb.go
dist/cli

View file

@ -86,13 +86,13 @@ build-js-addon:
build-ios: setup-go
@echo 'Building library for iOS...'
@$(eval FLAGS := $$(shell govvv -flags | sed 's/main/github.com\/anytypeio\/go-anytype-middleware\/core/g'))
@GOPRIVATE=github.com/anytypeio gomobile bind -tags nogrpcserver -ldflags "$(FLAGS)" -v -target=ios github.com/anytypeio/go-anytype-middleware/clientlibrary/service github.com/anytypeio/go-anytype-middleware/core
@GOPRIVATE=github.com/anytypeio gomobile bind -tags nogrpcserver -ldflags "$(FLAGS)" -v -target=ios -o Lib.framework github.com/anytypeio/go-anytype-middleware/clientlibrary/service github.com/anytypeio/go-anytype-middleware/core
@mkdir -p dist/ios/ && mv Lib.framework dist/ios/
build-android: setup-go
@echo 'Building library for Android...'
@$(eval FLAGS := $$(shell govvv -flags | sed 's/main/github.com\/anytypeio\/go-anytype-middleware\/core/g'))
@GOPRIVATE=github.com/anytypeio gomobile bind -tags nogrpcserver -ldflags "$(FLAGS)" -v -target=android -o mobile.aar github.com/anytypeio/go-anytype-middleware/clientlibrary/service github.com/anytypeio/go-anytype-middleware/core
@GOPRIVATE=github.com/anytypeio gomobile bind -tags nogrpcserver -ldflags "$(FLAGS)" -v -target=android -o lib.aar github.com/anytypeio/go-anytype-middleware/clientlibrary/service github.com/anytypeio/go-anytype-middleware/core
@mkdir -p dist/android/ && mv lib.aar dist/android/
setup-protoc-go:
@ -165,7 +165,7 @@ protos: protos-go protos-server protos-docs
protos-swift:
@echo 'Generating protobuf packages (Swift)...'
@protoc -I ./ --swift_opt=FileNaming=DropPath --swift_opt=Visibility=Internal --swift_out=./dist/ios/pb pb/protos/*.proto pkg/lib/pb/model/protos/*.proto pkg/lib/pb/relation/protos/*.proto
@protoc -I ./ --swift_opt=FileNaming=DropPath --swift_opt=Visibility=Public --swift_out=./dist/ios/pb pb/protos/*.proto pkg/lib/pb/model/protos/*.proto pkg/lib/pb/relation/protos/*.proto
protos-js:
@echo 'Generating protobuf packages (JS)...'
@ -176,10 +176,15 @@ protos-java:
@echo 'Generating protobuf packages (Java)...'
@protoc -I ./ --java_out=./dist/android/pb pb/protos/*.proto pkg/lib/pb/model/protos/*.proto pkg/lib/pb/relation/protos/*.proto
build-cli:
@echo 'Building middleware cli...'
@$(eval FLAGS := $$(shell govvv -flags -pkg github.com/anytypeio/go-anytype-middleware/core))
@go build -v -o dist/cli -ldflags "$(FLAGS)" ./cmd/cli
build-server: protos-server
@echo 'Building middleware server...'
@$(eval FLAGS := $$(shell govvv -flags -pkg github.com/anytypeio/go-anytype-middleware/core))
@go build -i -v -o dist/server -ldflags "$(FLAGS)" ./cmd/grpcserver/grpc.go
@go build -v -o dist/server -ldflags "$(FLAGS)" ./cmd/grpcserver/grpc.go
build-server-debug: protos-server
@echo 'Building middleware server with debug symbols...'
@ -192,6 +197,7 @@ run-server: build-server
install-dev-js-addon: setup build-lib build-js-addon protos-js
@echo 'Installing JS-addon (dev-mode)...'
@rm -rf ../js-anytype/build
@cp -r clientlibrary/jsaddon/build ../js-anytype/
@cp -r dist/js/pb/* ../js-anytype/dist/lib

View file

@ -71,3 +71,13 @@ https://github.com/uw-labs/bloomrpc
**CLI**
https://github.com/njpatel/grpcc
### Running with prometheus and grafana
- `cd metrics/docker` cd into folder with docker-compose file
- `docker-compose up` - run the prometheus/grafana
- use `ANYTYPE_PROM=0.0.0.0:9094` when running middleware to enable metrics collection. Client commands metrics available only in gRPC mode
- open http://127.0.0.1:3000 to view collected metrics in Grafana. You can find several dashboards there:
- **Threads gRPC client** for go-threads client metrics(when you make requests to other nodes)
- **Threads gRPC server** for go-threads server metrics(when other nodes make requests to you)
- **MW** internal middleware metrics such as changes, added and created threads histograms
- **MW commands server** metrics for clients commands. Works only in grpc-server mode

View file

@ -143,4 +143,4 @@ func TrimSingleWhitespaceNormalizeLine(in string) string {
}
return string(result)
}
}

View file

@ -17,7 +17,7 @@ import (
)
//export SetEventHandler
func SetEventHandler(proxyFunc C.proxyFunc, ctx unsafe.Pointer) {
func SetEventHandler(pf C.proxyFunc, ctx unsafe.Pointer) {
service.SetEventHandler(func(event *pb.Event) {
b, err := proto.Marshal(event)
if err != nil {
@ -25,8 +25,8 @@ func SetEventHandler(proxyFunc C.proxyFunc, ctx unsafe.Pointer) {
return
}
if proxyFunc != nil {
C.ProxyCall(proxyFunc, ctx, C.CString(""), C.CString(string(b)), C.int(len(b)))
if pf != nil {
C.ProxyCall(pf, ctx, C.CString(""), C.CString(string(b)), C.int(len(b)))
} else {
eventB, _ := json.Marshal(event)
fmt.Printf("failed to send event to nil eventHandler: %s", string(eventB))

24
cmd/cli/cli.go Normal file
View file

@ -0,0 +1,24 @@
package main
import (
"fmt"
"github.com/spf13/cobra"
)
var CliCmd = &cobra.Command{
Use: "cli",
Short: "CLI utilities",
Long: `Various CLI utilities for cafe operations.`,
}
func init() {
// subcommands
CliCmd.AddCommand(migrateCmd)
// local flags
}
func main() {
if err := CliCmd.Execute(); err != nil {
fmt.Printf("failed to execute: %s", err.Error())
}
}

53
cmd/cli/migrate.go Normal file
View file

@ -0,0 +1,53 @@
package main
import (
"github.com/anytypeio/go-anytype-middleware/core"
"github.com/anytypeio/go-anytype-middleware/core/event"
"github.com/anytypeio/go-anytype-middleware/pb"
core2 "github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/spf13/cobra"
"os"
)
var migrateCmd = &cobra.Command{
Use: "migrate",
Short: "Migrations commands",
}
var (
migrateRepoPath string
migrateAccount string
)
var reindex = &cobra.Command{
Use: "reindex",
Short: "Reindex all existing objects in the local repo",
Run: func(c *cobra.Command, args []string) {
var mw = core.New()
mw.EventSender = event.NewCallbackSender(func(event *pb.Event) {
// nothing to do
})
resp := mw.AccountSelect(&pb.RpcAccountSelectRequest{Id: migrateAccount, RootPath: migrateRepoPath})
if resp.Error.Code != 0 {
c.PrintErrf("failed to open account repo: %s\n", resp.Error.Description)
return
}
migrated, err := core2.ReindexAll(mw.Anytype.(*core2.Anytype))
if err != nil {
c.PrintErrf("failed to run reindex migration: %s\n", resp.Error.Description)
}
c.Printf("reindexed %d objects\n", migrated)
c.Println("Shutting down account...")
mw.Shutdown(&pb.RpcShutdownRequest{})
},
}
func init() {
// subcommands
homeDir, _ := os.UserHomeDir()
migrateCmd.AddCommand(reindex)
migrateCmd.PersistentFlags().StringVarP(&migrateRepoPath, "repo", "r", homeDir+"/Library/Application Support/anytype2/dev/data", "path to dir with accounts folder")
migrateCmd.PersistentFlags().StringVarP(&migrateAccount, "account", "a", "", "id of account in the repo folder")
}

View file

@ -4,6 +4,8 @@ package main
import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"net"
"net/http"
_ "net/http/pprof"
@ -81,8 +83,17 @@ func main() {
}
webaddr = webLis.Addr().String()
server := grpc.NewServer(grpc.MaxRecvMsgSize(20 * 1024 * 1024))
var unaryServerInterceptor grpc.UnaryServerInterceptor
if metrics.Enabled {
unaryServerInterceptor = grpc_prometheus.UnaryServerInterceptor
}
server := grpc.NewServer(grpc.MaxRecvMsgSize(20*1024*1024), grpc.UnaryInterceptor(unaryServerInterceptor))
service.RegisterClientCommandsServer(server, mw)
if metrics.Enabled {
grpc_prometheus.EnableHandlingTimeHistogram()
//grpc_prometheus.Register(server)
}
webrpc := grpcweb.WrapServer(
server,

View file

@ -24,8 +24,8 @@ import (
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
)
const cafeUrl = "https://cafe1.anytype.io"
const cafePeerId = "12D3KooWKwPC165PptjnzYzGrEs7NSjsF5vvMmxmuqpA2VfaBbLw"
const defaultCafeUrl = "https://cafe1.anytype.io"
const defaultCafePeerId = "12D3KooWKwPC165PptjnzYzGrEs7NSjsF5vvMmxmuqpA2VfaBbLw"
// we cannot check the constant error from badger because they hardcoded it there
const errSubstringMultipleAnytypeInstance = "Cannot acquire directory lock"
@ -53,7 +53,9 @@ func checkInviteCode(code string, account string) error {
Account: account,
})
req, err := http.NewRequest("POST", cafeUrl+"/alpha-invite", bytes.NewBuffer(jsonStr))
// TODO: here we always using the default cafe address, because we want to check invite code only on our server
// this code should be removed with a public release
req, err := http.NewRequest("POST", defaultCafeUrl+"/alpha-invite", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
@ -81,7 +83,7 @@ func checkInviteCode(code string, account string) error {
return fmt.Errorf("failed to decode response json: %s", err.Error())
}
pubk, err := wallet.NewPubKeyFromAddress(wallet.KeypairTypeDevice, cafePeerId)
pubk, err := wallet.NewPubKeyFromAddress(wallet.KeypairTypeDevice, defaultCafePeerId)
if err != nil {
return fmt.Errorf("failed to decode cafe pubkey: %s", err.Error())
}

View file

@ -19,7 +19,6 @@ type Ctrl interface {
GetRelations(objectId string) (relations []*pbrelation.Relation, err error)
CreateSmartBlock(sbType coresb.SmartBlockType, details *types.Struct, relations []*pbrelation.Relation) (id string, newDetails *types.Struct, err error)
GetObjectType(url string) (objectType *pbrelation.ObjectType, err error)
UpdateExtraRelations(ctx *state.Context, id string, relations []*pbrelation.Relation, createIfMissing bool) (err error)
AddExtraRelations(ctx *state.Context, id string, relations []*pbrelation.Relation) (relationsWithKeys []*pbrelation.Relation, err error)
RemoveExtraRelations(ctx *state.Context, id string, relationKeys []string) (err error)

View file

@ -2,6 +2,7 @@ package objects
import (
"errors"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
@ -11,14 +12,15 @@ import (
pbrelation "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/gogo/protobuf/types"
"github.com/google/martian/log"
)
const (
CustomObjectTypeURLPrefix = "https://anytype.io/schemas/object/custom/"
BundledObjectTypeURLPrefix = "https://anytype.io/schemas/object/bundled/"
//CustomObjectTypeURLPrefix = "https://anytype.io/schemas/object/custom/"
BundledObjectTypeURLPrefix = "_ot"
)
var log = logging.Logger("anytype-core-db")
func New(
pageStore localstore.ObjectStore,
objectTypeUrl string,
@ -57,8 +59,15 @@ func (sp setOfObjects) Create(relations []*pbrelation.Relation, rec database.Rec
rec.Details = &types.Struct{Fields: make(map[string]*types.Value)}
}
var relsToSet []*pbrelation.Relation
for _, rel := range relations {
if pbtypes.HasField(rec.Details, rel.Key) {
relsToSet = append(relsToSet, rel)
}
}
rec.Details.Fields[bundle.RelationKeyType.String()] = pbtypes.StringList([]string{sp.objectTypeUrl})
id, newDetails, err := sp.createSmartBlock(coresb.SmartBlockTypePage, rec.Details, nil)
id, newDetails, err := sp.createSmartBlock(coresb.SmartBlockTypePage, rec.Details, relsToSet)
if err != nil {
return rec, err
}
@ -74,18 +83,6 @@ func (sp setOfObjects) Create(relations []*pbrelation.Relation, rec database.Rec
sub.Subscribe([]string{id})
}
var relsToSet []*pbrelation.Relation
for _, rel := range relations {
if pbtypes.HasField(rec.Details, rel.Key) {
relsToSet = append(relsToSet, rel)
}
}
err = sp.setRelations(id, relsToSet)
if err != nil {
return rec, err
}
return rec, nil
}

View file

@ -221,9 +221,17 @@ func TestBasic_SetRelationKey(t *testing.T) {
Key: "key",
})
require.NoError(t, err)
require.Len(t, sb.Results.Events, 1)
require.Len(t, sb.Results.Events[0], 1)
assert.Equal(t, "key", sb.Results.Events[0][0].Msg.GetBlockSetRelation().GetKey().Value)
var setRelationEvent *pb.EventBlockSetRelation
for _, ev := range sb.Results.Events {
for _, em := range ev {
if m := em.Msg.GetBlockSetRelation(); m != nil {
setRelationEvent = m
break
}
}
}
require.NotNil(t, setRelationEvent)
assert.Equal(t, "key", setRelationEvent.GetKey().Value)
assert.Equal(t, "key", sb.NewState().Pick("2").Model().GetRelation().Key)
})
t.Run("not relation block", func(t *testing.T) {

View file

@ -13,6 +13,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/pb"
bundle "github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/database"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
pbrelation "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
@ -28,7 +29,6 @@ const defaultLimit = 50
var log = logging.Logger("anytype-mw-editor")
type Dataview interface {
GetObjectTypeURL(ctx *state.Context, blockId string) (string, error)
GetAggregatedRelations(blockId string) ([]*pbrelation.Relation, error)
GetDataviewRelations(blockId string) ([]*pbrelation.Relation, error)
@ -48,11 +48,13 @@ type Dataview interface {
UpdateRecord(ctx *state.Context, blockId string, recID string, rec model.ObjectDetails) error
DeleteRecord(ctx *state.Context, blockId string, recID string) error
WithSystemObjects(yes bool)
smartblock.SmartblockOpenListner
}
func NewDataview(sb smartblock.SmartBlock, objTypeGetter ObjectTypeGetter) Dataview {
return &dataviewCollectionImpl{SmartBlock: sb, ObjectTypeGetter: objTypeGetter}
func NewDataview(sb smartblock.SmartBlock) Dataview {
return &dataviewCollectionImpl{SmartBlock: sb}
}
type dataviewImpl struct {
@ -69,14 +71,14 @@ type dataviewImpl struct {
recordsUpdatesCancel context.CancelFunc
}
type ObjectTypeGetter interface {
GetObjectType(url string) (objectType *pbrelation.ObjectType, err error)
}
type dataviewCollectionImpl struct {
smartblock.SmartBlock
ObjectTypeGetter
dataviews []*dataviewImpl
dataviews []*dataviewImpl
withSystemObjects bool
}
func (d *dataviewCollectionImpl) WithSystemObjects(yes bool) {
d.withSystemObjects = yes
}
func (d *dataviewCollectionImpl) AddRelation(ctx *state.Context, blockId string, relation pbrelation.Relation, showEvent bool) (*pbrelation.Relation, error) {
@ -288,7 +290,7 @@ func (d *dataviewCollectionImpl) GetAggregatedRelations(blockId string) ([]*pbre
return nil, err
}
objectType, err := d.GetObjectType(tb.GetSource())
objectType, err := localstore.GetObjectType(d.Anytype().ObjectStore(), tb.GetSource())
if err != nil {
return nil, err
}
@ -473,7 +475,7 @@ func (d *dataviewCollectionImpl) CreateView(ctx *state.Context, id string, view
}
if len(view.Relations) == 0 {
objType, err := d.ObjectTypeGetter.GetObjectType(tb.GetSource())
objType, err := localstore.GetObjectType(d.Anytype().ObjectStore(), tb.GetSource())
if err != nil {
return nil, fmt.Errorf("object type not found")
}
@ -574,7 +576,7 @@ func (d *dataviewCollectionImpl) UpdateRecord(_ *state.Context, blockId string,
}
dv := d.getDataviewImpl(dvBlock)
objectType, err := d.GetObjectType(source)
objectType, err := localstore.GetObjectType(d.Anytype().ObjectStore(), source)
if err != nil {
return err
}
@ -704,7 +706,7 @@ func (d *dataviewCollectionImpl) fetchAndGetEventsMessages(dv *dataviewImpl, dvB
}
// todo: inject schema
objectType, err := d.GetObjectType(source)
objectType, err := localstore.GetObjectType(d.Anytype().ObjectStore(), source)
if err != nil {
return nil, err
}
@ -722,12 +724,13 @@ func (d *dataviewCollectionImpl) fetchAndGetEventsMessages(dv *dataviewImpl, dvB
recordsSub := database.NewSubscription(nil, recordsCh)
depRecordsSub := database.NewSubscription(nil, depRecordsCh)
entries, cancelRecordSubscripton, total, err := db.QueryAndSubscribeForChanges(&sch, database.Query{
Relations: activeView.Relations,
Filters: activeView.Filters,
Sorts: activeView.Sorts,
Limit: dv.limit,
Offset: dv.offset,
entries, cancelRecordSubscription, total, err := db.QueryAndSubscribeForChanges(&sch, database.Query{
Relations: activeView.Relations,
Filters: activeView.Filters,
Sorts: activeView.Sorts,
Limit: dv.limit,
Offset: dv.offset,
WithSystemObjects: d.withSystemObjects,
}, recordsSub)
if err != nil {
return nil, err
@ -748,7 +751,7 @@ func (d *dataviewCollectionImpl) fetchAndGetEventsMessages(dv *dataviewImpl, dvB
continue
}
if rel, _ := sch.GetRelationByKey(key); rel != nil && (rel.GetFormat() == pbrelation.RelationFormat_object || rel.GetFormat() == pbrelation.RelationFormat_file) {
if rel, _ := sch.GetRelationByKey(key); rel != nil && (rel.GetFormat() == pbrelation.RelationFormat_object || rel.GetFormat() == pbrelation.RelationFormat_file) && (len(rel.ObjectTypes) == 0 || rel.ObjectTypes[0] != bundle.TypeKeyRelation.URL()) {
depIdsToAdd := pbtypes.GetStringListValue(item)
for _, depId := range depIdsToAdd {
if _, exists := depIdsMap[depId]; !exists {
@ -768,7 +771,7 @@ func (d *dataviewCollectionImpl) fetchAndGetEventsMessages(dv *dataviewImpl, dvB
dv.depsUpdatesSubscription = depRecordsSub
dv.recordsUpdatesCancel = func() {
cancelDepsSubscripton()
cancelRecordSubscripton()
cancelRecordSubscription()
}
var msgs = []*pb.EventMessage{

View file

@ -21,7 +21,7 @@ func TestDataview_SetDetails(t *testing.T) {
Key: "key",
Value: pbtypes.String("value"),
},
})
}, false)
require.NoError(t, err)
require.NotNil(t, event)
}

View file

@ -0,0 +1,153 @@
package editor
import (
"github.com/anytypeio/go-anytype-middleware/core/block/database"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/template"
"github.com/anytypeio/go-anytype-middleware/core/block/meta"
"github.com/anytypeio/go-anytype-middleware/core/block/source"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
)
const (
viewIdLibrary = "library"
viewIdMarketplace = "marketplace"
)
func NewMarketplaceType(ms meta.Service, dbCtrl database.Ctrl) *MarketplaceType {
return &MarketplaceType{Set: NewSet(ms, dbCtrl)}
}
type MarketplaceType struct {
*Set
}
func (p *MarketplaceType) Init(s source.Source, allowEmpty bool, _ []string) (err error) {
err = p.SmartBlock.Init(s, true, nil)
if err != nil {
return err
}
ot := bundle.TypeKeyObjectType.URL()
templates := []template.StateTransformer{
template.WithTitle,
template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList([]string{ot})),
template.WithObjectTypesAndLayout([]string{bundle.TypeKeySet.URL()})}
dataview := model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Source: ot,
Relations: bundle.MustGetType(bundle.TypeKeyObjectType).Relations,
Views: []*model.BlockContentDataviewView{
{
Id: viewIdMarketplace,
Type: model.BlockContentDataviewView_Gallery,
Name: "Marketplace",
Sorts: []*model.BlockContentDataviewSort{},
Relations: []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyId.String(), IsVisible: false},
{Key: bundle.RelationKeyName.String(), IsVisible: true},
{Key: bundle.RelationKeyDescription.String(), IsVisible: true},
{Key: bundle.RelationKeyIconImage.String(), IsVisible: true},
{Key: bundle.RelationKeyCreator.String(), IsVisible: true}},
Filters: []*model.BlockContentDataviewFilter{{
RelationKey: bundle.RelationKeyIsHidden.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
}},
},
{
Id: viewIdLibrary,
Type: model.BlockContentDataviewView_Gallery,
Name: "Library",
Sorts: []*model.BlockContentDataviewSort{},
Relations: []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyId.String(), IsVisible: false},
{Key: bundle.RelationKeyName.String(), IsVisible: true},
{Key: bundle.RelationKeyDescription.String(), IsVisible: true},
{Key: bundle.RelationKeyIconImage.String(), IsVisible: true},
{Key: bundle.RelationKeyCreator.String(), IsVisible: true}},
Filters: []*model.BlockContentDataviewFilter{{
RelationKey: bundle.RelationKeyIsHidden.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
}},
},
},
},
}
templates = append(templates, template.WithDataview(dataview, true), template.WithDetailName("Types"), template.WithDetailIconEmoji("📒"))
if err = template.ApplyTemplate(p, nil, templates...); err != nil {
return
}
p.WithSystemObjects(true)
return p.FillAggregatedOptions(nil)
}
type MarketplaceRelation struct {
*Set
}
func NewMarketplaceRelation(ms meta.Service, dbCtrl database.Ctrl) *MarketplaceRelation {
return &MarketplaceRelation{Set: NewSet(ms, dbCtrl)}
}
func (p *MarketplaceRelation) Init(s source.Source, allowEmpty bool, _ []string) (err error) {
err = p.SmartBlock.Init(s, true, nil)
if err != nil {
return err
}
ot := bundle.TypeKeyRelation.URL()
templates := []template.StateTransformer{
template.WithTitle,
template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList([]string{ot})),
template.WithObjectTypesAndLayout([]string{bundle.TypeKeySet.URL()})}
dataview := model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Source: ot,
Relations: bundle.MustGetType(bundle.TypeKeyRelation).Relations,
Views: []*model.BlockContentDataviewView{
{
Id: viewIdMarketplace,
Type: model.BlockContentDataviewView_Gallery,
Name: "Marketplace",
Sorts: []*model.BlockContentDataviewSort{},
Relations: []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyId.String(), IsVisible: false},
{Key: bundle.RelationKeyDescription.String(), IsVisible: true},
{Key: bundle.RelationKeyIconImage.String(), IsVisible: true},
{Key: bundle.RelationKeyCreator.String(), IsVisible: true}},
Filters: []*model.BlockContentDataviewFilter{{
RelationKey: bundle.RelationKeyIsHidden.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
}},
},
{
Id: viewIdLibrary,
Type: model.BlockContentDataviewView_Gallery,
Name: "Library",
Sorts: []*model.BlockContentDataviewSort{},
Relations: []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyId.String(), IsVisible: false},
{Key: bundle.RelationKeyDescription.String(), IsVisible: true},
{Key: bundle.RelationKeyIconImage.String(), IsVisible: true},
{Key: bundle.RelationKeyCreator.String(), IsVisible: true}},
Filters: []*model.BlockContentDataviewFilter{{
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
}},
},
},
},
}
templates = append(templates, template.WithDataview(dataview, true), template.WithDetailName("Relations"), template.WithDetailIconEmoji("📒"))
if err = template.ApplyTemplate(p, nil, templates...); err != nil {
return
}
return p.FillAggregatedOptions(nil)
}

View file

@ -1,21 +1,25 @@
package editor
import (
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/database"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/template"
"github.com/anytypeio/go-anytype-middleware/core/block/meta"
"github.com/anytypeio/go-anytype-middleware/core/block/source"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/slice"
"github.com/google/uuid"
)
type ObjectType struct {
smartblock.SmartBlock
*Set
}
func NewObjectType(m meta.Service) *ObjectType {
sb := smartblock.New(m)
func NewObjectType(m meta.Service, dbCtrl database.Ctrl) *ObjectType {
return &ObjectType{
SmartBlock: sb,
Set: NewSet(m, dbCtrl),
}
}
@ -23,7 +27,74 @@ func (p *ObjectType) Init(s source.Source, _ bool, _ []string) (err error) {
if err = p.SmartBlock.Init(s, true, nil); err != nil {
return
}
dataview := model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Source: p.Id(),
Views: []*model.BlockContentDataviewView{
{
Id: uuid.New().String(),
Type: model.BlockContentDataviewView_Table,
Name: "All",
Sorts: []*model.BlockContentDataviewSort{
{
RelationKey: "name",
Type: model.BlockContentDataviewSort_Asc,
},
},
Relations: []*model.BlockContentDataviewRelation{},
Filters: nil,
},
},
},
}
rels := p.Relations()
recommendedRelationsKeys := pbtypes.GetStringList(p.Details(), bundle.RelationKeyRecommendedRelations.String())
for _, rel := range bundle.RequiredInternalRelations {
if slice.FindPos(recommendedRelationsKeys, rel.String()) == -1 {
recommendedRelationsKeys = append(recommendedRelationsKeys, rel.String())
}
}
recommendedLayout := pbtypes.GetString(p.Details(), bundle.RelationKeyRecommendedLayout.String())
if recommendedLayout == "" {
recommendedLayout = relation.ObjectType_basic.String()
} else if _, ok := relation.ObjectTypeLayout_value[recommendedLayout]; !ok {
recommendedLayout = relation.ObjectType_basic.String()
}
recommendedLayoutObj := bundle.MustGetLayout(relation.ObjectTypeLayout(relation.ObjectTypeLayout_value[recommendedLayout]))
for _, rel := range recommendedLayoutObj.RequiredRelations {
if slice.FindPos(recommendedRelationsKeys, rel.Key) == -1 {
recommendedRelationsKeys = append(recommendedRelationsKeys, rel.Key)
}
}
var recommendedRelations []*relation.Relation
for _, rk := range recommendedRelationsKeys {
rel := pbtypes.GetRelation(rels, rk)
if rel == nil {
rel, _ = bundle.GetRelation(bundle.RelationKey(rk))
if rel == nil {
continue
}
}
relCopy := pbtypes.CopyRelation(rel)
relCopy.Scope = relation.Relation_type
recommendedRelations = append(recommendedRelations, relCopy)
dataview.Dataview.Relations = append(dataview.Dataview.Relations, relCopy)
dataview.Dataview.Views[0].Relations = append(dataview.Dataview.Views[0].Relations, &model.BlockContentDataviewRelation{
Key: rel.Key,
IsVisible: !rel.Hidden,
})
}
return template.ApplyTemplate(p, nil,
template.WithEmpty,
template.WithObjectTypesAndLayout([]string{bundle.TypeKeyObjectType.URL()}))
}
template.WithDataview(dataview, true),
template.WithObjectTypesAndLayout([]string{bundle.TypeKeyObjectType.URL()}),
template.WithObjectTypeRecommendedRelationsMigration(recommendedRelations),
template.WithObjectTypeLayoutMigration())
}

View file

@ -49,13 +49,14 @@ type Page struct {
}
func (p *Page) Init(s source.Source, _ bool, objectTypeUrls []string) (err error) {
if err = p.SmartBlock.Init(s, true, nil); err != nil {
return
}
if objectTypeUrls == nil {
objectTypeUrls = []string{bundle.TypeKeyPage.URL()}
}
if err = p.SmartBlock.Init(s, true, nil); err != nil {
return
}
var layout relation.ObjectTypeLayout
otypes := p.MetaService().FetchObjectTypes(objectTypeUrls)
for _, ot := range otypes {

View file

@ -53,8 +53,8 @@ func (p *Profile) Init(s source.Source, _ bool, _ []string) (err error) {
)
}
func (p *Profile) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail) (err error) {
if err = p.SmartBlock.SetDetails(ctx, details); err != nil {
func (p *Profile) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail, showEvent bool) (err error) {
if err = p.SmartBlock.SetDetails(ctx, details, showEvent); err != nil {
return
}
meta := p.SmartBlock.Meta()

View file

@ -21,7 +21,7 @@ func TestProfile_SetDetails(t *testing.T) {
Key: "key",
Value: pbtypes.String("value"),
},
})
}, false)
require.NoError(t, err)
require.NotNil(t, event)
}

View file

@ -29,7 +29,7 @@ func NewSet(
sb.Basic = basic.NewBasic(sb)
sb.IHistory = basic.NewHistory(sb)
sb.Dataview = dataview.NewDataview(sb, dbCtrl)
sb.Dataview = dataview.NewDataview(sb)
sb.Router = database.New(dbCtrl)
sb.Text = stext.NewText(sb.SmartBlock)
return sb
@ -54,7 +54,7 @@ func (p *Set) Init(s source.Source, allowEmpty bool, _ []string) (err error) {
if p.Id() == p.Anytype().PredefinedBlocks().SetPages {
dataview := model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Source: "https://anytype.io/schemas/object/bundled/page",
Source: "_otpage",
Relations: bundle.MustGetType(bundle.TypeKeyPage).Relations,
Views: []*model.BlockContentDataviewView{
{
@ -78,18 +78,13 @@ func (p *Set) Init(s source.Source, allowEmpty bool, _ []string) (err error) {
},
},
}
templates = append(templates, template.WithDataview(dataview), template.WithDetailName("Pages"), template.WithDetailIconEmoji("📒"))
templates = append(templates, template.WithDataview(dataview, false), template.WithDetailName("Pages"), template.WithDetailIconEmoji("📒"))
} else if dvBlock := p.Pick("dataview"); dvBlock != nil {
templates = append(templates, template.WithDetail(bundle.RelationKeySetOf, pbtypes.String(dvBlock.Model().GetDataview().Source)))
}
st := p.NewState()
if err = template.ApplyTemplate(p, st, templates...); err != nil {
return
templates = append(templates, template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList([]string{dvBlock.Model().GetDataview().Source})))
}
err = p.Apply(st)
if err != nil {
log.Errorf("failed to apply state: %s", err.Error())
if err = template.ApplyTemplate(p, nil, templates...); err != nil {
return
}
return p.FillAggregatedOptions(nil)
@ -99,8 +94,8 @@ func (p *Set) InitDataview(blockContent model.BlockContentOfDataview, name strin
s := p.NewState()
return template.ApplyTemplate(p, s,
template.WithDetailName(name),
template.WithDetail(bundle.RelationKeySetOf, pbtypes.String(blockContent.Dataview.Source)),
template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList([]string{blockContent.Dataview.Source})),
template.WithDetailIconEmoji(icon),
template.WithDataview(blockContent),
template.WithDataview(blockContent, false),
)
}

View file

@ -0,0 +1 @@
package smartblock

View file

@ -8,7 +8,6 @@ import (
"sync"
"time"
"github.com/anytypeio/go-anytype-middleware/core/block/database/objects"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/meta"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
@ -61,6 +60,7 @@ func New(ms meta.Service) SmartBlock {
}
type SmartblockOpenListner interface {
// should not do any Do operations inside
SmartblockOpened(*state.Context)
}
@ -74,7 +74,7 @@ type SmartBlock interface {
Apply(s *state.State, flags ...ApplyFlag) error
History() undo.History
Anytype() core.Service
SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail) (err error)
SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail, showEvent bool) (err error)
Relations() []*pbrelation.Relation
HasRelation(relationKey string) bool
AddExtraRelations(ctx *state.Context, relations []*pbrelation.Relation) (relationsWithKeys []*pbrelation.Relation, err error)
@ -148,7 +148,7 @@ func (sb *smartBlock) Type() pb.SmartBlockType {
return sb.source.Type()
}
func (sb *smartBlock) Init(s source.Source, allowEmpty bool, _ []string) (err error) {
func (sb *smartBlock) Init(s source.Source, allowEmpty bool, objectType []string) (err error) {
if sb.Doc, err = s.ReadDoc(sb, allowEmpty); err != nil {
return fmt.Errorf("reading document: %w", err)
}
@ -157,9 +157,57 @@ func (sb *smartBlock) Init(s source.Source, allowEmpty bool, _ []string) (err er
sb.undo = undo.NewHistory(0)
sb.storeFileKeys()
sb.Doc.BlocksInit()
if len(objectType) > 0 && len(sb.ObjectTypes()) == 0 {
err = sb.SetObjectTypes(nil, objectType)
if err != nil {
return err
}
}
if err := sb.NormalizeRelations(); err != nil {
return err
}
return
}
func (sb *smartBlock) NormalizeRelations() error {
st := sb.NewState()
relations := sb.Relations()
details := sb.Details()
if details == nil || details.Fields == nil {
return nil
}
var stateChanged bool
for k := range details.Fields {
rel := pbtypes.GetRelation(relations, k)
if rel == nil {
if bundleRel, _ := bundle.GetRelation(bundle.RelationKey(k)); bundleRel != nil {
st.AddRelation(bundleRel)
stateChanged = true
log.Warnf("NormalizeRelations bundle relation is missing, have been added: %s", k)
} else {
log.Errorf("NormalizeRelations relation is missing: %s", k)
}
continue
}
if rel.Scope != pbrelation.Relation_object {
log.Warnf("NormalizeRelations change scope for relation %s", rel.Key)
st.SetExtraRelation(rel)
stateChanged = true
}
}
if stateChanged {
return sb.Apply(st)
}
return nil
}
func (sb *smartBlock) SendEvent(msgs []*pb.EventMessage) {
if sb.sendEvent != nil {
sb.sendEvent(&pb.Event{
@ -236,6 +284,9 @@ func (sb *smartBlock) fetchMeta() (details []*pb.EventBlockSetDetails, objectTyp
if ot == "" {
log.Errorf("dv relation %s(%s) has empty obj types", rel.Key, rel.Name)
} else {
if strings.HasPrefix(ot, "http") {
log.Errorf("dv rels has http source")
}
uniqueObjTypes = append(uniqueObjTypes, ot)
}
}
@ -363,9 +414,7 @@ func (sb *smartBlock) dependentSmartIds() (ids []string) {
ids = append(ids, sb.Id())
for _, ot := range sb.ObjectTypes() {
if strings.HasSuffix(ot, objects.CustomObjectTypeURLPrefix) {
ids = append(ids, strings.TrimPrefix(ot, objects.CustomObjectTypeURLPrefix))
}
ids = append(ids, ot)
}
details := sb.Details()
@ -376,9 +425,7 @@ func (sb *smartBlock) dependentSmartIds() (ids []string) {
}
// add all custom object types as dependents
for _, ot := range rel.ObjectTypes {
if strings.HasPrefix(ot, objects.CustomObjectTypeURLPrefix) {
ids = append(ids, strings.TrimPrefix(ot, objects.CustomObjectTypeURLPrefix))
}
ids = append(ids, ot)
}
if rel.Key == bundle.RelationKeyId.String() || rel.Key == bundle.RelationKeyType.String() {
@ -452,6 +499,7 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
if err != nil {
return
}
sb.Doc.(*state.State).SetChangeId(id)
if sb.undo != nil && addHistory {
act.Group = s.GroupId()
@ -506,12 +554,12 @@ func (sb *smartBlock) CheckSubscriptions() (changed bool) {
func (sb *smartBlock) NewState() *state.State {
sb.execHooks(HookOnNewState)
return sb.Doc.NewState()
return sb.Doc.NewState().SetNoObjectType(sb.Type() == pb.SmartBlockType_Archive || sb.Type() == pb.SmartBlockType_Breadcrumbs)
}
func (sb *smartBlock) NewStateCtx(ctx *state.Context) *state.State {
sb.execHooks(HookOnNewState)
return sb.Doc.NewStateCtx(ctx)
return sb.Doc.NewStateCtx(ctx).SetNoObjectType(sb.Type() == pb.SmartBlockType_Archive || sb.Type() == pb.SmartBlockType_Breadcrumbs)
}
func (sb *smartBlock) History() undo.History {
@ -522,103 +570,94 @@ func (sb *smartBlock) Anytype() core.Service {
return sb.source.Anytype()
}
func (sb *smartBlock) validateDetailsAndAddOptions(st *state.State, d *types.Struct) (err error) {
if d == nil || d.Fields == nil {
return nil
}
rels := sb.Relations()
for k, v := range d.Fields {
var found bool
for _, rel := range rels {
if rel.Key != k {
continue
}
/*if rel.DataSource != pbrelation.Relation_details {
delete(d.Fields, k)
found = true
break
}*/
if rel.Scope != pbrelation.Relation_object {
rel.Scope = pbrelation.Relation_object
st.AddRelation(rel)
}
found = true
if rel.Format == pbrelation.RelationFormat_status || rel.Format == pbrelation.RelationFormat_tag {
objType := sb.ObjectType()
options, err := sb.Anytype().ObjectStore().GetAggregatedOptions(rel.Key, rel.Format, objType)
if err != nil {
log.Errorf("failed to get aggregated options for a select/tag relation '%s' ot '%s' format '%s': %s", rel.Key, objType, rel.Format, err.Error())
return fmt.Errorf("failed to get aggregated options for a select/tag relation: %s", err.Error())
}
vals := pbtypes.GetStringListValue(v)
if len(vals) == 0 {
continue
}
var valsFiltered []string
for _, val := range vals {
var optFound bool
for _, opt := range options {
if opt.Id == val {
optFound = true
optCopy := *opt
// reset scope
optCopy.Scope = pbrelation.RelationOption_local
_, err = st.AddExtraRelationOption(*rel, optCopy)
if err != nil {
return fmt.Errorf("failed to add an option for a select/tag relation '%s': %s", rel.Key, err.Error())
}
break
}
}
if !optFound {
log.Errorf("failed to find an option for a select/tag relation '%s'", rel.Key)
continue
}
valsFiltered = append(valsFiltered, val)
}
d.Fields[k] = pbtypes.StringList(valsFiltered)
}
}
if !found {
// todo: make this an error after stabilizing of relations names and creating of migrations
log.Errorf("relation for detail '%s' doesn't exist", k)
}
}
return nil
}
func (sb *smartBlock) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail) (err error) {
func (sb *smartBlock) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail, showEvent bool) (err error) {
s := sb.NewStateCtx(ctx)
copy := pbtypes.CopyStruct(s.Details())
if copy == nil || copy.Fields == nil {
copy = &types.Struct{
detCopy := pbtypes.CopyStruct(s.Details())
if detCopy == nil || detCopy.Fields == nil {
detCopy = &types.Struct{
Fields: make(map[string]*types.Value),
}
}
relations := sb.Relations()
for _, detail := range details {
if detail.Value != nil {
copy.Fields[detail.Key] = detail.Value
rel := pbtypes.GetRelation(relations, detail.Key)
if rel == nil {
log.Errorf("SetDetails: missing relation for detail %s", detail.Key)
return fmt.Errorf("relation not found: you should add the missing relation first")
}
if rel.Scope != pbrelation.Relation_object {
s.SetExtraRelation(rel)
}
if rel.Format == pbrelation.RelationFormat_status || rel.Format == pbrelation.RelationFormat_tag {
newOptsIds := slice.Difference(pbtypes.GetStringListValue(detail.Value), pbtypes.GetStringListValue(detCopy.Fields[detail.Key]))
var missingOptsIds []string
for _, newOptId := range newOptsIds {
if !pbtypes.HasOption(rel.SelectDict, newOptId) {
missingOptsIds = append(missingOptsIds, newOptId)
}
}
if len(missingOptsIds) > 0 {
opts, err := sb.Anytype().ObjectStore().GetAggregatedOptions(rel.Key, rel.Format, s.ObjectType())
if err != nil {
return err
}
for _, missingOptsId := range missingOptsIds {
opt := pbtypes.GetOption(opts, missingOptsId)
if opt == nil {
log.Errorf("relation %s is missing option: %s", rel.Key, missingOptsId)
continue
}
optCopy := *opt
// reset scope
optCopy.Scope = pbrelation.RelationOption_local
_, err := s.AddExtraRelationOption(*rel, optCopy)
if err != nil {
return err
}
}
}
}
err = s.ValidateNewDetail(detail.Key, detail.Value)
if err != nil {
return fmt.Errorf("relation %s validation failed: %s", detail.Key, err.Error())
}
detCopy.Fields[detail.Key] = detail.Value
} else {
delete(copy.Fields, detail.Key)
delete(detCopy.Fields, detail.Key)
}
}
if copy.Equal(sb.Details()) {
if detCopy.Equal(sb.Details()) {
return
}
err = sb.validateDetailsAndAddOptions(s, copy)
if err != nil {
return err
}
s.SetDetails(copy)
s.SetDetails(detCopy)
if err = sb.Apply(s); err != nil {
return
}
// filter-out setDetails event
if !showEvent && ctx != nil {
var filtered []*pb.EventMessage
msgs := ctx.GetMessages()
var isFiltered bool
for i, msg := range msgs {
if sd := msg.GetBlockSetDetails(); sd == nil || sd.Id != sb.Id() {
filtered = append(filtered, msgs[i])
} else {
isFiltered = true
}
}
if isFiltered {
ctx.SetMessages(sb.Id(), filtered)
}
}
return nil
}
@ -653,7 +692,6 @@ func (sb *smartBlock) AddExtraRelations(ctx *state.Context, relations []*pbrelat
if err = sb.Apply(s, NoEvent); err != nil {
return
}
if ctx != nil {
// todo: send an atomic event for each changed relation
ctx.AddMessages(sb.Id(), []*pb.EventMessage{{
@ -1077,10 +1115,19 @@ func mergeAndSortRelations(objTypeRelations []*pbrelation.Relation, extraRelatio
return rels
}
func (sb *smartBlock) baseRelations() []*pbrelation.Relation {
rels := []*pbrelation.Relation{bundle.MustGetRelation(bundle.RelationKeyId), bundle.MustGetRelation(bundle.RelationKeyLayout), bundle.MustGetRelation(bundle.RelationKeyIconEmoji)}
for _, rel := range rels {
rel.Scope = pbrelation.Relation_object
}
return rels
}
func (sb *smartBlock) Relations() []*pbrelation.Relation {
if sb.Type() == pb.SmartBlockType_Archive || sb.source.Virtual() {
return nil
return sb.baseRelations()
}
objType := sb.ObjectType()
var err error
@ -1117,12 +1164,12 @@ func (sb *smartBlock) ObjectTypeRelations() []*pbrelation.Relation {
var relations []*pbrelation.Relation
if sb.meta != nil {
objectTypes := sb.meta.FetchObjectTypes(sb.ObjectTypes())
if !(len(objectTypes) == 1 && objectTypes[0].Url == bundle.TypeKeyObjectType.URL()) {
// do not fetch objectTypes for object type type to avoid universe collapse
for _, objType := range objectTypes {
relations = append(relations, objType.Relations...)
}
//if !(len(objectTypes) == 1 && objectTypes[0].Url == bundle.TypeKeyObjectType.URL()) {
// do not fetch objectTypes for object type type to avoid universe collapse
for _, objType := range objectTypes {
relations = append(relations, objType.Relations...)
}
//}
}
return relations
}

View file

@ -81,11 +81,11 @@ func TestSmartBlock_Apply(t *testing.T) {
t.Run("no flags", func(t *testing.T) {
fx := newFixture(t)
defer fx.tearDown()
fx.init([]*model.Block{{Id: "1"}})
s := fx.NewState()
s.Add(simple.New(&model.Block{Id: "2"}))
require.NoError(t, s.InsertTo("1", model.Block_Inner, "2"))
fx.metaService.EXPECT().FetchObjectTypes(gomock.Any())
fx.source.EXPECT().PushChange(gomock.Any())
var event *pb.Event
fx.SetEventFunc(func(e *pb.Event) {
@ -121,6 +121,8 @@ func newFixture(t *testing.T) *fixture {
metaService := mockMeta.NewMockService(ctrl)
metaService.EXPECT().PubSub().AnyTimes().Return(metaPubSub)
metaPubSub.EXPECT().NewSubscriber().AnyTimes().Return(metaSubscriber)
metaService.EXPECT().FetchObjectTypes(gomock.Any()).AnyTimes()
return &fixture{
SmartBlock: New(metaService),
t: t,

View file

@ -199,7 +199,7 @@ func (st *SmartTest) SendEvent(msgs []*pb.EventMessage) {
return
}
func (st *SmartTest) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail) (err error) {
func (st *SmartTest) SetDetails(ctx *state.Context, details []*pb.RpcBlockSetDetailsDetail, showEvent bool) (err error) {
if st.meta == nil {
st.meta = &core.SmartBlockMeta{
Relations: st.ExtraRelations(),

View file

@ -238,9 +238,13 @@ func (s *State) changeObjectTypeAdd(add *pb.ChangeObjectTypeAdd) error {
}
func (s *State) changeObjectTypeRemove(remove *pb.ChangeObjectTypeRemove) error {
for i, ot := range s.ObjectTypes() {
otypes := s.ObjectTypes()
otypesCopy := make([]string, len(otypes))
copy(otypesCopy, otypes)
for i, ot := range otypesCopy {
if ot == remove.Url {
s.objectTypes = append(s.objectTypes[:i], s.objectTypes[i+1:]...)
s.objectTypes = append(otypesCopy[:i], otypesCopy[i+1:]...)
return nil
}
}
@ -333,6 +337,8 @@ func (s *State) fillChanges(msgs []simple.EventMessage) {
}
}
case *pb.EventMessageValueOfBlockDataviewSourceSet:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockDataviewViewSet:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockDataviewViewDelete:

View file

@ -88,6 +88,15 @@ func (s *State) applyEvent(ev *pb.EventMessage) (err error) {
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockDataviewSourceSet:
if err = apply(o.BlockDataviewSourceSet.Id, func(b simple.Block) error {
if f, ok := b.(dataview.Block); ok {
return f.SetSource(o.BlockDataviewSourceSet.Source)
}
return fmt.Errorf("not a dataview block")
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockDataviewViewSet:
if err = apply(o.BlockDataviewViewSet.Id, func(b simple.Block) error {
if f, ok := b.(dataview.Block); ok && o.BlockDataviewViewSet.View != nil {

View file

@ -2,6 +2,8 @@ package state
import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"strings"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/dataview"
@ -50,7 +52,8 @@ func (s *State) normalize(withLayouts bool) (err error) {
}
}
s.normalizeRelations()
s.NormalizeRelations()
s.MigrateObjectTypes()
for _, b := range s.blocks {
if dv := b.Model().GetDataview(); dv != nil {
@ -338,12 +341,84 @@ func CleanupLayouts(s *State) (removedCount int) {
return
}
func (s *State) normalizeRelations() {
func (s *State) MigrateObjectTypes() {
migrate := func(old string) (new string, hasChanges bool) {
if strings.HasPrefix(old, addr.OldCustomObjectTypeURLPrefix) {
new = strings.TrimPrefix(old, addr.OldCustomObjectTypeURLPrefix)
hasChanges = true
} else if strings.HasPrefix(old, addr.OldBundledObjectTypeURLPrefix) {
new = addr.BundledObjectTypeURLPrefix + strings.TrimPrefix(old, addr.OldBundledObjectTypeURLPrefix)
hasChanges = true
} else {
new = old
}
return
}
newObjObjType, hasChanges1 := migrate(s.ObjectType())
if hasChanges1 {
s.SetObjectType(newObjObjType)
}
b := s.Get("dataview")
if b != nil {
dv := b.Model().GetDataview()
dvBlock, ok := b.(dataview.Block)
if dv != nil && ok {
newDvSource, hasChanges1 := migrate(dv.Source)
if hasChanges1 {
dvBlock.SetSource(newDvSource)
s.Set(dvBlock)
}
for _, rel := range dv.Relations {
if len(rel.ObjectTypes) == 0 {
continue
}
var newOts []string
var hasChanges2 bool
for _, ot := range rel.ObjectTypes {
newOt, hasChanges1 := migrate(ot)
hasChanges2 = hasChanges2 || hasChanges1
newOts = append(newOts, newOt)
}
if hasChanges2 {
relCopy := pbtypes.CopyRelation(rel)
relCopy.ObjectTypes = newOts
dvBlock.UpdateRelation(relCopy.Key, *relCopy)
}
}
}
}
for _, rel := range s.ExtraRelations() {
if len(rel.ObjectTypes) == 0 {
continue
}
var newOts []string
var hasChanges2 bool
for _, ot := range rel.ObjectTypes {
newOt, hasChanges1 := migrate(ot)
hasChanges2 = hasChanges2 || hasChanges1
newOts = append(newOts, newOt)
}
if hasChanges2 {
relCopy := pbtypes.CopyRelation(rel)
relCopy.ObjectTypes = newOts
s.SetExtraRelation(relCopy)
}
}
}
func (s *State) NormalizeRelations() {
for _, r := range s.ExtraRelations() {
var updateRelation *relation.Relation
equal, exists := bundle.EqualWithRelation(r.Key, r)
if exists && !equal {
// reset bundle relation in case the bundle has it updated
updateRelation = bundle.MustGetRelation(bundle.RelationKey(r.Key))
updateRelation.SelectDict = r.SelectDict
}
@ -357,7 +432,7 @@ func (s *State) normalizeRelations() {
if slice.FindPos(values, opt.Id) >= 0 {
optsFiltered = append(optsFiltered, r.SelectDict[i])
} else {
log.With("thread",s.rootId).Errorf("normalizeRelations: remove option %s", opt.Id)
log.With("thread",s.rootId).Errorf("NormalizeRelations: remove option %s", opt.Id)
hasChanges = true
}
}
@ -370,7 +445,7 @@ func (s *State) normalizeRelations() {
}
}*/
if r.Format == relation.RelationFormat_status && r.MaxCount != 1 {
if !pbtypes.RelationFormatCanHaveListValue(r.Format) && r.MaxCount != 1 {
if updateRelation == nil {
updateRelation = pbtypes.CopyRelation(r)
}
@ -398,7 +473,7 @@ func (s *State) normalizeDvRelations(b simple.Block) {
continue
}
if r.Format == relation.RelationFormat_status && r.MaxCount != 1 {
if !pbtypes.RelationFormatCanHaveListValue(r.Format) && r.MaxCount != 1 {
rc := pbtypes.CopyRelation(r)
rc.MaxCount = 1
@ -413,7 +488,7 @@ func (s *State) normalizeDvRelation(r *relation.Relation) {
*r = *bundle.MustGetRelation(bundle.RelationKey(r.Key))
}
if r.Format == relation.RelationFormat_status && r.MaxCount != 1 {
if !pbtypes.RelationFormatCanHaveListValue(r.Format) && r.MaxCount != 1 {
r.MaxCount = 1
}
}

View file

@ -400,7 +400,7 @@ func TestState_Normalize(t *testing.T) {
r.AddRelation(r1)
s := r.NewState()
s.normalizeRelations()
s.NormalizeRelations()
ApplyState(s, true)
assert.Equal(t, int32(1), pbtypes.GetRelation(s.ExtraRelations(), "a1").MaxCount)
})
@ -412,7 +412,7 @@ func TestState_Normalize(t *testing.T) {
r.AddRelation(r1)
s := r.NewState()
s.normalizeRelations()
s.NormalizeRelations()
ApplyState(s, true)
assert.Equal(t, "Done", pbtypes.GetRelation(s.ExtraRelations(), bundle.RelationKeyDone.String()).Name)
})

View file

@ -0,0 +1,132 @@
package state
import (
"fmt"
pbrelation "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/gogo/protobuf/types"
"net/url"
)
func validateRelationFormat(rel *pbrelation.Relation, v *types.Value) error {
if _, isNull := v.Kind.(*types.Value_NullValue); isNull {
// allow null value for any field
return nil
}
switch rel.Format {
case pbrelation.RelationFormat_longtext, pbrelation.RelationFormat_shorttext:
if _, ok := v.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect type: %T instead of string", v.Kind)
}
return nil
case pbrelation.RelationFormat_number:
if _, ok := v.Kind.(*types.Value_NumberValue); !ok {
return fmt.Errorf("incorrect type: %T instead of number", v.Kind)
}
return nil
case pbrelation.RelationFormat_status, pbrelation.RelationFormat_tag:
if _, ok := v.Kind.(*types.Value_ListValue); !ok {
return fmt.Errorf("incorrect type: %T instead of list", v.Kind)
}
if rel.MaxCount > 0 && len(v.GetListValue().Values) > int(rel.MaxCount) {
return fmt.Errorf("maxCount exceeded")
}
return validateOptions(rel.SelectDict, v.GetListValue().Values)
case pbrelation.RelationFormat_date:
if _, ok := v.Kind.(*types.Value_NumberValue); !ok {
return fmt.Errorf("incorrect type: %T instead of number", v.Kind)
}
return nil
case pbrelation.RelationFormat_file, pbrelation.RelationFormat_object:
switch s := v.Kind.(type) {
case *types.Value_StringValue:
if rel.MaxCount != 1 {
return fmt.Errorf("incorrect type: %T instead of list(maxCount!=1)", v.Kind)
}
return nil
case *types.Value_ListValue:
if rel.MaxCount > 0 && len(s.ListValue.Values) > int(rel.MaxCount) {
return fmt.Errorf("relation %s(%s) has maxCount exceeded", rel.Key, rel.Format.String())
}
for i, lv := range s.ListValue.Values {
if optId, ok := lv.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect list item value at index %d: %T instead of string", i, lv.Kind)
} else if optId.StringValue == "" {
return fmt.Errorf("empty option at index %d", i)
}
}
return nil
default:
return fmt.Errorf("incorrect type: %T instead of list/string", v.Kind)
}
case pbrelation.RelationFormat_checkbox:
if _, ok := v.Kind.(*types.Value_BoolValue); !ok {
return fmt.Errorf("incorrect type: %T instead of bool", v.Kind)
}
return nil
case pbrelation.RelationFormat_url:
if _, ok := v.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect type: %T instead of string", v.Kind)
}
_, err := url.Parse(v.GetStringValue())
if err != nil {
return fmt.Errorf("failed to parse URL: %s", err.Error())
}
// todo: should we allow schemas other than http/https?
//if !strings.EqualFold(u.Scheme, "http") && !strings.EqualFold(u.Scheme, "https") {
// return fmt.Errorf("url scheme %s not supported", u.Scheme)
//}
return nil
case pbrelation.RelationFormat_email:
if _, ok := v.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect type: %T instead of string", v.Kind)
}
// todo: revise regexp and reimplement
/*valid := uri.ValidateEmail(v.GetStringValue())
if !valid {
return fmt.Errorf("failed to validate email")
}*/
return nil
case pbrelation.RelationFormat_phone:
if _, ok := v.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect type: %T instead of string", v.Kind)
}
// todo: revise regexp and reimplement
/*valid := uri.ValidatePhone(v.GetStringValue())
if !valid {
return fmt.Errorf("failed to validate phone")
}*/
return nil
case pbrelation.RelationFormat_emoji:
if _, ok := v.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect type: %T instead of string", v.Kind)
}
// check if the symbol is emoji
return nil
default:
return fmt.Errorf("unsupported rel format: %s", rel.Format.String())
}
}
func validateOptions(opts []*pbrelation.RelationOption, vals []*types.Value) error {
for i, lv := range vals {
if optId, ok := lv.Kind.(*types.Value_StringValue); !ok {
return fmt.Errorf("incorrect list item value at index %d: %T", i, lv.Kind)
} else if optId.StringValue == "" {
return fmt.Errorf("empty option at index %d", i)
} else if opt := pbtypes.GetOption(opts, optId.StringValue); opt == nil {
return fmt.Errorf("option with id %s not found", optId.StringValue)
}
}
return nil
}

View file

@ -82,6 +82,7 @@ type State struct {
bufIterateParentIds []string
groupId string
noObjectType bool
}
func (s *State) RootId() string {
@ -104,11 +105,11 @@ func (s *State) RootId() string {
}
func (s *State) NewState() *State {
return &State{parent: s, blocks: make(map[string]simple.Block), rootId: s.rootId}
return &State{parent: s, blocks: make(map[string]simple.Block), rootId: s.rootId, noObjectType: s.noObjectType}
}
func (s *State) NewStateCtx(ctx *Context) *State {
return &State{parent: s, blocks: make(map[string]simple.Block), rootId: s.rootId, ctx: ctx}
return &State{parent: s, blocks: make(map[string]simple.Block), rootId: s.rootId, ctx: ctx, noObjectType: s.noObjectType}
}
func (s *State) Context() *Context {
@ -198,6 +199,19 @@ func (s *State) GetParentOf(id string) (res simple.Block) {
return
}
func (s *State) IsParentOf(parentId string, childId string) bool {
p := s.Pick(parentId)
if p == nil {
return false
}
if slice.FindPos(p.Model().ChildrenIds, childId) != -1 {
return true
}
return false
}
func (s *State) PickParentOf(id string) (res simple.Block) {
s.Iterate(func(b simple.Block) bool {
if slice.FindPos(b.Model().ChildrenIds, id) != -1 {
@ -626,6 +640,7 @@ func (s *State) SetExtraRelation(rel *pbrelation.Relation) {
s.extraRelations = pbtypes.CopyRelations(s.parent.ExtraRelations())
}
relCopy := pbtypes.CopyRelation(rel)
relCopy.Scope = pbrelation.Relation_object
var found bool
for i, exRel := range s.extraRelations {
if exRel.Key == rel.Key {
@ -636,8 +651,6 @@ func (s *State) SetExtraRelation(rel *pbrelation.Relation) {
if !found {
s.extraRelations = append(s.extraRelations, relCopy)
}
return
}
func (s *State) AddRelation(relation *pbrelation.Relation) *State {
@ -650,8 +663,8 @@ func (s *State) AddRelation(relation *pbrelation.Relation) *State {
relCopy := pbtypes.CopyRelation(relation)
// reset the scope to object
relCopy.Scope = pbrelation.Relation_object
if relation.Format == pbrelation.RelationFormat_status {
relation.MaxCount = 1
if !pbtypes.RelationFormatCanHaveListValue(relCopy.Format) && relCopy.MaxCount != 1 {
relCopy.MaxCount = 1
}
if relCopy.Format == pbrelation.RelationFormat_file && relCopy.ObjectTypes == nil {
@ -667,11 +680,11 @@ func (s *State) SetExtraRelations(relations []*pbrelation.Relation) *State {
for _, rel := range relationsCopy {
// reset scopes for all relations
rel.Scope = pbrelation.Relation_object
if rel.Format == pbrelation.RelationFormat_status {
if !pbtypes.RelationFormatCanHaveListValue(rel.Format) && rel.MaxCount != 1 {
rel.MaxCount = 1
}
}
s.extraRelations = relations
s.extraRelations = relationsCopy
return s
}
@ -703,15 +716,29 @@ func (s *State) AddExtraRelationOption(rel pbrelation.Relation, option pbrelatio
return &option, nil
}
func (s *State) SetObjectType(objectType string) *State {
return s.SetObjectTypes([]string{objectType})
}
func (s *State) SetObjectTypes(objectTypes []string) *State {
s.objectTypes = objectTypes
s.SetDetail(bundle.RelationKeyType.String(), pbtypes.String(s.ObjectType()))
if pbtypes.GetRelation(s.ExtraRelations(), bundle.RelationKeyType.String()) == nil {
s.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyType))
}
return s
}
func (s *State) InjectDerivedDetails() {
s.SetDetail(string(bundle.RelationKeyId), pbtypes.String(s.rootId))
s.SetDetail(string(bundle.RelationKeyType), pbtypes.String(s.ObjectType()))
if !pbtypes.HasRelation(s.ExtraRelations(), bundle.RelationKeyId.String()) {
s.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyId))
}
if !pbtypes.HasRelation(s.ExtraRelations(), bundle.RelationKeyType.String()) {
s.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyType))
}
}
// ObjectScopedDetails contains only persistent details that are going to be saved in changes/snapshots
@ -757,8 +784,8 @@ func (s *State) ObjectTypes() []string {
// this method is useful because we have decided that currently objects can have only one object type, while preserving the ability to unlock this later
func (s *State) ObjectType() string {
objTypes := s.ObjectTypes()
if len(objTypes) != 1 {
log.Errorf("obj %s has %d objectTypes instead of 1", s.RootId(), len(objTypes))
if len(objTypes) != 1 && !s.noObjectType {
log.Errorf("obj %s(%s) has %d objectTypes instead of 1", s.RootId(), pbtypes.GetString(s.Details(), bundle.RelationKeyName.String()), len(objTypes))
}
if len(objTypes) > 0 {
@ -857,12 +884,48 @@ func (s *State) DepSmartIds() (ids []string) {
return
}
func (s *State) ValidateNewDetail(key string, v *types.Value) (err error) {
rel := pbtypes.GetRelation(s.ExtraRelations(), key)
if rel == nil {
return fmt.Errorf("relation for detail not found")
}
if err := validateRelationFormat(rel, v); err != nil {
log.Errorf("relation %s(%s) failed to validate: %s", rel.Key, rel.Format, err.Error())
return fmt.Errorf("format validation failed: %s", err.Error())
}
return nil
}
func (s *State) ValidateRelations() (err error) {
var details = s.Details()
if details == nil {
return nil
}
for k, v := range details.Fields {
rel := pbtypes.GetRelation(s.ExtraRelations(), k)
if rel == nil {
return fmt.Errorf("relation for detail %s not found", k)
}
if err := validateRelationFormat(rel, v); err != nil {
return fmt.Errorf("relation %s(%s) failed to validate: %s", rel.Key, rel.Format.String(), err.Error())
}
}
return nil
}
func (s *State) Validate() (err error) {
var (
err2 error
childrenIds = make(map[string]string)
)
if err = s.ValidateRelations(); err != nil {
return fmt.Errorf("failed to validate relations: %s", err.Error())
}
if err = s.Iterate(func(b simple.Block) (isContinue bool) {
for _, cid := range b.Model().ChildrenIds {
if parentId, ok := childrenIds[cid]; ok {
@ -879,6 +942,7 @@ func (s *State) Validate() (err error) {
}); err != nil {
return
}
return err2
}
@ -920,6 +984,7 @@ func (s *State) Copy() *State {
details: pbtypes.CopyStruct(s.details),
extraRelations: pbtypes.CopyRelations(s.extraRelations),
objectTypes: objTypes,
noObjectType: s.noObjectType,
}
return copy
}
@ -941,6 +1006,11 @@ func (s *State) Len() (l int) {
return
}
func (s *State) SetNoObjectType(noObjectType bool) *State {
s.noObjectType = noObjectType
return s
}
type linkSource interface {
FillSmartIds(ids []string) []string
HasSmartIds() bool

View file

@ -8,12 +8,12 @@ import (
"github.com/anytypeio/go-anytype-middleware/core/block/simple/link"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/text"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/slice"
"github.com/gogo/protobuf/types"
"github.com/google/martian/log"
)
const (
@ -22,6 +22,8 @@ const (
DataviewBlockId = "dataview"
)
var log = logging.Logger("anytype-state-template")
type StateTransformer func(s *state.State)
var WithEmpty = StateTransformer(func(s *state.State) {
@ -46,6 +48,44 @@ var WithObjectTypes = func(otypes []string) StateTransformer {
}
}
var WithObjectTypeLayoutMigration = func() StateTransformer {
return func(s *state.State) {
layout := pbtypes.GetFloat64(s.Details(), bundle.RelationKeyLayout.String())
if layout == float64(relation.ObjectType_objectType) {
return
}
s.SetDetail(bundle.RelationKeyRecommendedLayout.String(), pbtypes.Float64(layout))
s.SetDetail(bundle.RelationKeyLayout.String(), pbtypes.Float64(float64(relation.ObjectType_objectType)))
}
}
var WithObjectTypeRecommendedRelationsMigration = func(relations []*relation.Relation) StateTransformer {
return func(s *state.State) {
var keys []string
if len(pbtypes.GetStringList(s.Details(), bundle.RelationKeyRecommendedRelations.String())) > 0 {
return
}
for _, rel := range relations {
keys = append(keys, rel.Key)
var found bool
for _, exRel := range s.ExtraRelations() {
if exRel.Key == rel.Key {
found = true
break
}
}
if !found {
s.AddRelation(rel)
}
}
s.SetDetail(bundle.RelationKeyRecommendedRelations.String(), pbtypes.StringList(keys))
}
}
var WithObjectTypesAndLayout = func(otypes []string) StateTransformer {
return func(s *state.State) {
if len(s.ObjectTypes()) == 0 {
@ -60,48 +100,47 @@ var WithObjectTypesAndLayout = func(otypes []string) StateTransformer {
continue
}
s.SetDetail(bundle.RelationKeyLayout.String(), pbtypes.Float64(float64(t.Layout)))
s.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyLayout))
}
}
}
}
var WithLayout = func(layout relation.ObjectTypeLayout) StateTransformer {
return func(s *state.State) {
d := s.Details()
if d == nil || d.Fields == nil || d.Fields[bundle.RelationKeyLayout.String()] == nil {
s.SetDetail(bundle.RelationKeyLayout.String(), pbtypes.Float64(float64(layout)))
}
}
return WithDetail(bundle.RelationKeyLayout, pbtypes.Float64(float64(layout)))
}
var WithDetailName = func(name string) StateTransformer {
return func(s *state.State) {
if s.Details() != nil && s.Details().Fields != nil && s.Details().Fields[bundle.RelationKeyName.String()] != nil {
return
}
s.SetDetail(bundle.RelationKeyName.String(), pbtypes.String(name))
}
return WithDetail(bundle.RelationKeyName, pbtypes.String(name))
}
var WithDetail = func(key bundle.RelationKey, value *types.Value) StateTransformer {
return func(s *state.State) {
if s.Details() != nil && s.Details().Fields != nil && s.Details().Fields[key.String()] != nil {
return
if s.Details() == nil || s.Details().Fields == nil || s.Details().Fields[key.String()] == nil {
s.SetDetail(key.String(), value)
}
s.SetDetail(key.String(), value)
if rel := pbtypes.GetRelation(s.ExtraRelations(), key.String()); rel == nil {
s.SetExtraRelation(bundle.MustGetRelation(key))
}
}
}
var WithForcedDetail = func(key bundle.RelationKey, value *types.Value) StateTransformer {
return func(s *state.State) {
if s.Details() == nil || s.Details().Fields == nil || s.Details().Fields[key.String()] == nil || !s.Details().Fields[key.String()].Equal(value) {
s.SetDetail(key.String(), value)
}
if rel := pbtypes.GetRelation(s.ExtraRelations(), key.String()); rel == nil {
s.SetExtraRelation(bundle.MustGetRelation(key))
}
}
}
var WithDetailIconEmoji = func(iconEmoji string) StateTransformer {
return func(s *state.State) {
if s.Details() != nil && s.Details().Fields != nil && s.Details().Fields[bundle.RelationKeyIconEmoji.String()] != nil {
return
}
return WithDetail(bundle.RelationKeyIconEmoji, pbtypes.String(iconEmoji))
s.SetDetail(bundle.RelationKeyIconEmoji.String(), pbtypes.String(iconEmoji))
}
}
var WithHeader = StateTransformer(func(s *state.State) {
@ -195,32 +234,43 @@ var WithRootBlocks = func(blocks []*model.Block) StateTransformer {
}
}
var WithDataview = func(dataview model.BlockContentOfDataview) StateTransformer {
var WithDataview = func(dataview model.BlockContentOfDataview, forceViews bool) StateTransformer {
return func(s *state.State) {
// remove old dataview
var blockNeedToUpdate bool
s.Iterate(func(b simple.Block) (isContinue bool) {
if dvBlock, ok := b.(simpleDataview.Block); !ok {
return true
} else {
if dvBlock.Model().GetDataview().Source == "pages" || len(dvBlock.Model().GetDataview().Relations) == 0 {
// remove old pages set
s.Unlink(b.Model().Id)
if len(dvBlock.Model().GetDataview().Relations) == 0 ||
dvBlock.Model().GetDataview().Source != dataview.Dataview.Source ||
len(dvBlock.Model().GetDataview().Views) == 0 ||
forceViews && len(dvBlock.Model().GetDataview().Relations) != len(dataview.Dataview.Relations) ||
forceViews && !pbtypes.DataviewViewsEqualSorted(dvBlock.Model().GetDataview().Views, dataview.Dataview.Views) {
log.With("thread", s.RootId()).With("name", pbtypes.GetString(s.Details(), "name")).Warnf("dataview needs to be migrated: %v, %v, %v, %v",
len(dvBlock.Model().GetDataview().Relations) == 0,
dvBlock.Model().GetDataview().Source != dataview.Dataview.Source,
len(dvBlock.Model().GetDataview().Views) == 0,
forceViews && len(dvBlock.Model().GetDataview().Views[0].Filters) != len(dataview.Dataview.Views[0].Filters) ||
forceViews && len(dvBlock.Model().GetDataview().Relations) != len(dataview.Dataview.Relations))
blockNeedToUpdate = true
return false
}
}
return true
})
// todo: move to the begin of func
if s.Exists(DataviewBlockId) {
return
if blockNeedToUpdate || !s.Exists(DataviewBlockId) {
s.Set(simple.New(&model.Block{Content: &dataview, Id: DataviewBlockId}))
if !s.IsParentOf(s.RootId(), DataviewBlockId) {
err := s.InsertTo(s.RootId(), model.Block_Inner, DataviewBlockId)
if err != nil {
log.Errorf("template WithDataview failed to insert: %w", err)
}
}
}
s.Add(simple.New(&model.Block{Content: &dataview, Id: DataviewBlockId}))
err := s.InsertTo(s.RootId(), model.Block_Inner, DataviewBlockId)
if err != nil {
log.Errorf("template WithDataview failed to insert: %w", err)
}
}
}

View file

@ -1,6 +1,7 @@
package meta
import (
"github.com/anytypeio/go-anytype-middleware/util/slice"
"strings"
"sync"
"time"
@ -112,10 +113,10 @@ func (s *service) FetchObjectTypes(objectTypeUrls []string) []*pbrelation.Object
continue
}
objectTypes = append(objectTypes, objectType)
} else if !strings.HasPrefix(otypeUrl, objects.CustomObjectTypeURLPrefix) {
} else if !strings.HasPrefix(otypeUrl, "b") {
log.Errorf("failed to get objectType %s: incorrect url", otypeUrl)
} else {
customOtypeIds = append(customOtypeIds, strings.TrimPrefix(otypeUrl, objects.CustomObjectTypeURLPrefix))
customOtypeIds = append(customOtypeIds, otypeUrl)
}
}
@ -129,7 +130,7 @@ func (s *service) FetchObjectTypes(objectTypeUrls []string) []*pbrelation.Object
if name := pbtypes.GetString(meta.Details, bundle.RelationKeyName.String()); name != "" {
objectType.Name = name
}
if layout := pbtypes.GetFloat64(meta.Details, bundle.RelationKeyLayout.String()); layout != 0.0 {
if layout := pbtypes.GetFloat64(meta.Details, bundle.RelationKeyRecommendedLayout.String()); layout != 0.0 {
objectType.Layout = pbrelation.ObjectTypeLayout(int(layout))
}
@ -137,8 +138,30 @@ func (s *service) FetchObjectTypes(objectTypeUrls []string) []*pbrelation.Object
objectType.IconEmoji = iconEmoji
}
objectType.Url = objects.CustomObjectTypeURLPrefix + meta.BlockId
objectType.Relations = meta.Relations
recommendedRelationsKeys := pbtypes.GetStringList(meta.Details, bundle.RelationKeyRecommendedRelations.String())
for _, rel := range bundle.RequiredInternalRelations {
if slice.FindPos(recommendedRelationsKeys, rel.String()) == -1 {
recommendedRelationsKeys = append(recommendedRelationsKeys, rel.String())
}
}
var recommendedRelations []*pbrelation.Relation
for _, rk := range recommendedRelationsKeys {
rel := pbtypes.GetRelation(meta.Relations, rk)
if rel == nil {
rel, _ = bundle.GetRelation(bundle.RelationKey(rk))
if rel == nil {
continue
}
}
relCopy := pbtypes.CopyRelation(rel)
relCopy.Scope = pbrelation.Relation_type
recommendedRelations = append(recommendedRelations, relCopy)
}
objectType.Url = meta.BlockId
objectType.Relations = recommendedRelations
objectTypes = append(objectTypes, objectType)
}

View file

@ -43,7 +43,7 @@ func (s *service) NewQueue(info pb.ModelProcess, workers int) Queue {
info.Id = bson.NewObjectId().Hex()
}
q := &queue{
id: info.Id ,
id: info.Id,
info: info,
state: pb.ModelProcess_None,
msgs: mb.New(0),

View file

@ -4,10 +4,11 @@ import (
"context"
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore"
"github.com/anytypeio/go-anytype-middleware/app"
"github.com/anytypeio/go-anytype-middleware/core/event"
"github.com/anytypeio/go-anytype-middleware/core/indexer"
@ -16,7 +17,6 @@ import (
pbrelation "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/globalsign/mgo/bson"
"github.com/anytypeio/go-anytype-middleware/core/block/database/objects"
"github.com/anytypeio/go-anytype-middleware/core/block/editor"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/basic"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/bookmark"
@ -104,8 +104,6 @@ type Service interface {
SetDetails(ctx *state.Context, req pb.RpcBlockSetDetailsRequest) (err error)
ModifyDetails(objectId string, modifier func(current *types.Struct) (*types.Struct, error)) (err error)
GetObjectType(url string) (objectType *pbrelation.ObjectType, err error)
GetRelations(objectId string) (relations []*pbrelation.Relation, err error)
UpdateExtraRelations(ctx *state.Context, id string, relations []*pbrelation.Relation, createIfMissing bool) (err error)
ModifyExtraRelations(ctx *state.Context, objectId string, modifier func(current []*pbrelation.Relation) ([]*pbrelation.Relation, error)) (err error)
@ -153,7 +151,6 @@ type Service interface {
DeletePages(req pb.RpcBlockListDeletePageRequest) error
GetAggregatedRelations(req pb.RpcBlockDataviewRelationListAvailableRequest) (relations []*pbrelation.Relation, err error)
GetDataviewObjectType(ctx *state.Context, contextId string, blockId string) (string, error)
DeleteDataviewView(ctx *state.Context, req pb.RpcBlockDataviewViewDeleteRequest) error
UpdateDataviewView(ctx *state.Context, req pb.RpcBlockDataviewViewUpdateRequest) error
SetDataviewActiveView(ctx *state.Context, req pb.RpcBlockDataviewViewSetActiveRequest) error
@ -250,6 +247,14 @@ func (s *service) Run() (err error) {
s.Do(s.anytype.PredefinedBlocks().SetPages, func(b smartblock.SmartBlock) error {
return nil
})
s.Do(s.anytype.PredefinedBlocks().MarketplaceType, func(b smartblock.SmartBlock) error {
return nil
})
s.Do(s.anytype.PredefinedBlocks().MarketplaceRelation, func(b smartblock.SmartBlock) error {
return nil
})
go s.cleanupTicker()
return
}
@ -422,7 +427,7 @@ func (s *service) MarkArchived(id string, archived bool) (err error) {
Key: "isArchived",
Value: pbtypes.Bool(archived),
},
})
}, true)
})
}
@ -468,6 +473,7 @@ func (s *service) CreateSmartBlock(sbType coresb.SmartBlockType, details *types.
log.Debugf("created new smartBlock: %v, objectType: %v", id, sb.ObjectTypes())
// todo: move into createSmartblock params to make a single tx
if relations != nil {
var setDetails []*pb.RpcBlockSetDetailsDetail
for k, v := range details.Fields {
@ -476,9 +482,12 @@ func (s *service) CreateSmartBlock(sbType coresb.SmartBlockType, details *types.
Value: v,
})
}
sb.Lock()
if _, err = sb.AddExtraRelations(nil, relations); err != nil {
sb.Unlock()
return id, nil, fmt.Errorf("can't add relations to object: %v", err)
}
sb.Unlock()
}
if details != nil && details.Fields != nil {
@ -489,9 +498,13 @@ func (s *service) CreateSmartBlock(sbType coresb.SmartBlockType, details *types.
Value: v,
})
}
if err = sb.SetDetails(nil, setDetails); err != nil {
sb.Lock()
if err = sb.SetDetails(nil, setDetails, true); err != nil {
sb.Unlock()
return id, nil, fmt.Errorf("can't set details to object: %v", err)
}
sb.Unlock()
}
return id, sb.Details(), nil
@ -696,7 +709,7 @@ func (s *service) SetFields(ctx *state.Context, req pb.RpcBlockSetFieldsRequest)
func (s *service) SetDetails(ctx *state.Context, req pb.RpcBlockSetDetailsRequest) (err error) {
return s.Do(req.ContextId, func(b smartblock.SmartBlock) error {
return b.SetDetails(ctx, req.Details)
return b.SetDetails(ctx, req.Details, false)
})
}
@ -715,15 +728,6 @@ func (s *service) GetAggregatedRelations(req pb.RpcBlockDataviewRelationListAvai
return
}
func (s *service) GetDataviewObjectType(ctx *state.Context, contextId string, blockId string) (objectType string, err error) {
err = s.DoDataview(contextId, func(b dataview.Dataview) error {
objectType, err = b.GetObjectTypeURL(ctx, blockId)
return err
})
return
}
func (s *service) UpdateDataviewView(ctx *state.Context, req pb.RpcBlockDataviewViewUpdateRequest) error {
return s.DoDataview(req.ContextId, func(b dataview.Dataview) error {
return b.UpdateView(ctx, req.BlockId, req.ViewId, *req.View, true)
@ -1188,9 +1192,13 @@ func (s *service) createSmartBlock(id string, initEmpty bool, initWithObjectType
case pb.SmartBlockType_ProfilePage:
sb = editor.NewProfile(s.meta, s, s, s.linkPreview, s.sendEvent)
case pb.SmartBlockType_ObjectType:
sb = editor.NewObjectType(s.meta)
sb = editor.NewObjectType(s.meta, s)
case pb.SmartBlockType_File:
sb = editor.NewFiles(s.meta)
case pb.SmartBlockType_MarketplaceType:
sb = editor.NewMarketplaceType(s.meta, s)
case pb.SmartBlockType_MarketplaceRelation:
sb = editor.NewMarketplaceRelation(s.meta, s)
default:
return nil, fmt.Errorf("unexpected smartblock type: %v", sc.Type())
}
@ -1347,49 +1355,6 @@ func (s *service) Do(id string, apply func(b smartblock.SmartBlock) error) error
return apply(sb)
}
func (s *service) GetObjectType(url string) (objectType *pbrelation.ObjectType, err error) {
objectType = &pbrelation.ObjectType{}
if strings.HasPrefix(url, objects.BundledObjectTypeURLPrefix) {
var err error
objectType, err = bundle.GetTypeByUrl(url)
if err != nil {
if err == bundle.ErrNotFound {
return nil, ErrUnknownObjectType
}
return nil, err
}
return objectType, nil
} else if !strings.HasPrefix(url, objects.CustomObjectTypeURLPrefix) {
return nil, fmt.Errorf("incorrect object type URL format")
}
sbid := strings.TrimPrefix(url, objects.CustomObjectTypeURLPrefix)
sb, err := s.anytype.GetBlock(sbid)
if err != nil {
return nil, err
}
err = s.Do(sb.ID(), func(b smartblock.SmartBlock) error {
details := b.Details()
objectType.Relations = b.ExtraRelations()
objectType.Url = url
if details != nil && details.Fields != nil {
if v, ok := details.Fields[bundle.RelationKeyName.String()]; ok {
objectType.Name = v.GetStringValue()
}
if v, ok := details.Fields[bundle.RelationKeyLayout.String()]; ok {
objectType.Layout = pbrelation.ObjectTypeLayout(int(v.GetNumberValue()))
}
if v, ok := details.Fields[bundle.RelationKeyIconEmoji.String()]; ok {
objectType.IconEmoji = v.GetStringValue()
}
}
return nil
})
return objectType, err
}
func (s *service) GetRelations(objectId string) (relations []*pbrelation.Relation, err error) {
err = s.Do(objectId, func(b smartblock.SmartBlock) error {
relations = b.Relations()
@ -1486,7 +1451,7 @@ func (s *service) SetObjectTypes(ctx *state.Context, objectId string, objectType
}
func (s *service) CreateSet(ctx *state.Context, req pb.RpcBlockCreateSetRequest) (linkId string, setId string, err error) {
objType, err := s.GetObjectType(req.ObjectTypeUrl)
objType, err := localstore.GetObjectType(s.anytype.ObjectStore(), req.ObjectTypeUrl)
if err != nil {
return "", "", err
}

View file

@ -163,6 +163,14 @@ func (d *Dataview) Diff(b simple.Block) (msgs []simple.EventMessage, err error)
}
}
if dv.content.Source != d.content.Source {
msgs = append(msgs,
simple.EventMessage{Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfBlockDataviewSourceSet{
&pb.EventBlockDataviewSourceSet{
Id: dv.Id,
Source: dv.content.Source,
}}}})
}
return
}
@ -339,7 +347,7 @@ func (td *Dataview) ModelToSave() *model.Block {
}
func (d *Dataview) SetSource(source string) error {
if !strings.HasPrefix(source, objects.BundledObjectTypeURLPrefix) && !strings.HasPrefix(source, objects.CustomObjectTypeURLPrefix) {
if !strings.HasPrefix(source, objects.BundledObjectTypeURLPrefix) && !strings.HasPrefix(source, "b") {
return fmt.Errorf("invalid source URL")
}
@ -452,7 +460,7 @@ func (d *Dataview) UpdateRelationOption(relationKey string, option pbrelation.Re
relFound := pbtypes.GetRelation(d.content.Relations, relationKey)
for _, rel := range d.content.Relations {
if rel.Key != relationKey{
if rel.Key != relationKey {
if relFound.Format != rel.Format {
continue
}

View file

@ -0,0 +1,83 @@
package source
import (
"context"
"github.com/anytypeio/go-anytype-middleware/change"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/gogo/protobuf/types"
)
func NewAnytypeProfile(a core.Service, id string) (s Source) {
return &anytypeProfile{
id: id,
a: a,
}
}
type anytypeProfile struct {
id string
a core.Service
}
func (v *anytypeProfile) Id() string {
return v.id
}
func (v *anytypeProfile) Anytype() core.Service {
return v.a
}
func (v *anytypeProfile) Type() pb.SmartBlockType {
return pb.SmartBlockType_ProfilePage
}
func (v *anytypeProfile) Virtual() bool {
return false
}
func (v *anytypeProfile) getDetails() (p *types.Struct) {
return &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("Anytype"),
bundle.RelationKeyDescription.String(): pbtypes.String("Authored by Anytype team"),
bundle.RelationKeyIconImage.String(): pbtypes.String("bafybeigpvcr42wobigjm63hmx3223gktsz4hpwp3jmiwkpcawo7wtpimv4"),
bundle.RelationKeyId.String(): pbtypes.String(v.id),
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(relation.ObjectType_profile)),
}}
}
func (v *anytypeProfile) ReadDoc(receiver ChangeReceiver, empty bool) (doc state.Doc, err error) {
s := state.NewDoc(v.id, nil).(*state.State)
d := v.getDetails()
s.SetDetails(d)
s.SetObjectType(bundle.TypeKeyProfile.URL())
return s, nil
}
func (v *anytypeProfile) ReadMeta(_ ChangeReceiver) (doc state.Doc, err error) {
s := &state.State{}
d := v.getDetails()
s.SetDetails(d)
s.SetObjectType(bundle.TypeKeyProfile.URL())
return s, nil
}
func (v *anytypeProfile) PushChange(params PushChangeParams) (id string, err error) {
return "", nil
}
func (v *anytypeProfile) FindFirstChange(ctx context.Context) (c *change.Change, err error) {
return nil, change.ErrEmpty
}
func (v *anytypeProfile) Close() (err error) {
return
}

View file

@ -0,0 +1,111 @@
package source
import (
"context"
"github.com/anytypeio/go-anytype-middleware/change"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
pbrelation "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/gogo/protobuf/types"
)
func NewBundledObjectType(a core.Service, id string) (s Source) {
return &bundledObjectType{
id: id,
a: a,
}
}
type bundledObjectType struct {
id string
a core.Service
}
func (v *bundledObjectType) Id() string {
return v.id
}
func (v *bundledObjectType) Anytype() core.Service {
return v.a
}
func (v *bundledObjectType) Type() pb.SmartBlockType {
return pb.SmartBlockType_ObjectType
}
func (v *bundledObjectType) Virtual() bool {
return false
}
func getDetailsForBundledObjectType(id string) (extraRels []*pbrelation.Relation, p *types.Struct, err error) {
ot, err := bundle.GetTypeByUrl(id)
if err != nil {
return nil, nil, err
}
var relationKeys []string
for i := range ot.Relations {
extraRels = append(extraRels, ot.Relations[i])
relationKeys = append(relationKeys, addr.BundledRelationURLPrefix+ot.Relations[i].Key)
}
det := &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyType.String(): pbtypes.StringList([]string{bundle.TypeKeyObjectType.String()}),
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(pbrelation.ObjectType_objectType)),
bundle.RelationKeyName.String(): pbtypes.String(ot.Name),
bundle.RelationKeyCreator.String(): pbtypes.String(addr.AnytypeProfileId),
bundle.RelationKeyIconEmoji.String(): pbtypes.String(ot.IconEmoji),
bundle.RelationKeyRecommendedRelations.String(): pbtypes.StringList(relationKeys),
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Float64(float64(ot.Layout)),
bundle.RelationKeyDescription.String(): pbtypes.String(ot.Description),
bundle.RelationKeyId.String(): pbtypes.String(id),
bundle.RelationKeyIsHidden.String(): pbtypes.Bool(ot.Hidden),
}}
return extraRels, det, nil
}
func (v *bundledObjectType) ReadDoc(receiver ChangeReceiver, empty bool) (doc state.Doc, err error) {
s := state.NewDoc(v.id, nil).(*state.State)
rels, d, err := getDetailsForBundledObjectType(v.id)
if err != nil {
return nil, err
}
s.SetExtraRelations(rels)
s.SetDetails(d)
s.SetObjectType(bundle.TypeKeyObjectType.URL())
return s, nil
}
func (v *bundledObjectType) ReadMeta(_ ChangeReceiver) (doc state.Doc, err error) {
s := &state.State{}
rels, d, err := getDetailsForBundledObjectType(v.id)
if err != nil {
return nil, err
}
s.SetExtraRelations(rels)
s.SetDetails(d)
s.SetObjectType(bundle.TypeKeyObjectType.URL())
return s, nil
}
func (v *bundledObjectType) PushChange(params PushChangeParams) (id string, err error) {
return "", nil
}
func (v *bundledObjectType) FindFirstChange(ctx context.Context) (c *change.Change, err error) {
return nil, change.ErrEmpty
}
func (v *bundledObjectType) Close() (err error) {
return
}

View file

@ -0,0 +1,99 @@
package source
import (
"context"
"fmt"
"strings"
"github.com/anytypeio/go-anytype-middleware/change"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/gogo/protobuf/types"
)
func NewBundledRelation(a core.Service, id string) (s Source) {
return &bundledRelation{
id: id,
a: a,
}
}
type bundledRelation struct {
id string
a core.Service
}
func (v *bundledRelation) Id() string {
return v.id
}
func (v *bundledRelation) Anytype() core.Service {
return v.a
}
func (v *bundledRelation) Type() pb.SmartBlockType {
return pb.SmartBlockType_File
}
func (v *bundledRelation) Virtual() bool {
return false
}
func (v *bundledRelation) getDetails(id string) (rels []*relation.Relation, p *types.Struct, err error) {
if !strings.HasPrefix(id, addr.BundledRelationURLPrefix) {
return nil, nil, fmt.Errorf("incorrect relation id: not a bundled relation id")
}
rel, err := bundle.GetRelation(bundle.RelationKey(strings.TrimPrefix(id, addr.BundledRelationURLPrefix)))
if err != nil {
return nil, nil, err
}
rel.Creator = addr.AnytypeProfileId
rels, d := bundle.GetDetailsForRelation(true, rel)
return rels, d, nil
}
func (v *bundledRelation) ReadDoc(receiver ChangeReceiver, empty bool) (doc state.Doc, err error) {
s := state.NewDoc(v.id, nil).(*state.State)
rels, d, err := v.getDetails(v.id)
if err != nil {
return nil, err
}
s.SetDetails(d)
s.SetExtraRelations(rels)
s.SetObjectType(bundle.TypeKeyRelation.URL())
return s, nil
}
func (v *bundledRelation) ReadMeta(_ ChangeReceiver) (doc state.Doc, err error) {
s := &state.State{}
rels, d, err := v.getDetails(v.id)
if err != nil {
return nil, err
}
s.SetDetails(d)
s.SetExtraRelations(rels)
s.SetObjectType(bundle.TypeKeyRelation.URL())
return s, nil
}
func (v *bundledRelation) PushChange(params PushChangeParams) (id string, err error) {
return "", nil
}
func (v *bundledRelation) FindFirstChange(ctx context.Context) (c *change.Change, err error) {
return nil, change.ErrEmpty
}
func (v *bundledRelation) Close() (err error) {
return
}

View file

@ -0,0 +1,101 @@
package source
import (
"context"
"fmt"
"strings"
"github.com/anytypeio/go-anytype-middleware/change"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
"github.com/gogo/protobuf/types"
)
func NewIndexedRelation(a core.Service, id string) (s Source) {
return &indexedRelation{
id: id,
a: a,
}
}
type indexedRelation struct {
id string
a core.Service
}
func (v *indexedRelation) Id() string {
return v.id
}
func (v *indexedRelation) Anytype() core.Service {
return v.a
}
func (v *indexedRelation) Type() pb.SmartBlockType {
return pb.SmartBlockType_Page
}
func (v *indexedRelation) Virtual() bool {
return false
}
func (v *indexedRelation) getDetails(id string) (rels []*relation.Relation, p *types.Struct, err error) {
if !strings.HasPrefix(id, addr.BundledRelationURLPrefix) {
return nil, nil, fmt.Errorf("incorrect relation id: not an indexed relation id")
}
key := strings.TrimPrefix(id, addr.CustomRelationURLPrefix)
rel, err := v.Anytype().ObjectStore().GetRelation(key)
if err != nil {
return nil, nil, err
}
// todo: store source objectType and extract real profileId
rel.Creator = v.a.ProfileID()
rels, d := bundle.GetDetailsForRelation(false, rel)
return rels, d, nil
}
func (v *indexedRelation) ReadDoc(receiver ChangeReceiver, empty bool) (doc state.Doc, err error) {
s := state.NewDoc(v.id, nil).(*state.State)
rels, d, err := v.getDetails(v.id)
if err != nil {
return nil, err
}
s.SetDetails(d)
s.SetExtraRelations(rels)
s.SetObjectType(bundle.TypeKeyRelation.URL())
return s, nil
}
func (v *indexedRelation) ReadMeta(_ ChangeReceiver) (doc state.Doc, err error) {
s := &state.State{}
rels, d, err := v.getDetails(v.id)
if err != nil {
return nil, err
}
s.SetDetails(d)
s.SetExtraRelations(rels)
s.SetObjectType(bundle.TypeKeyRelation.URL())
return s, nil
}
func (v *indexedRelation) PushChange(params PushChangeParams) (id string, err error) {
return "", nil
}
func (v *indexedRelation) FindFirstChange(ctx context.Context) (c *change.Change, err error) {
return nil, change.ErrEmpty
}
func (v *indexedRelation) Close() (err error) {
return
}

View file

@ -3,6 +3,7 @@ package source
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"math/rand"
"sync"
"time"
@ -36,6 +37,7 @@ type Source interface {
Anytype() core.Service
Type() pb.SmartBlockType
Virtual() bool
//ReadOnly() bool
ReadDoc(receiver ChangeReceiver, empty bool) (doc state.Doc, err error)
ReadMeta(receiver ChangeReceiver) (doc state.Doc, err error)
PushChange(params PushChangeParams) (id string, err error)
@ -47,9 +49,27 @@ var ErrUnknownDataFormat = fmt.Errorf("unknown data format: you may need to upgr
func NewSource(a core.Service, ss status.Service, id string) (s Source, err error) {
st, err := smartblock.SmartBlockTypeFromID(id)
if id == addr.AnytypeProfileId {
return NewAnytypeProfile(a, id), nil
}
if st == smartblock.SmartBlockTypeFile {
return NewFiles(a, id), nil
}
if st == smartblock.SmartBlockTypeBundledObjectType {
return NewBundledObjectType(a, id), nil
}
if st == smartblock.SmartBlockTypeBundledRelation {
return NewBundledRelation(a, id), nil
}
if st == smartblock.SmartBlockTypeIndexedRelation {
return NewIndexedRelation(a, id), nil
}
return newSource(a, ss, id)
}
@ -172,8 +192,10 @@ func (s *source) buildState() (doc state.Doc, err error) {
return
}
if verr := st.Validate(); verr != nil {
log.With("thread", s.id).Errorf("not valid state: %v", verr)
if s.sb.Type() != smartblock.SmartBlockTypeArchive && !s.Virtual() {
if verr := st.Validate(); verr != nil {
log.With("thread", s.id).With("sbType", s.sb.Type()).Errorf("not valid state: %v", verr)
}
}
if err = st.Normalize(false); err != nil {
return
@ -210,6 +232,15 @@ func InjectCreationInfo(s Source, st *state.State) (err error) {
return fmt.Errorf("anytype is nil")
}
defer func() {
if !pbtypes.HasRelation(st.ExtraRelations(), bundle.RelationKeyCreator.String()) {
st.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyCreator))
}
if !pbtypes.HasRelation(st.ExtraRelations(), bundle.RelationKeyCreatedDate.String()) {
st.SetExtraRelation(bundle.MustGetRelation(bundle.RelationKeyCreatedDate))
}
}()
if pbtypes.HasField(st.Details(), bundle.RelationKeyCreator.String()) {
return nil
}

View file

@ -2,22 +2,21 @@ package core
import (
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
)
func (mw *Middleware) ConfigGet(*pb.RpcConfigGetRequest) *pb.RpcConfigGetResponse {
mw.m.RLock()
defer mw.m.RUnlock()
if mw.app == nil {
at := mw.getAnytype()
if at == nil {
return &pb.RpcConfigGetResponse{Error: &pb.RpcConfigGetResponseError{pb.RpcConfigGetResponseError_NODE_NOT_STARTED, "account not started"}}
}
at := mw.app.MustComponent(core.CName).(core.Service)
pBlocks := at.PredefinedBlocks()
return &pb.RpcConfigGetResponse{
Error: &pb.RpcConfigGetResponseError{pb.RpcConfigGetResponseError_NULL, ""},
HomeBlockId: at.PredefinedBlocks().Home,
ArchiveBlockId: at.PredefinedBlocks().Archive,
ProfileBlockId: at.PredefinedBlocks().Profile,
GatewayUrl: mw.gatewayAddr,
Error: &pb.RpcConfigGetResponseError{pb.RpcConfigGetResponseError_NULL, ""},
HomeBlockId: pBlocks.Home,
ArchiveBlockId: pBlocks.Archive,
ProfileBlockId: pBlocks.Profile,
MarketplaceTypeId: pBlocks.MarketplaceType,
MarketplaceRelationId: pBlocks.MarketplaceRelation,
GatewayUrl: mw.gatewayAddr,
}
}

View file

@ -9,6 +9,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/core/block"
"github.com/anytypeio/go-anytype-middleware/core/event"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
)
@ -81,6 +82,15 @@ func (mw *Middleware) stop() error {
return nil
}
func (mw *Middleware) getAnytype() core.Service {
mw.m.RLock()
defer mw.m.RUnlock()
if mw.app != nil {
return mw.app.MustComponent(core.CName).(core.Service)
}
return nil
}
func init() {
logging.SetVersion(GitSummary)
}

View file

@ -40,9 +40,10 @@ func (mw *Middleware) DebugThread(req *pb.RpcDebugThreadRequest) *pb.RpcDebugThr
at := mw.app.MustComponent(core.CName).(core.Service)
cafePeerStr, _ := at.CafePeer().ValueForProtocol(ma.P_P2P)
t := at.(*core.Anytype).ThreadService().Threads()
cafePeer, _ := peer.Decode(cafePeerId)
cafePeer, _ := peer.Decode(cafePeerStr)
tid, err := thread.Decode(req.ThreadId)
if err != nil {
return response(nil, pb.RpcDebugThreadResponseError_BAD_INPUT, err)
@ -72,7 +73,8 @@ func (mw *Middleware) DebugSync(req *pb.RpcDebugSyncRequest) *pb.RpcDebugSyncRes
var threads []*pb.RpcDebugthreadInfo
t := at.(*core.Anytype).ThreadService().Threads()
ids, _ := t.Logstore().Threads()
cafePeer, _ := peer.Decode(cafePeerId)
cafePeerStr, _ := at.CafePeer().ValueForProtocol(ma.P_P2P)
cafePeer, _ := peer.Decode(cafePeerStr)
var (
threadsWithoutRepl int32
@ -128,7 +130,7 @@ func getThreadInfo(t net.NetBoostrapper, id thread.ID, ownDeviceId string, cafeP
if lg.ID.String() == ownDeviceId {
for _, ad := range lg.Addrs {
adHost, _ := ad.ValueForProtocol(ma.P_P2P)
if adHost == cafePeerId {
if adHost == cafePeer.String() {
tinfo.OwnLogHasCafeReplicator = true
}
}

View file

@ -8,9 +8,11 @@ import (
"github.com/anytypeio/go-anytype-middleware/app"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pb/service"
"github.com/google/martian/log"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
)
var log = logging.Logger("anytype-grpc")
func NewGrpcSender() *GrpcSender {
return &GrpcSender{}
}

View file

@ -3,6 +3,7 @@ package indexer
import (
"context"
"fmt"
"strings"
"sync"
"time"
@ -19,6 +20,9 @@ import (
)
func newDoc(id string, a core.Service) (d *doc, err error) {
if strings.HasPrefix(id, "_ot") {
return nil, fmt.Errorf("not indexable")
}
sb, err := a.GetBlock(id)
if err != nil {
err = fmt.Errorf("anytype.GetBlock error: %v", err)
@ -175,6 +179,8 @@ func (d *doc) buildState() (doc *state.State, err error) {
d.injectLocalRelations(st)
st.InjectDerivedDetails()
st.NormalizeRelations()
st.MigrateObjectTypes()
err = d.injectCreationInfo(st)
if err != nil {

View file

@ -1,14 +1,19 @@
package indexer
import (
"context"
"fmt"
"sync"
"time"
"github.com/anytypeio/go-anytype-middleware/app"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/source"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/threads"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/ftsearch"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
@ -51,12 +56,13 @@ type GetSearchInfo interface {
}
type indexer struct {
store localstore.ObjectStore
anytype core.Service
searchInfo GetSearchInfo
cache map[string]*doc
quitWG *sync.WaitGroup
quit chan struct{}
store localstore.ObjectStore
anytype core.Service
searchInfo GetSearchInfo
cache map[string]*doc
quitWG *sync.WaitGroup
quit chan struct{}
subscriptionCancel context.CancelFunc
threadIdsBuf []string
recBuf []core.SmartblockRecordEnvelope
@ -81,16 +87,56 @@ func (i *indexer) Run() (err error) {
if ftErr := i.ftInit(); ftErr != nil {
log.Errorf("can't init ft: %v", ftErr)
}
ch, err := i.anytype.SubscribeForNewRecords()
ctx, cancel := context.WithCancel(context.Background())
ch, err := i.anytype.SubscribeForNewRecords(ctx)
if err != nil {
return
cancel()
return err
}
i.subscriptionCancel = cancel
i.quitWG.Add(2)
go i.detailsLoop(ch)
go i.ftLoop()
return
}
func (i *indexer) openDoc(id string) (state.Doc, error) {
s, err := source.NewSource(i.anytype, nil, id)
if err != nil {
err = fmt.Errorf("anytype.GetBlock error: %v", err)
return nil, err
}
return s.ReadDoc(nil, false)
}
func (i *indexer) reindexBundled() {
var (
d state.Doc
err error
)
var ids []string
for _, rk := range bundle.ListRelationsKeys() {
ids = append(ids, addr.BundledRelationURLPrefix+rk.String())
}
for _, tk := range bundle.ListTypesKeys() {
ids = append(ids, tk.URL())
}
ids = append(ids, addr.AnytypeProfileId)
for _, id := range ids {
if d, err = i.openDoc(id); err != nil {
log.Errorf("reindexBundled failed to open %s: %s", id, err.Error())
return
}
if err := i.store.CreateObject(id, d.Details(), &pbrelation.Relations{d.ExtraRelations()}, nil, pbtypes.GetString(d.Details(), bundle.RelationKeyDescription.String())); err != nil {
log.With("thread", id).Errorf("can't update object store: %v", err)
}
}
}
func (i *indexer) detailsLoop(ch chan core.SmartblockRecordWithThreadID) {
batch := mb.New(0)
defer batch.Close()
@ -118,6 +164,7 @@ func (i *indexer) detailsLoop(ch chan core.SmartblockRecordWithThreadID) {
for {
select {
case rec, ok := <-ch:
//log.Debugf("indexer got change on %s(%s): %s", rec.ThreadID, rec.LogID, rec.ID)
if !ok {
return
}
@ -126,6 +173,7 @@ func (i *indexer) detailsLoop(ch chan core.SmartblockRecordWithThreadID) {
i.cleanup()
case <-quit:
batch.Close()
return
}
}
}
@ -183,6 +231,7 @@ func (i *indexer) index(id string, records []core.SmartblockRecordEnvelope, only
dataviewSourceBefore = b.Model().GetDataview().Source
}
}
d.mu.Unlock()
lastChangeTS, lastChangeBy, _ := d.addRecords(records...)
@ -316,6 +365,9 @@ func (i *indexer) cleanup() {
func (i *indexer) Close() error {
i.mu.Lock()
quit := i.quit
if i.subscriptionCancel != nil {
i.subscriptionCancel()
}
i.mu.Unlock()
if quit != nil {

View file

@ -92,9 +92,12 @@ func newFixture(t *testing.T) *fixture {
fx.anytype = testMock.NewMockService(fx.ctrl)
fx.objectStore = testMock.NewMockObjectStore(fx.ctrl)
fx.objectStore.EXPECT().FTSearch().Return(nil).AnyTimes()
fx.objectStore.EXPECT().CreateObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
fx.anytype.EXPECT().ObjectStore().Return(fx.objectStore).AnyTimes()
fx.ch = make(chan core.SmartblockRecordWithThreadID)
fx.anytype.EXPECT().SubscribeForNewRecords().Return(fx.ch, nil)
fx.anytype.EXPECT().SubscribeForNewRecords(gomock.Any()).Return(fx.ch, nil)
fx.Indexer = indexer.New()
a.Register(fx.getSerach).
Register(fx.anytype).

View file

@ -4,8 +4,11 @@ import (
"fmt"
"strings"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/globalsign/mgo/bson"
"github.com/anytypeio/go-anytype-middleware/core/block"
"github.com/anytypeio/go-anytype-middleware/core/block/database/objects"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
@ -16,11 +19,6 @@ import (
"github.com/gogo/protobuf/types"
)
const (
customObjectTypeURLPrefix = "https://anytype.io/schemas/object/custom/"
bundledObjectTypeURLPrefix = "https://anytype.io/schemas/object/bundled/"
)
func (mw *Middleware) ObjectTypeRelationList(req *pb.RpcObjectTypeRelationListRequest) *pb.RpcObjectTypeRelationListResponse {
response := func(code pb.RpcObjectTypeRelationListResponseErrorCode, relations []*pbrelation.Relation, err error) *pb.RpcObjectTypeRelationListResponse {
m := &pb.RpcObjectTypeRelationListResponse{Relations: relations, Error: &pb.RpcObjectTypeRelationListResponseError{Code: code}}
@ -29,8 +27,12 @@ func (mw *Middleware) ObjectTypeRelationList(req *pb.RpcObjectTypeRelationListRe
}
return m
}
at := mw.getAnytype()
if at == nil {
return response(pb.RpcObjectTypeRelationListResponseError_BAD_INPUT, nil, fmt.Errorf("account must be started"))
}
objType, err := mw.getObjectType(req.ObjectTypeUrl)
objType, err := mw.getObjectType(at, req.ObjectTypeUrl)
if err != nil {
if err == block.ErrUnknownObjectType {
return response(pb.RpcObjectTypeRelationListResponseError_UNKNOWN_OBJECT_TYPE_URL, nil, err)
@ -51,7 +53,12 @@ func (mw *Middleware) ObjectTypeRelationAdd(req *pb.RpcObjectTypeRelationAddRequ
return m
}
objType, err := mw.getObjectType(req.ObjectTypeUrl)
at := mw.getAnytype()
if at == nil {
return response(pb.RpcObjectTypeRelationAddResponseError_BAD_INPUT, nil, fmt.Errorf("account must be started"))
}
objType, err := mw.getObjectType(at, req.ObjectTypeUrl)
if err != nil {
if err == block.ErrUnknownObjectType {
return response(pb.RpcObjectTypeRelationAddResponseError_UNKNOWN_OBJECT_TYPE_URL, nil, err)
@ -65,10 +72,9 @@ func (mw *Middleware) ObjectTypeRelationAdd(req *pb.RpcObjectTypeRelationAddRequ
}
var relations []*pbrelation.Relation
id := strings.TrimPrefix(objType.Url, objects.CustomObjectTypeURLPrefix)
err = mw.doBlockService(func(bs block.Service) (err error) {
relations, err = bs.AddExtraRelations(nil, id, req.Relations)
relations, err = bs.AddExtraRelations(nil, objType.Url, req.Relations)
if err != nil {
return err
}
@ -90,7 +96,13 @@ func (mw *Middleware) ObjectTypeRelationUpdate(req *pb.RpcObjectTypeRelationUpda
}
return m
}
objType, err := mw.getObjectType(req.ObjectTypeUrl)
at := mw.getAnytype()
if at == nil {
return response(pb.RpcObjectTypeRelationUpdateResponseError_BAD_INPUT, fmt.Errorf("account must be started"))
}
objType, err := mw.getObjectType(at, req.ObjectTypeUrl)
if err != nil {
if err == block.ErrUnknownObjectType {
return response(pb.RpcObjectTypeRelationUpdateResponseError_UNKNOWN_OBJECT_TYPE_URL, err)
@ -103,9 +115,8 @@ func (mw *Middleware) ObjectTypeRelationUpdate(req *pb.RpcObjectTypeRelationUpda
return response(pb.RpcObjectTypeRelationUpdateResponseError_READONLY_OBJECT_TYPE, fmt.Errorf("can't modify bundled object type"))
}
id := strings.TrimPrefix(objType.Url, objects.CustomObjectTypeURLPrefix)
err = mw.doBlockService(func(bs block.Service) (err error) {
err = bs.UpdateExtraRelations(nil, id, []*pbrelation.Relation{req.Relation}, false)
err = bs.UpdateExtraRelations(nil, objType.Url, []*pbrelation.Relation{req.Relation}, false)
if err != nil {
return err
}
@ -127,7 +138,13 @@ func (mw *Middleware) ObjectTypeRelationRemove(req *pb.RpcObjectTypeRelationRemo
}
return m
}
objType, err := mw.getObjectType(req.ObjectTypeUrl)
at := mw.getAnytype()
if at == nil {
return response(pb.RpcObjectTypeRelationRemoveResponseError_BAD_INPUT, fmt.Errorf("account must be started"))
}
objType, err := mw.getObjectType(at, req.ObjectTypeUrl)
if err != nil {
if err == block.ErrUnknownObjectType {
return response(pb.RpcObjectTypeRelationRemoveResponseError_UNKNOWN_OBJECT_TYPE_URL, err)
@ -139,10 +156,9 @@ func (mw *Middleware) ObjectTypeRelationRemove(req *pb.RpcObjectTypeRelationRemo
if strings.HasPrefix(objType.Url, bundle.TypePrefix) {
return response(pb.RpcObjectTypeRelationRemoveResponseError_READONLY_OBJECT_TYPE, fmt.Errorf("can't modify bundled object type"))
}
id := strings.TrimPrefix(objType.Url, objects.CustomObjectTypeURLPrefix)
err = mw.doBlockService(func(bs block.Service) (err error) {
err = bs.RemoveExtraRelations(nil, id, []string{req.RelationKey})
err = bs.RemoveExtraRelations(nil, objType.Url, []string{req.RelationKey})
if err != nil {
return err
}
@ -165,35 +181,42 @@ func (mw *Middleware) ObjectTypeCreate(req *pb.RpcObjectTypeCreateRequest) *pb.R
return m
}
var sbId string
var requiredRelationByKey = make(map[string]*pbrelation.Relation, len(bundle.RequiredInternalRelations))
var recommendedRelationKeys []string
var relations = make([]*pbrelation.Relation, 0, len(req.ObjectType.Relations)+len(bundle.RequiredInternalRelations))
for _, rel := range bundle.RequiredInternalRelations {
requiredRelationByKey[rel.String()] = bundle.MustGetRelation(rel)
relations = append(relations, bundle.MustGetRelation(rel))
recommendedRelationKeys = append(recommendedRelationKeys, addr.BundledRelationURLPrefix+rel.String())
}
for _, rel := range req.ObjectType.Relations {
if rel.Key == "" {
continue
}
if v, exists := requiredRelationByKey[rel.Key]; exists {
for i, rel := range req.ObjectType.Relations {
if v := pbtypes.GetRelation(relations, rel.Key); v != nil {
if !pbtypes.RelationEqual(v, rel) {
return response(pb.RpcObjectTypeCreateResponseError_BAD_INPUT, nil, fmt.Errorf("required relation %s not equals the bundled one", rel.Key))
}
delete(requiredRelationByKey, rel.Key)
}
}
} else {
if rel.Key == "" {
rel.Key = bson.NewObjectId().Hex()
}
for _, rel := range requiredRelationByKey {
req.ObjectType.Relations = append(req.ObjectType.Relations, rel)
if bundle.HasRelation(rel.Key) {
recommendedRelationKeys = append(recommendedRelationKeys, addr.BundledRelationURLPrefix+rel.Key)
} else {
recommendedRelationKeys = append(recommendedRelationKeys, addr.CustomRelationURLPrefix+rel.Key)
}
relations = append(relations, req.ObjectType.Relations[i])
}
}
err := mw.doBlockService(func(bs block.Service) (err error) {
sbId, _, err = bs.CreateSmartBlock(smartblock.SmartBlockTypeObjectType, &types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(req.ObjectType.Name),
bundle.RelationKeyIconEmoji.String(): pbtypes.String(req.ObjectType.IconEmoji),
bundle.RelationKeyType.String(): pbtypes.StringList([]string{bundle.TypeKeyObjectType.URL()}),
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(req.ObjectType.Layout)),
bundle.RelationKeyName.String(): pbtypes.String(req.ObjectType.Name),
bundle.RelationKeyIconEmoji.String(): pbtypes.String(req.ObjectType.IconEmoji),
bundle.RelationKeyType.String(): pbtypes.StringList([]string{bundle.TypeKeyObjectType.URL()}),
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(pbrelation.ObjectType_objectType)),
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Float64(float64(req.ObjectType.Layout)),
bundle.RelationKeyRecommendedRelations.String(): pbtypes.StringList(recommendedRelationKeys),
},
}, req.ObjectType.Relations)
if err != nil {
@ -202,14 +225,13 @@ func (mw *Middleware) ObjectTypeCreate(req *pb.RpcObjectTypeCreateRequest) *pb.R
return nil
})
if err != nil {
return response(pb.RpcObjectTypeCreateResponseError_UNKNOWN_ERROR, nil, err)
}
otype := req.ObjectType
otype.Relations = req.ObjectType.Relations
otype.Url = customObjectTypeURLPrefix + sbId
otype.Relations = relations
otype.Url = sbId
return response(pb.RpcObjectTypeCreateResponseError_NULL, otype, nil)
}
@ -227,29 +249,23 @@ func (mw *Middleware) ObjectTypeList(_ *pb.RpcObjectTypeListRequest) *pb.RpcObje
return response(pb.RpcObjectTypeListResponseError_UNKNOWN_ERROR, nil, err)
}
mw.m.RLock()
defer mw.m.RUnlock()
if mw.app == nil {
at := mw.getAnytype()
if at == nil {
return response(pb.RpcObjectTypeListResponseError_BAD_INPUT, nil, fmt.Errorf("account must be started"))
}
at := mw.app.MustComponent(core.CName).(core.Service)
threadIds, err := at.ThreadService().ListThreadIdsByType(smartblock.SmartBlockTypeObjectType)
if err != nil {
return response(pb.RpcObjectTypeListResponseError_UNKNOWN_ERROR, nil, err)
}
for _, id := range threadIds {
err = mw.doBlockService(func(bs block.Service) (err error) {
otype, err := bs.GetObjectType(objects.CustomObjectTypeURLPrefix + id.String())
if err != nil {
return err
}
otypes = append(otypes, otype)
return nil
})
otype, err := mw.getObjectType(at, id.String())
if err != nil {
log.Errorf("failed to get objectType info: %s", err.Error())
continue
}
otypes = append(otypes, otype)
}
return response(pb.RpcObjectTypeListResponseError_NULL, otypes, nil)
@ -283,15 +299,6 @@ func (mw *Middleware) SetCreate(req *pb.RpcSetCreateRequest) *pb.RpcSetCreateRes
return response(pb.RpcSetCreateResponseError_NULL, id, nil)
}
func (mw *Middleware) getObjectType(url string) (*pbrelation.ObjectType, error) {
var objType = &pbrelation.ObjectType{}
err := mw.doBlockService(func(bs block.Service) (err error) {
objType, err = bs.GetObjectType(url)
if err != nil {
return err
}
return nil
})
return objType, err
func (mw *Middleware) getObjectType(at core.Service, url string) (*pbrelation.ObjectType, error) {
return localstore.GetObjectType(at.ObjectStore(), url)
}

View file

@ -209,9 +209,20 @@ func TestRelationAdd(t *testing.T) {
}
}
mw.BlockSetDetails(&pb.RpcBlockSetDetailsRequest{ContextId: respPageCreate.PageId, Details: []*pb.RpcBlockSetDetailsDetail{
{Key: bundle.RelationKeyStatus.String(), Value: pbtypes.String("Done")},
optionAddResp := mw.ObjectRelationOptionAdd(&pb.RpcObjectRelationOptionAddRequest{
ContextId: respPageCreate.PageId,
RelationKey: bundle.RelationKeyStatus.String(),
Option: &pbrelation.RelationOption{
Text: "Done",
Color: "red",
},
})
require.Equal(t, 0, int(optionAddResp.Error.Code), optionAddResp.Error.Description)
setDetailsResp := mw.BlockSetDetails(&pb.RpcBlockSetDetailsRequest{ContextId: respPageCreate.PageId, Details: []*pb.RpcBlockSetDetailsDetail{
{Key: bundle.RelationKeyStatus.String(), Value: pbtypes.StringList([]string{optionAddResp.Option.Id})},
}})
require.Equal(t, 0, int(setDetailsResp.Error.Code), setDetailsResp.Error.Description)
respOpenNewPage = mw.BlockOpen(&pb.RpcBlockOpenRequest{BlockId: respPageCreate.PageId})
require.Equal(t, 0, int(respOpenNewPage.Error.Code), respOpenNewPage.Error.Description)
@ -341,10 +352,21 @@ func TestRelationAdd(t *testing.T) {
},
})
require.Equal(t, 0, int(respRelCreate.Error.Code), respRelCreate.Error.Description)
rel := respRelCreate.Event.GetMessages()[0].GetBlockDataviewRelationSet()
require.Equal(t, respRelCreate.RelationKey, rel.RelationKey)
var foundRel *pbrelation.Relation
for _, msg := range respRelCreate.Event.GetMessages() {
if rel := msg.GetBlockDataviewRelationSet(); rel != nil && rel.Relation.Name == "relation2" {
foundRel = rel.Relation
break
}
}
require.NotNil(t, foundRel)
require.Equal(t, respRelCreate.RelationKey, foundRel.Key)
// option is trimmed
require.Len(t, rel.Relation.SelectDict, 0)
for _, opt := range foundRel.SelectDict {
require.NotEqual(t, pbrelation.RelationOption_local, opt.Scope)
require.NotEqual(t, "relation2", opt.Text)
}
respRecordCreate := mw.BlockDataviewRecordCreate(
&pb.RpcBlockDataviewRecordCreateRequest{
@ -375,7 +397,7 @@ func TestRelationAdd(t *testing.T) {
RecordId: newPageId,
Record: &types2.Struct{
Fields: map[string]*types2.Value{
rel.Relation.Key: pbtypes.StringList([]string{respRelOptCreate.Option.Id}),
foundRel.Key: pbtypes.StringList([]string{respRelOptCreate.Option.Id}),
},
},
})
@ -386,13 +408,13 @@ func TestRelationAdd(t *testing.T) {
require.Equal(t, 0, int(respOpenNewPage.Error.Code), respOpenNewPage.Error.Description)
require.Len(t, respOpenNewPage.Event.Messages, 1)
relOnPage := getRelationByKey(getEventBlockShow(respOpenNewPage.Event.Messages).Relations, rel.RelationKey)
require.Equal(t, rel.Relation.Key, relOnPage.Key)
relOnPage := getRelationByKey(getEventBlockShow(respOpenNewPage.Event.Messages).Relations, foundRel.Key)
require.Equal(t, foundRel.Key, relOnPage.Key)
respOptAdd := mw.BlockDataviewRecordRelationOptionAdd(&pb.RpcBlockDataviewRecordRelationOptionAddRequest{
ContextId: mw.Anytype.PredefinedBlocks().SetPages,
BlockId: "dataview",
RelationKey: rel.RelationKey,
RelationKey: foundRel.Key,
RecordId: newPageId,
Option: &pbrelation.RelationOption{
Text: "opt2",
@ -410,7 +432,7 @@ func TestRelationAdd(t *testing.T) {
RecordId: newPageId,
Record: &types2.Struct{
Fields: map[string]*types2.Value{
rel.Relation.Key: pbtypes.String(respOptAdd.Option.Id),
foundRel.Key: pbtypes.StringList([]string{respOptAdd.Option.Id}),
},
},
})
@ -419,10 +441,9 @@ func TestRelationAdd(t *testing.T) {
require.Equal(t, 0, int(respOpenNewPage.Error.Code), respOpenNewPage.Error.Description)
require.Len(t, respOpenNewPage.Event.Messages, 1)
rel.Relation.SelectDict = append(rel.Relation.SelectDict, respOptAdd.Option)
relOnPage = getRelationByKey(getEventBlockShow(respOpenNewPage.Event.Messages).Relations, rel.RelationKey)
require.Equal(t, rel.Relation.Key, relOnPage.Key)
require.Len(t, relOnPage.SelectDict, 2)
relOnPage = getRelationByKey(getEventBlockShow(respOpenNewPage.Event.Messages).Relations, foundRel.Key)
require.Equal(t, foundRel.Key, relOnPage.Key)
require.Len(t, relOnPage.SelectDict, 3)
})
t.Run("aggregated_options", func(t *testing.T) {
@ -707,18 +728,19 @@ func TestCustomType(t *testing.T) {
respObjectTypeCreate := mw.ObjectTypeCreate(&pb.RpcObjectTypeCreateRequest{
ObjectType: &pbrelation.ObjectType{
Name: "1",
Name: "1",
Layout: pbrelation.ObjectType_todo,
Relations: []*pbrelation.Relation{
{Format: pbrelation.RelationFormat_date, Name: "date of birth"},
{Format: pbrelation.RelationFormat_object, Name: "assignee", ObjectTypes: []string{"https://anytype.io/schemas/object/bundled/page"}},
{Format: pbrelation.RelationFormat_longtext, Name: "bio"},
{Format: pbrelation.RelationFormat_date, Name: "date of birth", MaxCount: 1},
{Format: pbrelation.RelationFormat_object, Name: "assignee", ObjectTypes: []string{"_otpage"}},
{Format: pbrelation.RelationFormat_longtext, Name: "bio", MaxCount: 1},
},
},
})
require.Equal(t, 0, int(respObjectTypeCreate.Error.Code), respObjectTypeCreate.Error.Description)
require.Len(t, respObjectTypeCreate.ObjectType.Relations, len(bundle.RequiredInternalRelations)+3) // including relation.RequiredInternalRelations
require.True(t, strings.HasPrefix(respObjectTypeCreate.ObjectType.Url, "https://anytype.io/schemas/object/custom/"))
require.True(t, strings.HasPrefix(respObjectTypeCreate.ObjectType.Url, "b"))
var newRelation *pbrelation.Relation
for _, rel := range respObjectTypeCreate.ObjectType.Relations {
if rel.Name == "bio" {
@ -727,6 +749,7 @@ func TestCustomType(t *testing.T) {
}
}
time.Sleep(time.Second)
respObjectTypeList = mw.ObjectTypeList(nil)
require.Equal(t, 0, int(respObjectTypeList.Error.Code), respObjectTypeList.Error.Description)
lastObjType := respObjectTypeList.ObjectTypes[len(respObjectTypeList.ObjectTypes)-1]
@ -744,7 +767,6 @@ func TestCustomType(t *testing.T) {
respCreateRecordInCustomTypeSet := mw.BlockDataviewRecordCreate(&pb.RpcBlockDataviewRecordCreateRequest{ContextId: respCreateCustomTypeSet.Id, BlockId: "dataview", Record: &types2.Struct{Fields: map[string]*types2.Value{"name": pbtypes.String("custom1"), newRelation.Key: pbtypes.String("newRelationVal")}}})
require.Equal(t, 0, int(respCreateRecordInCustomTypeSet.Error.Code), respCreateRecordInCustomTypeSet.Error.Description)
customObjectId := respCreateRecordInCustomTypeSet.Record.Fields["id"].GetStringValue()
respOpenCustomTypeObject := mw.BlockOpen(&pb.RpcBlockOpenRequest{BlockId: customObjectId})
require.Equal(t, 0, int(respOpenCustomTypeObject.Error.Code), respOpenCustomTypeObject.Error.Description)
@ -755,8 +777,8 @@ func TestCustomType(t *testing.T) {
profile := getDetailsForContext(show.Details, mw.Anytype.PredefinedBlocks().Profile)
require.NotNil(t, profile)
// should have custom obj type + profile, because it has the relation `creator`
require.Len(t, show.ObjectTypes, 2)
require.Len(t, show.ObjectTypePerObject, 2)
require.Len(t, show.ObjectTypes, 3)
//require.Len(t, show.ObjectTypePerObject, 2)
var found bool
for _, ot := range show.ObjectTypes {
if ot.Url == respObjectTypeCreate.ObjectType.Url {

View file

@ -16,6 +16,7 @@ import (
"github.com/dgtony/collections/queue"
ct "github.com/dgtony/collections/time"
"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
"github.com/textileio/go-threads/core/net"
"github.com/textileio/go-threads/core/thread"
)
@ -91,11 +92,31 @@ func (s *service) Init(a *app.App) (err error) {
s.profile = anytype
s.emitter = a.MustComponent(event.CName).(event.Sender).Send
cafePid, _ := peer.Decode(cafePeerId)
s.cafeID = cafePid.String()
return
}
func (s *service) Run() error {
anytype := s.profile.(core.Service)
s.ownDeviceID = anytype.Device()
s.tInfo = anytype.SyncStatus()
s.fInfo = anytype.FileStatus()
var cafePeer string
if anytype.CafePeer() != nil {
cafePeer, _ = anytype.CafePeer().ValueForProtocol(ma.P_P2P)
}
if cafePeer == "" {
cafePeer = cafePeerId
}
cafePid, _ := peer.Decode(cafePeer)
s.cafeID = cafePid.String()
if err := s.startConnectivityTracking(); err != nil {
return err
}
go s.startSendingThreadStatus()
return nil
}
func (s *service) Name() string {
return CName
}
@ -202,19 +223,6 @@ func (s *service) UpdateTimeline(tid thread.ID, timeline []LogTime) {
}
}
func (s *service) Run() error {
anytype := s.profile.(core.Service)
s.ownDeviceID = anytype.Device()
s.tInfo = anytype.SyncStatus()
s.fInfo = anytype.FileStatus()
if err := s.startConnectivityTracking(); err != nil {
return err
}
go s.startSendingThreadStatus()
return nil
}
func (s *service) Close() error {
s.tsTrigger.Stop()

3
dist/.gitignore vendored
View file

@ -2,4 +2,5 @@ server
lib.a
lib.h
ios/Lib.framework
android/mobile.aar
android/lib.aar
cli

View file

@ -667,6 +667,7 @@
- [Event.Block.Dataview.RecordsUpdate](#anytype.Event.Block.Dataview.RecordsUpdate)
- [Event.Block.Dataview.RelationDelete](#anytype.Event.Block.Dataview.RelationDelete)
- [Event.Block.Dataview.RelationSet](#anytype.Event.Block.Dataview.RelationSet)
- [Event.Block.Dataview.SourceSet](#anytype.Event.Block.Dataview.SourceSet)
- [Event.Block.Dataview.ViewDelete](#anytype.Event.Block.Dataview.ViewDelete)
- [Event.Block.Dataview.ViewSet](#anytype.Event.Block.Dataview.ViewSet)
- [Event.Block.Delete](#anytype.Event.Block.Delete)
@ -6101,6 +6102,8 @@ commands acceptable only for text blocks, others will be ignored
| homeBlockId | [string](#string) | | home dashboard block id |
| archiveBlockId | [string](#string) | | archive block id |
| profileBlockId | [string](#string) | | profile block id |
| marketplaceTypeId | [string](#string) | | marketplace type id |
| marketplaceRelationId | [string](#string) | | marketplace relation id |
| gatewayUrl | [string](#string) | | gateway url for fetching static files |
@ -10449,6 +10452,22 @@ sent when the dataview relation has been changed or added
<a name="anytype.Event.Block.Dataview.SourceSet"></a>
### Event.Block.Dataview.SourceSet
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | dataview block&#39;s id |
| source | [string](#string) | | |
<a name="anytype.Event.Block.Dataview.ViewDelete"></a>
### Event.Block.Dataview.ViewDelete
@ -11745,6 +11764,7 @@ Dashboard opened, click on a page, Rpc.Block.open, Block.ShowFullscreen(PageBloc
| blockDataviewRecordsUpdate | [Event.Block.Dataview.RecordsUpdate](#anytype.Event.Block.Dataview.RecordsUpdate) | | |
| blockDataviewRecordsInsert | [Event.Block.Dataview.RecordsInsert](#anytype.Event.Block.Dataview.RecordsInsert) | | |
| blockDataviewRecordsDelete | [Event.Block.Dataview.RecordsDelete](#anytype.Event.Block.Dataview.RecordsDelete) | | |
| blockDataviewSourceSet | [Event.Block.Dataview.SourceSet](#anytype.Event.Block.Dataview.SourceSet) | | |
| blockDataviewViewSet | [Event.Block.Dataview.ViewSet](#anytype.Event.Block.Dataview.ViewSet) | | |
| blockDataviewViewDelete | [Event.Block.Dataview.ViewDelete](#anytype.Event.Block.Dataview.ViewDelete) | | |
| blockDataviewRelationDelete | [Event.Block.Dataview.RelationDelete](#anytype.Event.Block.Dataview.RelationDelete) | | |
@ -12168,6 +12188,8 @@ Precondition: user A and user B opened the same block
| Set | 5 | only have dataview simpleblock |
| ObjectType | 6 | have relations list |
| File | 7 | |
| MarketplaceType | 8 | |
| MarketplaceRelation | 9 | |
@ -13090,6 +13112,7 @@ default dictionary with unique values to choose for select/multiSelect format |
| maxCount | [int32](#int32) | | max number of values can be set for this relation. 0 means no limit. 1 means the value can be stored in non-repeated field |
| description | [string](#string) | | |
| scope | [Relation.Scope](#anytype.relation.Relation.Scope) | | on-store should be only local |
| creator | [string](#string) | | on-store should be only local |
@ -13156,13 +13179,14 @@ default dictionary with unique values to choose for select/multiSelect format |
| ---- | ------ | ----------- |
| basic | 0 | |
| profile | 1 | |
| action | 2 | |
| todo | 2 | |
| set | 3 | |
| objectType | 4 | |
| relation | 5 | |
| file | 6 | |
| dashboard | 7 | |
| database | 8 | to be released later |
| image | 8 | |
| database | 20 | to be released later |

28
go.mod
View file

@ -1,6 +1,6 @@
module github.com/anytypeio/go-anytype-middleware
go 1.13
go 1.14
require (
github.com/JohannesKaufmann/html-to-markdown v0.0.0-00010101000000-000000000000
@ -10,19 +10,19 @@ require (
github.com/blevesearch/bleve v1.0.14
github.com/cheggaaa/mb v1.0.2
github.com/dave/jennifer v1.4.1
github.com/dgtony/collections v0.1.5
github.com/dgtony/collections v0.1.6
github.com/disintegration/imaging v1.6.2
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/gobwas/glob v0.2.3
github.com/goccy/go-graphviz v0.0.9
github.com/gogo/protobuf v1.3.1
github.com/gogo/protobuf v1.3.2
github.com/gogo/status v1.1.0
github.com/golang/mock v1.4.4
github.com/google/martian v2.1.0+incompatible
github.com/google/uuid v1.1.4
github.com/h2non/filetype v1.1.0
github.com/google/uuid v1.2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/h2non/filetype v1.1.1
github.com/hashicorp/golang-lru v0.5.4
github.com/hsanjuan/ipfs-lite v1.1.17
github.com/hsanjuan/ipfs-lite v1.1.18
github.com/improbable-eng/grpc-web v0.13.0
github.com/ipfs/go-cid v0.0.7
github.com/ipfs/go-datastore v0.4.5
@ -38,12 +38,14 @@ require (
github.com/ipfs/interface-go-ipfs-core v0.4.0
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e
github.com/kelseyhightower/envconfig v1.4.0
github.com/libp2p/go-libp2p v0.12.0
github.com/libp2p/go-libp2p v0.13.1-0.20210219094403-69916ed4656f
github.com/libp2p/go-libp2p-connmgr v0.2.4
github.com/libp2p/go-libp2p-core v0.7.0
github.com/libp2p/go-libp2p-core v0.8.5
github.com/libp2p/go-libp2p-kad-dht v0.11.1
github.com/libp2p/go-libp2p-peerstore v0.2.6
github.com/libp2p/go-libp2p-swarm v0.4.2
github.com/libp2p/go-libp2p-tls v0.1.3
github.com/libp2p/go-tcp-transport v0.2.1
github.com/magiconair/properties v1.8.4
github.com/mauidude/go-readability v0.0.0-20141216012317-2f30b1a346f1
github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d
@ -55,9 +57,11 @@ require (
github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multihash v0.0.14
github.com/otiai10/opengraph v1.1.3
github.com/prometheus/client_golang v1.9.0
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/spf13/cobra v0.0.5
github.com/stretchr/testify v1.7.0
github.com/textileio/go-threads v1.0.2-0.20201217154614-3a79caa4def7
github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404
github.com/tyler-smith/go-bip39 v1.0.1-0.20190808214741-c55f737395bc
github.com/yuin/goldmark v1.3.1
go.uber.org/zap v1.16.0
@ -69,8 +73,8 @@ require (
replace github.com/JohannesKaufmann/html-to-markdown => github.com/anytypeio/html-to-markdown v0.0.0-20200617145221-2afd2a14bae1
replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.0.2-0.20201217154614-3a79caa4def7
replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.0.2-0.20210310115034-a6122e977ff0
replace github.com/ipfs/go-log/v2 => github.com/anytypeio/go-log/v2 v2.1.2-0.20200810212702-264b187bb04f
replace gopkg.in/Graylog2/go-gelf.v2 => github.com/anytypeio/go-gelf v0.0.0-20200813115635-198b2af80f88
replace gopkg.in/Graylog2/go-gelf.v2 => github.com/anytypeio/go-gelf v0.0.0-20210309123244-863840d21dbd

596
go.sum

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,749 @@
{
"id": 3,
"title": "System Monitoring",
"tags": [
"alerts"
],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": true,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": null,
"decimals": null,
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"id": 7,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 6,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"expr": "ALERTS{alertname=\"high_load\",alertstate=\"firing\"}",
"interval": "",
"intervalFactor": 1,
"legendFormat": "",
"metric": "ALERTS",
"refId": "A",
"step": 120
}
],
"thresholds": "0.6,0.9",
"title": "Load Status",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "HIGH",
"value": "1"
},
{
"op": "=",
"text": "OK",
"value": "0"
}
],
"valueName": "current"
},
{
"cacheTimeout": null,
"colorBackground": true,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": null,
"decimals": null,
"editable": true,
"error": false,
"format": "none",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": false,
"thresholdLabels": false,
"thresholdMarkers": true
},
"id": 9,
"interval": null,
"isNew": true,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 6,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"expr": "ALERTS{alertname=\"service_down\",alertstate=\"firing\"}",
"interval": "",
"intervalFactor": 1,
"legendFormat": "",
"metric": "ALERTS",
"refId": "A",
"step": 120
}
],
"thresholds": "0.6,0.9",
"title": "Service Down",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "Offline",
"value": "1"
},
{
"op": "=",
"text": "OK",
"value": "0"
}
],
"valueName": "current"
}
],
"title": "Row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 2,
"isNew": true,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "node_memory_MemFree",
"intervalFactor": 2,
"legendFormat": "{{job}}",
"metric": "node_memory_MemFree",
"refId": "A",
"step": 30
}
],
"timeFrom": null,
"timeShift": null,
"title": "Free RAM",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bytes",
"label": "",
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": 500000000,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": 250000000,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"isNew": true,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "node_memory_SwapFree",
"intervalFactor": 2,
"legendFormat": "{{job}}",
"refId": "A",
"step": 30
}
],
"timeFrom": null,
"timeShift": null,
"title": "Free Swap",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 6,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "process_virtual_memory_bytes",
"interval": "",
"intervalFactor": 2,
"legendFormat": "{{job}}",
"metric": "process_virtual_memory_bytes",
"refId": "A",
"step": 20
}
],
"timeFrom": null,
"timeShift": null,
"title": "Virtual Mem Size",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"decimals": null,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": 1,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": 2,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 5,
"isNew": true,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 8,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "node_load1",
"interval": "",
"intervalFactor": 1,
"legendFormat": "{{job}}",
"metric": "node_load1",
"refId": "A",
"step": 15
}
],
"timeFrom": null,
"timeShift": null,
"title": "CPU Load",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": "Prometheus",
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": 20,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": 10,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"isNew": true,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "(node_memory_MemFree + node_memory_SwapFree)/(node_memory_MemTotal + node_memory_SwapTotal)*100",
"intervalFactor": 2,
"legendFormat": "{{job}}",
"refId": "A",
"step": 60
}
],
"timeFrom": null,
"timeShift": null,
"title": "Free Memory (RAM + Swap)",
"tooltip": {
"msResolution": true,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "percent",
"label": "",
"logBase": 1,
"max": 100,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": 229,
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Prometheus",
"decimals": null,
"editable": true,
"error": false,
"fill": 0,
"grid": {
"threshold1": 2500000000,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": 1000000000,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"isNew": true,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "node_filesystem_avail{mountpoint=\"/\"}",
"interval": "",
"intervalFactor": 2,
"legendFormat": "{{job}}",
"metric": "",
"refId": "A",
"step": 60
}
],
"timeFrom": null,
"timeShift": null,
"title": "Free Storage",
"tooltip": {
"msResolution": false,
"shared": true,
"sort": 0,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bytes",
"label": "",
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": "",
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
}
],
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "10s",
"schemaVersion": 12,
"version": 5,
"links": [],
"gnetId": null
}

View file

@ -0,0 +1,27 @@
version: '3.7'
services:
prometheus:
image: prom/prometheus:v2.1.0
volumes:
- ./prometheus/:/etc/prometheus/
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
ports:
- 9090:9090
restart: unless-stopped
grafana:
image: grafana/grafana
user: "104"
depends_on:
- prometheus
ports:
- 127.0.0.1:3000:3000
volumes:
- ./grafana/provisioning/:/etc/grafana/provisioning/
env_file:
- ./grafana/config.monitoring
restart: unless-stopped

View file

@ -0,0 +1,5 @@
GF_SECURITY_ADMIN_PASSWORD=foobar
GF_USERS_ALLOW_SIGN_UP=false
GF_AUTH_ANONYMOUS_ENABLED=true
GF_AUTH_ANONYMOUS_ORG_NAME=Main Org.
GF_AUTH_ANONYMOUS_ROLE=Viewer

View file

@ -0,0 +1,11 @@
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: false
options:
path: /etc/grafana/provisioning/dashboards

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,838 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 6,
"links": [],
"panels": [
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 0,
"y": 0
},
"id": 13,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "7.4.3",
"targets": [
{
"expr": "anytype_mw_threads_total",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Served threads",
"type": "stat"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 6,
"y": 0
},
"id": 10,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "7.4.3",
"targets": [
{
"expr": "anytype_mw_change_created",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Changes created",
"type": "stat"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 7,
"x": 12,
"y": 0
},
"id": 11,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "7.4.3",
"targets": [
{
"expr": "anytype_mw_change_received",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Changes received",
"type": "stat"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 8,
"x": 0,
"y": 7
},
"hiddenSeries": false,
"id": 6,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "histogram_quantile(0.5, anytype_mw_external_thread_handling_seconds_bucket)",
"interval": "",
"legendFormat": "50%",
"refId": "A"
},
{
"expr": "histogram_quantile(0.75, anytype_mw_external_thread_handling_seconds_bucket)",
"interval": "",
"legendFormat": "75%",
"refId": "B"
},
{
"expr": "histogram_quantile(0.95, anytype_mw_external_thread_handling_seconds_bucket)",
"interval": "",
"legendFormat": "95%",
"refId": "C"
},
{
"expr": "histogram_quantile(0.99, anytype_mw_external_thread_handling_seconds_bucket)",
"interval": "",
"legendFormat": "99%",
"refId": "D"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "External thread handling duration quantiles",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 8,
"x": 8,
"y": 7
},
"hiddenSeries": false,
"id": 16,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "histogram_quantile(0.5, anytype_mw_thread_add_replicator_seconds_bucket)",
"interval": "",
"legendFormat": "50%",
"refId": "A"
},
{
"expr": "histogram_quantile(0.75, anytype_mw_thread_add_replicator_seconds_bucket)",
"interval": "",
"legendFormat": "75%",
"refId": "B"
},
{
"expr": "histogram_quantile(0.95, anytype_mw_thread_add_replicator_seconds_bucket)",
"interval": "",
"legendFormat": "95%",
"refId": "C"
},
{
"expr": "histogram_quantile(0.99, anytype_mw_thread_add_replicator_seconds_bucket)",
"interval": "",
"legendFormat": "99%",
"refId": "D"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Created thread addReplicator duration quantiles",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 8,
"x": 0,
"y": 14
},
"hiddenSeries": false,
"id": 8,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "anytype_mw_external_thread_handling_attempts/ anytype_mw_external_thread_received",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "External thread handling attempts per thread",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 8,
"x": 8,
"y": 14
},
"hiddenSeries": false,
"id": 18,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "avg(rate(anytype_mw_external_thread_handling_seconds_count[5m]))",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "External thread handling rate",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {},
"custom": {},
"thresholds": {
"mode": "absolute",
"steps": []
}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 7,
"x": 0,
"y": 20
},
"hiddenSeries": false,
"id": 12,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "anytype_mw_external_thread_received - anytype_mw_external_thread_handling_seconds_count",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "External threads not proceed",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {},
"custom": {},
"thresholds": {
"mode": "absolute",
"steps": []
}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 6,
"w": 7,
"x": 7,
"y": 20
},
"hiddenSeries": false,
"id": 17,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.3",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "anytype_mw_thread_added-anytype_mw_thread_add_replicator_seconds_count",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Created thread not replicated",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 27,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "MW",
"uid": "qzyhMBsMk",
"version": 6
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
# config file version
apiVersion: 1
# list of datasources that should be deleted from the database
deleteDatasources:
- name: Prometheus
orgId: 1
# list of datasources to insert/update depending
# whats available in the database
datasources:
# <string, required> name of the datasource. Required
- name: Prometheus
# <string, required> datasource type. Required
type: prometheus
# <string, required> access mode. direct or proxy. Required
access: proxy
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
url: http://prometheus:9090
# <string> database password, if used
password:
# <string> database user, if used
user:
# <string> database name, if used
database:
# <bool> enable/disable basic auth
basicAuth: false
# <string> basic auth username, if used
basicAuthUser:
# <string> basic auth password, if used
basicAuthPassword:
# <bool> enable/disable with credentials headers
withCredentials:
# <bool> mark as default datasource. Max one per org
isDefault: true
# <map> fields that will be converted to json and stored in json_data
jsonData:
graphiteVersion: "1.1"
tlsAuth: false
tlsAuthWithCACert: false
# <string> json object of data that will be encrypted.
secureJsonData:
tlsCACert: "..."
tlsClientCert: "..."
tlsClientKey: "..."
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

View file

@ -0,0 +1,18 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_timeout: 10s
external_labels:
monitor: 'mw'
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 5s
static_configs:
- targets: ['prometheus:9090']
- job_name: 'mw'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:9094']

143
metrics/metrics.go Normal file
View file

@ -0,0 +1,143 @@
package metrics
import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
"os"
"sync"
"time"
)
var log = logging.Logger("anytype-logger")
var (
Enabled bool
once sync.Once
ServedThreads = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "threads_total",
Help: "Number of served threads",
})
ChangeCreatedCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "change_created",
Help: "Number of changes created",
})
ChangeReceivedCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "change_received",
Help: "Number of changes received",
})
ExternalThreadReceivedCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "external_thread_received",
Help: "New thread received",
})
ExternalThreadHandlingAttempts = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "external_thread_handling_attempts",
Help: "New thread handling attempts",
})
ExternalThreadHandlingDuration = promauto.NewHistogram(prometheus.HistogramOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "external_thread_handling_seconds",
Help: "New thread successfully handling duration",
Buckets: MetricTimeBuckets([]time.Duration{
256 * time.Millisecond,
512 * time.Millisecond,
1024 * time.Millisecond,
2 * time.Second,
4 * time.Second,
8 * time.Second,
16 * time.Second,
30 * time.Second,
45 * time.Second,
60 * time.Second,
90 * time.Second,
120 * time.Second,
180 * time.Second,
240 * time.Second,
}),
})
ThreadAdded = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "thread_added",
Help: "New thread added",
})
ThreadAddReplicatorAttempts = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "thread_add_replicator_attempts",
Help: "New thread handling attempts",
})
ThreadAddReplicatorDuration = promauto.NewHistogram(prometheus.HistogramOpts{
Namespace: "anytype",
Subsystem: "mw",
Name: "thread_add_replicator_seconds",
Help: "New thread successfully handling duration",
Buckets: MetricTimeBuckets([]time.Duration{
256 * time.Millisecond,
512 * time.Millisecond,
1024 * time.Millisecond,
2 * time.Second,
4 * time.Second,
8 * time.Second,
16 * time.Second,
30 * time.Second,
45 * time.Second,
60 * time.Second,
90 * time.Second,
120 * time.Second,
180 * time.Second,
240 * time.Second,
}),
})
)
func runPrometheusHttp(addr string) {
once.Do(func() {
// Create a HTTP server for prometheus.
httpServer := &http.Server{Handler: promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}), Addr: addr}
Enabled = true
// Start your http server for prometheus.
go func() {
if err := httpServer.ListenAndServe(); err != nil {
Enabled = false
log.Errorf("Unable to start a prometheus http server.")
}
}()
})
}
func MetricTimeBuckets(scale []time.Duration) []float64 {
buckets := make([]float64, len(scale))
for i, b := range scale {
buckets[i] = b.Seconds()
}
return buckets
}
func init() {
if addr := os.Getenv("ANYTYPE_PROM"); addr != "" {
runPrometheusHttp(addr)
}
}

File diff suppressed because it is too large Load diff

View file

@ -28,14 +28,16 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type SmartBlockType int32
const (
SmartBlockType_Page SmartBlockType = 0
SmartBlockType_Home SmartBlockType = 1
SmartBlockType_ProfilePage SmartBlockType = 2
SmartBlockType_Archive SmartBlockType = 3
SmartBlockType_Breadcrumbs SmartBlockType = 4
SmartBlockType_Set SmartBlockType = 5
SmartBlockType_ObjectType SmartBlockType = 6
SmartBlockType_File SmartBlockType = 7
SmartBlockType_Page SmartBlockType = 0
SmartBlockType_Home SmartBlockType = 1
SmartBlockType_ProfilePage SmartBlockType = 2
SmartBlockType_Archive SmartBlockType = 3
SmartBlockType_Breadcrumbs SmartBlockType = 4
SmartBlockType_Set SmartBlockType = 5
SmartBlockType_ObjectType SmartBlockType = 6
SmartBlockType_File SmartBlockType = 7
SmartBlockType_MarketplaceType SmartBlockType = 8
SmartBlockType_MarketplaceRelation SmartBlockType = 9
)
var SmartBlockType_name = map[int32]string{
@ -47,17 +49,21 @@ var SmartBlockType_name = map[int32]string{
5: "Set",
6: "ObjectType",
7: "File",
8: "MarketplaceType",
9: "MarketplaceRelation",
}
var SmartBlockType_value = map[string]int32{
"Page": 0,
"Home": 1,
"ProfilePage": 2,
"Archive": 3,
"Breadcrumbs": 4,
"Set": 5,
"ObjectType": 6,
"File": 7,
"Page": 0,
"Home": 1,
"ProfilePage": 2,
"Archive": 3,
"Breadcrumbs": 4,
"Set": 5,
"ObjectType": 6,
"File": 7,
"MarketplaceType": 8,
"MarketplaceRelation": 9,
}
func (x SmartBlockType) String() string {
@ -251,6 +257,7 @@ type EventMessage struct {
// *EventMessageValueOfBlockDataviewRecordsUpdate
// *EventMessageValueOfBlockDataviewRecordsInsert
// *EventMessageValueOfBlockDataviewRecordsDelete
// *EventMessageValueOfBlockDataviewSourceSet
// *EventMessageValueOfBlockDataviewViewSet
// *EventMessageValueOfBlockDataviewViewDelete
// *EventMessageValueOfBlockDataviewRelationDelete
@ -376,6 +383,9 @@ type EventMessageValueOfBlockDataviewRecordsInsert struct {
type EventMessageValueOfBlockDataviewRecordsDelete struct {
BlockDataviewRecordsDelete *EventBlockDataviewRecordsDelete `protobuf:"bytes,28,opt,name=blockDataviewRecordsDelete,proto3,oneof" json:"blockDataviewRecordsDelete,omitempty"`
}
type EventMessageValueOfBlockDataviewSourceSet struct {
BlockDataviewSourceSet *EventBlockDataviewSourceSet `protobuf:"bytes,35,opt,name=blockDataviewSourceSet,proto3,oneof" json:"blockDataviewSourceSet,omitempty"`
}
type EventMessageValueOfBlockDataviewViewSet struct {
BlockDataviewViewSet *EventBlockDataviewViewSet `protobuf:"bytes,19,opt,name=blockDataviewViewSet,proto3,oneof" json:"blockDataviewViewSet,omitempty"`
}
@ -442,6 +452,7 @@ func (*EventMessageValueOfBlockDataviewRecordsSet) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewRecordsUpdate) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewRecordsInsert) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewRecordsDelete) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewSourceSet) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewViewSet) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewViewDelete) IsEventMessageValue() {}
func (*EventMessageValueOfBlockDataviewRelationDelete) IsEventMessageValue() {}
@ -625,6 +636,13 @@ func (m *EventMessage) GetBlockDataviewRecordsDelete() *EventBlockDataviewRecord
return nil
}
func (m *EventMessage) GetBlockDataviewSourceSet() *EventBlockDataviewSourceSet {
if x, ok := m.GetValue().(*EventMessageValueOfBlockDataviewSourceSet); ok {
return x.BlockDataviewSourceSet
}
return nil
}
func (m *EventMessage) GetBlockDataviewViewSet() *EventBlockDataviewViewSet {
if x, ok := m.GetValue().(*EventMessageValueOfBlockDataviewViewSet); ok {
return x.BlockDataviewViewSet
@ -749,6 +767,7 @@ func (*EventMessage) XXX_OneofWrappers() []interface{} {
(*EventMessageValueOfBlockDataviewRecordsUpdate)(nil),
(*EventMessageValueOfBlockDataviewRecordsInsert)(nil),
(*EventMessageValueOfBlockDataviewRecordsDelete)(nil),
(*EventMessageValueOfBlockDataviewSourceSet)(nil),
(*EventMessageValueOfBlockDataviewViewSet)(nil),
(*EventMessageValueOfBlockDataviewViewDelete)(nil),
(*EventMessageValueOfBlockDataviewRelationDelete)(nil),
@ -5120,6 +5139,58 @@ func (m *EventBlockDataviewViewDelete) GetViewId() string {
return ""
}
type EventBlockDataviewSourceSet struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"`
}
func (m *EventBlockDataviewSourceSet) Reset() { *m = EventBlockDataviewSourceSet{} }
func (m *EventBlockDataviewSourceSet) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewSourceSet) ProtoMessage() {}
func (*EventBlockDataviewSourceSet) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 2}
}
func (m *EventBlockDataviewSourceSet) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *EventBlockDataviewSourceSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_EventBlockDataviewSourceSet.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *EventBlockDataviewSourceSet) XXX_Merge(src proto.Message) {
xxx_messageInfo_EventBlockDataviewSourceSet.Merge(m, src)
}
func (m *EventBlockDataviewSourceSet) XXX_Size() int {
return m.Size()
}
func (m *EventBlockDataviewSourceSet) XXX_DiscardUnknown() {
xxx_messageInfo_EventBlockDataviewSourceSet.DiscardUnknown(m)
}
var xxx_messageInfo_EventBlockDataviewSourceSet proto.InternalMessageInfo
func (m *EventBlockDataviewSourceSet) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *EventBlockDataviewSourceSet) GetSource() string {
if m != nil {
return m.Source
}
return ""
}
type EventBlockDataviewRelationDelete struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
RelationKey string `protobuf:"bytes,2,opt,name=relationKey,proto3" json:"relationKey,omitempty"`
@ -5129,7 +5200,7 @@ func (m *EventBlockDataviewRelationDelete) Reset() { *m = EventBlockData
func (m *EventBlockDataviewRelationDelete) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRelationDelete) ProtoMessage() {}
func (*EventBlockDataviewRelationDelete) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 2}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 3}
}
func (m *EventBlockDataviewRelationDelete) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -5183,7 +5254,7 @@ func (m *EventBlockDataviewRelationSet) Reset() { *m = EventBlockDatavie
func (m *EventBlockDataviewRelationSet) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRelationSet) ProtoMessage() {}
func (*EventBlockDataviewRelationSet) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 3}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 4}
}
func (m *EventBlockDataviewRelationSet) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -5245,7 +5316,7 @@ func (m *EventBlockDataviewRecordsSet) Reset() { *m = EventBlockDataview
func (m *EventBlockDataviewRecordsSet) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRecordsSet) ProtoMessage() {}
func (*EventBlockDataviewRecordsSet) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 4}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 5}
}
func (m *EventBlockDataviewRecordsSet) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -5314,7 +5385,7 @@ func (m *EventBlockDataviewRecordsInsert) Reset() { *m = EventBlockDatav
func (m *EventBlockDataviewRecordsInsert) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRecordsInsert) ProtoMessage() {}
func (*EventBlockDataviewRecordsInsert) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 5}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 6}
}
func (m *EventBlockDataviewRecordsInsert) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -5382,7 +5453,7 @@ func (m *EventBlockDataviewRecordsUpdate) Reset() { *m = EventBlockDatav
func (m *EventBlockDataviewRecordsUpdate) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRecordsUpdate) ProtoMessage() {}
func (*EventBlockDataviewRecordsUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 6}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 7}
}
func (m *EventBlockDataviewRecordsUpdate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -5443,7 +5514,7 @@ func (m *EventBlockDataviewRecordsDelete) Reset() { *m = EventBlockDatav
func (m *EventBlockDataviewRecordsDelete) String() string { return proto.CompactTextString(m) }
func (*EventBlockDataviewRecordsDelete) ProtoMessage() {}
func (*EventBlockDataviewRecordsDelete) Descriptor() ([]byte, []int) {
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 7}
return fileDescriptor_a966342d378ae5f5, []int{0, 2, 7, 8}
}
func (m *EventBlockDataviewRecordsDelete) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -6742,6 +6813,7 @@ func init() {
proto.RegisterType((*EventBlockDataview)(nil), "anytype.Event.Block.Dataview")
proto.RegisterType((*EventBlockDataviewViewSet)(nil), "anytype.Event.Block.Dataview.ViewSet")
proto.RegisterType((*EventBlockDataviewViewDelete)(nil), "anytype.Event.Block.Dataview.ViewDelete")
proto.RegisterType((*EventBlockDataviewSourceSet)(nil), "anytype.Event.Block.Dataview.SourceSet")
proto.RegisterType((*EventBlockDataviewRelationDelete)(nil), "anytype.Event.Block.Dataview.RelationDelete")
proto.RegisterType((*EventBlockDataviewRelationSet)(nil), "anytype.Event.Block.Dataview.RelationSet")
proto.RegisterType((*EventBlockDataviewRecordsSet)(nil), "anytype.Event.Block.Dataview.RecordsSet")
@ -6775,217 +6847,221 @@ func init() {
func init() { proto.RegisterFile("pb/protos/events.proto", fileDescriptor_a966342d378ae5f5) }
var fileDescriptor_a966342d378ae5f5 = []byte{
// 3349 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0x4b, 0x6c, 0xdc, 0xd6,
0xb9, 0x9e, 0x07, 0xe7, 0xf5, 0x8f, 0x25, 0x4f, 0x8e, 0x7d, 0x6d, 0x5e, 0x5a, 0x91, 0x1d, 0xf9,
0x11, 0x27, 0xb6, 0xc7, 0xb9, 0x4e, 0x62, 0x39, 0x4e, 0x6c, 0x47, 0x2f, 0x63, 0x14, 0xdb, 0xb2,
0x72, 0x64, 0x25, 0x17, 0xb9, 0x17, 0x28, 0xa8, 0xe1, 0x99, 0x11, 0x23, 0x0e, 0x39, 0x20, 0x39,
0x92, 0x95, 0xa2, 0xab, 0x2e, 0x5a, 0x14, 0x68, 0x51, 0x14, 0x45, 0xbb, 0xec, 0xa2, 0x8b, 0xa2,
0x9b, 0x6e, 0x8a, 0x00, 0x5d, 0x74, 0x5b, 0xa0, 0xdd, 0x65, 0x99, 0x65, 0xe0, 0x6c, 0xba, 0x6c,
0xbb, 0x0f, 0x50, 0x9c, 0x07, 0xc9, 0x43, 0x0e, 0x39, 0x1c, 0xa5, 0x09, 0xd0, 0x22, 0xd9, 0xcd,
0x39, 0xe7, 0xff, 0xbe, 0xf3, 0xfa, 0xf9, 0xf3, 0xfb, 0x0f, 0xcf, 0xc0, 0xa9, 0xe1, 0xce, 0xf5,
0xa1, 0xeb, 0xf8, 0x8e, 0x77, 0x9d, 0xec, 0x13, 0xdb, 0xf7, 0xda, 0xac, 0x84, 0x6a, 0xba, 0x7d,
0xe8, 0x1f, 0x0e, 0x89, 0x76, 0x61, 0xb8, 0xd7, 0xbf, 0x6e, 0x99, 0x3b, 0xd7, 0x87, 0x3b, 0xd7,
0x07, 0x8e, 0x41, 0xac, 0xc0, 0x9c, 0x15, 0x84, 0xb9, 0xf6, 0x92, 0x64, 0xe5, 0x12, 0x4b, 0xf7,
0x4d, 0xc7, 0x0e, 0x0c, 0x83, 0xb2, 0x30, 0x9d, 0xeb, 0x3b, 0x4e, 0xdf, 0x22, 0xbc, 0x75, 0x67,
0xd4, 0xbb, 0xee, 0xf9, 0xee, 0xa8, 0xeb, 0xf3, 0xd6, 0x85, 0x3f, 0x1a, 0x50, 0x59, 0xa3, 0x03,
0x41, 0x37, 0xa0, 0x3e, 0x20, 0x9e, 0xa7, 0xf7, 0x89, 0xa7, 0x16, 0xcf, 0x95, 0x2f, 0x37, 0x6f,
0x9c, 0x6a, 0x8b, 0x41, 0xb5, 0x99, 0x45, 0xfb, 0x11, 0x6f, 0xc6, 0xa1, 0x1d, 0x9a, 0x83, 0x46,
0xd7, 0xb1, 0x7d, 0xf2, 0xd4, 0x5f, 0x37, 0xd4, 0xd2, 0xb9, 0xe2, 0xe5, 0x06, 0x8e, 0x2a, 0xd0,
0x6b, 0xd0, 0x30, 0x6d, 0xd3, 0x37, 0x75, 0xdf, 0x71, 0xd5, 0xf2, 0xb9, 0x62, 0x8c, 0x92, 0x4d,
0xa7, 0xbd, 0xd4, 0xed, 0x3a, 0x23, 0xdb, 0xc7, 0x91, 0xa1, 0xf6, 0x9b, 0xd3, 0x50, 0x13, 0x3d,
0xa1, 0x7b, 0xd0, 0xd4, 0xb9, 0xc5, 0xd6, 0xae, 0x73, 0xa0, 0x16, 0x19, 0xc7, 0x99, 0xc4, 0xb0,
0x04, 0x47, 0x9b, 0x9a, 0x74, 0x0a, 0x58, 0x46, 0xa0, 0x75, 0x98, 0x15, 0xc5, 0x55, 0xe2, 0xeb,
0xa6, 0xe5, 0xa9, 0x7f, 0xe1, 0x24, 0xf3, 0x19, 0x24, 0xc2, 0xac, 0x53, 0xc0, 0x09, 0x20, 0x7a,
0x08, 0xc7, 0x77, 0x2c, 0xa7, 0xbb, 0xb7, 0x45, 0x42, 0xae, 0x16, 0xa3, 0x3a, 0x97, 0xa0, 0x5a,
0xa6, 0x56, 0xed, 0x2d, 0x22, 0x93, 0x25, 0xa1, 0x08, 0xc3, 0x73, 0x41, 0x15, 0x16, 0xfb, 0xe5,
0xa9, 0xa7, 0x18, 0xdf, 0x42, 0x26, 0x5f, 0x68, 0xd9, 0x29, 0xe0, 0x71, 0x38, 0xfa, 0x3f, 0x38,
0x19, 0x55, 0x7a, 0xbe, 0x6b, 0x76, 0x39, 0x6d, 0x9d, 0xd1, 0x5e, 0x9c, 0x40, 0x1b, 0x19, 0x77,
0x0a, 0x38, 0x95, 0x04, 0xbd, 0x01, 0x0d, 0x5e, 0x4f, 0x37, 0x62, 0x9e, 0x31, 0xfe, 0x77, 0x3a,
0x23, 0xdf, 0x86, 0xc8, 0x1a, 0xdd, 0x84, 0x3a, 0x2b, 0x2c, 0x19, 0xdc, 0x49, 0x9a, 0x37, 0xd4,
0x54, 0xe4, 0x92, 0x61, 0x74, 0x0a, 0x38, 0xb4, 0xa5, 0xbb, 0xcf, 0x7e, 0xaf, 0x12, 0x8b, 0xf8,
0x44, 0x78, 0xd0, 0x99, 0x54, 0x28, 0x37, 0xa1, 0xbb, 0x2f, 0x21, 0xd0, 0x2a, 0x34, 0x7b, 0xa6,
0x45, 0xbc, 0xed, 0xa1, 0xe5, 0xe8, 0x86, 0xaa, 0x4c, 0xd8, 0xae, 0xfb, 0x91, 0x1d, 0x65, 0x91,
0x60, 0xe8, 0x2e, 0x34, 0x06, 0xba, 0xbb, 0xe7, 0xad, 0xdb, 0x3d, 0x47, 0xad, 0xa4, 0x7a, 0x0f,
0xe7, 0x78, 0x14, 0x58, 0xd1, 0xe9, 0x87, 0x10, 0xea, 0x83, 0xc1, 0x8a, 0xde, 0x37, 0x89, 0x65,
0x78, 0x6a, 0x95, 0x91, 0x9c, 0xcd, 0xdc, 0x10, 0x6e, 0x46, 0x7d, 0x30, 0x0e, 0x44, 0xff, 0x0b,
0x27, 0x82, 0x9a, 0x95, 0x5d, 0xd3, 0x32, 0x5c, 0x62, 0xaf, 0x1b, 0x9e, 0x5a, 0x63, 0x7c, 0x17,
0x32, 0xf9, 0x24, 0xdb, 0x4e, 0x01, 0xa7, 0x51, 0x20, 0x03, 0x4e, 0x07, 0xd5, 0xcb, 0x7a, 0x77,
0xaf, 0xef, 0x3a, 0x23, 0xdb, 0x58, 0x71, 0x2c, 0xc7, 0x55, 0x1b, 0x8c, 0xfd, 0x72, 0x26, 0x7b,
0xc2, 0xbe, 0x53, 0xc0, 0x59, 0x54, 0x68, 0x05, 0x8e, 0x05, 0x4d, 0x4f, 0xc8, 0x53, 0x5f, 0x05,
0x46, 0xfd, 0x7c, 0x26, 0x35, 0x35, 0xea, 0x14, 0x70, 0x0c, 0x24, 0x93, 0xd0, 0x5d, 0x53, 0x9b,
0x39, 0x24, 0xd4, 0x48, 0x26, 0xa1, 0x65, 0x99, 0xe4, 0xa1, 0x69, 0xef, 0xa9, 0x33, 0x39, 0x24,
0xd4, 0x48, 0x26, 0xa1, 0x65, 0xf4, 0x18, 0x5a, 0xe1, 0x4c, 0x1d, 0x67, 0x8f, 0x6e, 0xb9, 0x3a,
0xcb, 0x88, 0x5e, 0xc8, 0x5e, 0x2d, 0x61, 0xd8, 0x29, 0xe0, 0x31, 0x30, 0xba, 0x0f, 0x33, 0x41,
0xdd, 0x92, 0x65, 0xf6, 0x6d, 0xf5, 0xf8, 0x04, 0x77, 0xa3, 0x6c, 0xcc, 0xaa, 0x53, 0xc0, 0x71,
0x18, 0x7a, 0x5b, 0x3c, 0x39, 0x34, 0xe0, 0x98, 0xfb, 0xea, 0x73, 0x8c, 0x65, 0x2e, 0x3b, 0x4e,
0x99, 0xfb, 0xe1, 0xa3, 0xc3, 0x21, 0xa1, 0x3f, 0xac, 0xea, 0xbe, 0xbe, 0x6f, 0x92, 0x03, 0x4c,
0xba, 0x8e, 0x6b, 0x78, 0x5b, 0xc4, 0x57, 0xd1, 0x04, 0x7f, 0x08, 0xcc, 0xdb, 0x91, 0x7d, 0xe8,
0x0f, 0xe3, 0x54, 0x68, 0x00, 0x5a, 0x5a, 0xd3, 0xf6, 0xd0, 0xd0, 0x7d, 0xa2, 0x6a, 0xac, 0xa3,
0x2b, 0x53, 0x75, 0xc4, 0x21, 0x9d, 0x02, 0x9e, 0x40, 0x98, 0xd5, 0xdd, 0xba, 0xed, 0x11, 0xd7,
0x57, 0xcf, 0x1c, 0xa1, 0x3b, 0x0e, 0xc9, 0xea, 0x8e, 0xb7, 0x66, 0x75, 0x27, 0xc2, 0xd9, 0xdc,
0x11, 0xba, 0x0b, 0xc3, 0xdb, 0x04, 0xc2, 0x30, 0xfc, 0x07, 0xad, 0xef, 0x99, 0xe4, 0x80, 0xee,
0xd7, 0x89, 0x09, 0xe1, 0x3f, 0xec, 0x48, 0x18, 0x87, 0xe1, 0x3f, 0x41, 0x32, 0xe6, 0x0f, 0xb4,
0x5e, 0x4c, 0xe4, 0xe4, 0x34, 0xfe, 0x10, 0xd9, 0x8f, 0xf9, 0x43, 0xd4, 0x84, 0x86, 0x70, 0x26,
0x31, 0x41, 0xfe, 0x6e, 0x13, 0x3d, 0xa9, 0xac, 0xa7, 0xab, 0x79, 0x4b, 0x26, 0x63, 0x3a, 0x05,
0x3c, 0x89, 0x12, 0xf5, 0x41, 0x4d, 0x6d, 0xa6, 0x0b, 0x77, 0x9a, 0x75, 0xf7, 0xd2, 0x74, 0xdd,
0xf1, 0xc5, 0xcb, 0x24, 0x93, 0x63, 0x45, 0x50, 0xad, 0xfe, 0x57, 0x4e, 0xac, 0x08, 0x0c, 0xe5,
0x58, 0x11, 0xd4, 0xd1, 0x58, 0x31, 0xf2, 0x88, 0xcb, 0xac, 0xdf, 0x71, 0x4c, 0x5b, 0x3d, 0x9b,
0x1a, 0x2b, 0xb6, 0x3d, 0xe2, 0x0a, 0x4a, 0x6a, 0x45, 0x63, 0x45, 0x0c, 0x16, 0xe3, 0x79, 0x48,
0x7a, 0xbe, 0x7a, 0x2e, 0x8f, 0x87, 0x5a, 0xc5, 0x78, 0x68, 0x05, 0x75, 0xbf, 0xb0, 0x62, 0x8b,
0x58, 0xa4, 0xeb, 0x63, 0xdd, 0xee, 0x13, 0xf5, 0x85, 0x54, 0xf7, 0x93, 0xe8, 0x24, 0x63, 0xea,
0x7e, 0x69, 0x24, 0x68, 0x1b, 0x50, 0x58, 0x4f, 0x5f, 0x02, 0x9c, 0x7a, 0x81, 0x51, 0x9f, 0xcf,
0xa6, 0x0e, 0x4d, 0x3b, 0x05, 0x9c, 0x42, 0x80, 0x5e, 0x02, 0x65, 0x68, 0xda, 0x7d, 0xd5, 0x60,
0x44, 0x27, 0x12, 0x44, 0x9b, 0xa6, 0xdd, 0xef, 0x14, 0x30, 0x33, 0x41, 0x6f, 0x01, 0x0c, 0x5d,
0xa7, 0x4b, 0x3c, 0x6f, 0x83, 0x1c, 0xa8, 0x84, 0x01, 0xb4, 0x24, 0x80, 0x1b, 0xb4, 0x37, 0x08,
0x55, 0x40, 0x92, 0x3d, 0x5a, 0x83, 0x19, 0x51, 0x12, 0xb1, 0xad, 0x97, 0xfa, 0xbe, 0x09, 0x08,
0xc2, 0x68, 0x16, 0x47, 0x51, 0x45, 0x24, 0x2a, 0x56, 0x1d, 0x9b, 0xa8, 0xfd, 0x54, 0x45, 0x14,
0x90, 0x50, 0x13, 0x1a, 0xd6, 0x25, 0x04, 0x5a, 0x86, 0x63, 0xfe, 0xae, 0x4b, 0x74, 0x63, 0xcb,
0xd7, 0xfd, 0x91, 0xa7, 0xda, 0xa9, 0x6f, 0x06, 0xde, 0xd8, 0x7e, 0xc2, 0x2c, 0xe9, 0x5b, 0x4f,
0xc6, 0x2c, 0xd7, 0xa0, 0xb2, 0xaf, 0x5b, 0x23, 0xa2, 0x7d, 0x5c, 0x84, 0x9a, 0xd0, 0xcd, 0xda,
0x06, 0x28, 0x4c, 0xeb, 0x9d, 0x84, 0x8a, 0x69, 0x1b, 0xe4, 0x29, 0xd3, 0xea, 0x15, 0xcc, 0x0b,
0xe8, 0x15, 0xa8, 0x09, 0x35, 0x2d, 0x04, 0x60, 0x56, 0x1e, 0x10, 0x98, 0x69, 0x1f, 0x40, 0x2d,
0x90, 0xca, 0x73, 0xd0, 0x18, 0xba, 0x0e, 0x55, 0x64, 0xeb, 0x06, 0xa3, 0x6d, 0xe0, 0xa8, 0x02,
0xfd, 0x0f, 0xd4, 0x0c, 0x21, 0xc7, 0x39, 0xf5, 0xe9, 0x36, 0x4f, 0x78, 0xda, 0x41, 0xc2, 0xd3,
0xde, 0x62, 0x09, 0x0f, 0x0e, 0xec, 0xb4, 0xbf, 0xdd, 0x81, 0x0a, 0x73, 0x04, 0xed, 0x55, 0x28,
0x53, 0xa1, 0x79, 0x15, 0xaa, 0xec, 0xf1, 0x0a, 0x12, 0x9f, 0x93, 0x89, 0xd1, 0x31, 0x63, 0x2c,
0x6c, 0xb4, 0xcf, 0x14, 0x31, 0xd7, 0x53, 0x50, 0x75, 0x1d, 0xc7, 0x0f, 0x47, 0x25, 0x4a, 0x12,
0x5d, 0x29, 0x9f, 0x0e, 0xdd, 0x8e, 0x26, 0x50, 0x66, 0xe6, 0xb9, 0xf9, 0x44, 0x38, 0x13, 0x74,
0x05, 0x14, 0x6a, 0xc8, 0x94, 0xed, 0xec, 0x8d, 0xd3, 0x21, 0x70, 0x6b, 0xa0, 0xbb, 0x3e, 0xf7,
0xf5, 0xc3, 0x21, 0xc1, 0xcc, 0x08, 0xdd, 0x85, 0xa6, 0xb3, 0xf3, 0x21, 0xe9, 0xfa, 0xb4, 0xce,
0x53, 0x2b, 0xac, 0xb3, 0x68, 0xeb, 0xc3, 0xb4, 0xf1, 0x71, 0x68, 0x84, 0x65, 0x00, 0xfa, 0x0e,
0x9c, 0x88, 0x8a, 0x9b, 0xc4, 0xe5, 0x76, 0x6a, 0x95, 0xf1, 0x5c, 0xcb, 0xcc, 0x05, 0x24, 0xbe,
0x10, 0x84, 0xd3, 0x98, 0xd0, 0x2d, 0x68, 0xb8, 0x61, 0x2e, 0x54, 0x63, 0xb4, 0xda, 0xf8, 0xf0,
0x82, 0x00, 0x88, 0x23, 0x63, 0xed, 0xbb, 0xa0, 0x05, 0xd5, 0xef, 0x9b, 0xfe, 0xee, 0x7b, 0xd4,
0x3d, 0x23, 0x5e, 0x0d, 0xea, 0xbc, 0xbb, 0x70, 0xa7, 0xc2, 0x32, 0x5a, 0x92, 0xfb, 0xe4, 0xdb,
0x75, 0x3e, 0xbb, 0xcf, 0x90, 0x5c, 0xee, 0xfc, 0x5d, 0x38, 0x91, 0x32, 0xc5, 0x89, 0xbd, 0xce,
0x03, 0x44, 0x0b, 0x20, 0x12, 0x67, 0xa9, 0x46, 0x5b, 0x81, 0xa6, 0x94, 0x90, 0x20, 0x15, 0x6a,
0xcc, 0x59, 0x42, 0xa6, 0xa0, 0x48, 0x3b, 0xa1, 0xcf, 0xc1, 0xa6, 0xee, 0xef, 0xb2, 0xd1, 0x37,
0x70, 0x58, 0xd6, 0x2e, 0x40, 0x55, 0xbc, 0xe4, 0x34, 0x91, 0x80, 0xd1, 0x5c, 0xa1, 0xc8, 0xad,
0x82, 0xb2, 0xf6, 0xff, 0xd0, 0x08, 0xf3, 0x16, 0xf4, 0x18, 0x8e, 0x89, 0xbc, 0x85, 0x07, 0x58,
0x6a, 0x3c, 0x2b, 0x69, 0x14, 0xc9, 0x7f, 0xdb, 0x2b, 0x34, 0xcf, 0xb7, 0xb9, 0x46, 0x67, 0xa9,
0x4f, 0x9b, 0xb9, 0x4c, 0x8c, 0x40, 0xfb, 0x58, 0x85, 0xf2, 0x16, 0xf1, 0xb5, 0x87, 0xd1, 0xe3,
0x3c, 0x0b, 0x25, 0x33, 0x98, 0x47, 0xc9, 0xfc, 0x52, 0x0f, 0xf0, 0x36, 0x34, 0xa2, 0xac, 0x37,
0xc9, 0x77, 0x6b, 0x7c, 0x47, 0xa7, 0xf4, 0xa2, 0x21, 0xd4, 0xc3, 0xb7, 0x6b, 0x92, 0x75, 0x11,
0xca, 0x7b, 0xe4, 0x50, 0x8c, 0xf0, 0x62, 0xee, 0x1b, 0xbb, 0xfd, 0x80, 0x1c, 0x62, 0x8a, 0xd0,
0xce, 0x40, 0xf9, 0x01, 0x39, 0xa4, 0x71, 0x91, 0x05, 0x4d, 0x41, 0x29, 0x22, 0xe8, 0x3a, 0x54,
0x45, 0x66, 0x97, 0xec, 0xef, 0x3a, 0x54, 0x7b, 0x3c, 0x59, 0xcc, 0x59, 0x14, 0x61, 0xa6, 0xdd,
0x83, 0xa6, 0x9c, 0xcf, 0x25, 0xf9, 0xce, 0x41, 0xb3, 0x2b, 0x65, 0x8c, 0xdc, 0x57, 0xe4, 0x2a,
0x8d, 0xc0, 0xb1, 0x58, 0xc2, 0x9f, 0x64, 0x58, 0x83, 0x63, 0xae, 0x7c, 0xaa, 0x50, 0x4a, 0x88,
0x17, 0xd9, 0x37, 0x64, 0x22, 0x1c, 0x83, 0x69, 0x0f, 0xe0, 0x78, 0x32, 0x2b, 0x4c, 0xf6, 0x74,
0x19, 0x8e, 0xef, 0x24, 0x72, 0x50, 0xfe, 0x88, 0x24, 0xab, 0xb5, 0x75, 0xa8, 0xf0, 0x84, 0x27,
0x49, 0xf1, 0x0a, 0x54, 0x74, 0x96, 0x40, 0x95, 0x58, 0x64, 0xd4, 0x52, 0x47, 0xc9, 0xa0, 0x98,
0x1b, 0x6a, 0xbf, 0x53, 0x40, 0x61, 0xe9, 0x65, 0x92, 0xea, 0x26, 0x28, 0x3e, 0xcd, 0x55, 0x4b,
0x39, 0x87, 0x33, 0xec, 0x39, 0x60, 0xd2, 0x82, 0xd9, 0xa3, 0x37, 0xa0, 0xe2, 0xf9, 0x87, 0x56,
0x70, 0x6e, 0x71, 0x7e, 0x32, 0x70, 0x8b, 0x9a, 0x62, 0x8e, 0xa0, 0x50, 0xf6, 0x14, 0x89, 0x13,
0x8b, 0x1c, 0x28, 0x7b, 0x7c, 0x31, 0x47, 0xa0, 0x7b, 0x50, 0xeb, 0xee, 0x92, 0xee, 0x1e, 0x31,
0xc4, 0x51, 0xc5, 0xc5, 0xc9, 0xe0, 0x15, 0x6e, 0x8c, 0x03, 0x14, 0xed, 0xbb, 0xcb, 0x96, 0xbc,
0x3a, 0x4d, 0xdf, 0x6c, 0x1b, 0x30, 0x47, 0x68, 0x73, 0x62, 0x05, 0xd3, 0x7d, 0xfd, 0x3e, 0x54,
0xd8, 0x24, 0xd1, 0x1d, 0xb9, 0x79, 0xf6, 0xc6, 0x8b, 0xf9, 0xd1, 0x45, 0x2c, 0x4e, 0xc8, 0xc3,
0x66, 0x1c, 0xe7, 0x69, 0x4e, 0xc3, 0x23, 0x56, 0x8a, 0xf3, 0x9c, 0x85, 0x9a, 0x98, 0x7c, 0x7c,
0xc0, 0xf5, 0xc0, 0xe0, 0x79, 0xa8, 0x70, 0xff, 0x4c, 0x9f, 0xcf, 0x2f, 0x8a, 0x50, 0xa6, 0x99,
0xf2, 0x78, 0xfc, 0x11, 0xfb, 0x9e, 0xe7, 0x30, 0xab, 0xe6, 0x7e, 0x6c, 0xdb, 0xb5, 0xb5, 0x60,
0x85, 0xde, 0x8a, 0xaf, 0xd0, 0xa5, 0x89, 0x33, 0x93, 0x68, 0xf8, 0xc0, 0x7e, 0x52, 0x01, 0x85,
0x9d, 0x71, 0xa4, 0x79, 0x72, 0xf0, 0xbe, 0x99, 0x34, 0x30, 0x0a, 0x6e, 0x4b, 0xc2, 0x81, 0x79,
0xb2, 0xee, 0xe7, 0x7b, 0x32, 0x03, 0x52, 0x95, 0xc8, 0xa6, 0x44, 0x05, 0xeb, 0x4d, 0x50, 0x06,
0xe6, 0x80, 0x08, 0x47, 0xce, 0xe9, 0xf2, 0x91, 0x39, 0x20, 0x98, 0xd9, 0x53, 0xdc, 0xae, 0xee,
0xed, 0x0a, 0x1f, 0xce, 0xc1, 0x75, 0x74, 0x6f, 0x17, 0x33, 0x7b, 0x8a, 0xb3, 0xf5, 0x01, 0x11,
0xce, 0x9b, 0x83, 0xdb, 0xd0, 0x69, 0x7f, 0xd4, 0x9e, 0xe2, 0x3c, 0xf3, 0x23, 0x22, 0x4e, 0xd2,
0x72, 0x70, 0x5b, 0xe6, 0x47, 0x04, 0x33, 0x7b, 0xea, 0xf2, 0x94, 0x25, 0xc3, 0x45, 0x9e, 0x87,
0xca, 0xfb, 0xa6, 0xe1, 0xef, 0xc6, 0x9b, 0x2b, 0xb1, 0x27, 0x82, 0xae, 0xd2, 0x91, 0x9e, 0x08,
0x79, 0x91, 0x39, 0xcf, 0x2a, 0x28, 0x74, 0xb7, 0x8e, 0xe6, 0x36, 0xd1, 0x26, 0x0b, 0x96, 0x39,
0x50, 0xe8, 0x42, 0x66, 0x4c, 0x65, 0x0e, 0x14, 0xba, 0x3d, 0xd9, 0xad, 0x74, 0x51, 0xe2, 0xad,
0xe5, 0xa0, 0xf5, 0x47, 0x65, 0x50, 0xd8, 0x79, 0x59, 0xd2, 0x21, 0xdf, 0x85, 0x19, 0x5f, 0x77,
0xfb, 0x84, 0x4b, 0xd5, 0xf5, 0xe0, 0x74, 0xf8, 0xca, 0xc4, 0x53, 0xb8, 0xf6, 0x13, 0x19, 0x82,
0xe3, 0x0c, 0xd3, 0x47, 0x5d, 0x46, 0x15, 0x8b, 0xba, 0x6f, 0x85, 0xaf, 0x5c, 0x25, 0xe7, 0x3c,
0x95, 0x61, 0xf9, 0x8b, 0x3b, 0x7c, 0xff, 0x5e, 0x84, 0x99, 0xd8, 0xc0, 0xbe, 0xd2, 0x28, 0x28,
0x0f, 0x96, 0xf3, 0x2c, 0x86, 0xca, 0xe1, 0x5a, 0x3c, 0x0c, 0x66, 0x0a, 0x05, 0x01, 0xfc, 0x75,
0x05, 0xea, 0xe1, 0x79, 0x63, 0x8a, 0xca, 0x19, 0xb9, 0x56, 0xae, 0xca, 0x09, 0xf0, 0xed, 0x6d,
0xd7, 0xc2, 0x14, 0x41, 0x67, 0xe3, 0x9b, 0x7e, 0xb8, 0xec, 0x2f, 0xe6, 0x43, 0x9f, 0x50, 0x73,
0xcc, 0x51, 0xe8, 0x31, 0x34, 0x0d, 0xe2, 0x75, 0x5d, 0x73, 0xc8, 0xce, 0x45, 0xf8, 0xfa, 0x5f,
0xcb, 0x27, 0x59, 0x8d, 0x40, 0x58, 0x66, 0x40, 0xeb, 0xd0, 0x30, 0x07, 0x7a, 0x9f, 0x74, 0xa2,
0x20, 0x72, 0x25, 0x9f, 0x6e, 0x3d, 0x80, 0xe0, 0x08, 0x4d, 0xc7, 0xd6, 0xd3, 0xf7, 0xcd, 0xae,
0x63, 0x33, 0xb2, 0xea, 0xb4, 0x63, 0xbb, 0x1f, 0x81, 0xb0, 0xcc, 0x80, 0x6e, 0x8b, 0x30, 0xcc,
0x63, 0xcd, 0xa5, 0x29, 0x96, 0x2a, 0x0c, 0xc5, 0x54, 0x4d, 0x6e, 0xbb, 0x56, 0x76, 0xb8, 0x61,
0xab, 0x9a, 0xd1, 0x7c, 0x1e, 0x9a, 0xd2, 0x7a, 0x65, 0x18, 0xbd, 0x00, 0x8d, 0x70, 0x15, 0xb2,
0x79, 0xa4, 0xb9, 0x65, 0x18, 0xdd, 0x11, 0x31, 0xe9, 0xf5, 0xb8, 0x9b, 0x9f, 0x4d, 0xb8, 0x39,
0x75, 0xec, 0x4d, 0x97, 0xb0, 0x93, 0x34, 0x39, 0x18, 0x7d, 0x7a, 0x9a, 0xbd, 0xc3, 0xac, 0xaf,
0x38, 0x71, 0x78, 0x02, 0xc7, 0x57, 0x75, 0x5f, 0xdf, 0xd1, 0x3d, 0x22, 0xce, 0x4e, 0xd3, 0x58,
0x5d, 0xde, 0x24, 0x92, 0x87, 0x6c, 0x56, 0x61, 0xf7, 0xad, 0x8a, 0xff, 0xf7, 0x51, 0xf1, 0xbf,
0xcf, 0x52, 0xf1, 0x8b, 0x31, 0x15, 0x7f, 0x3e, 0xeb, 0x1b, 0xa0, 0x35, 0x26, 0xe3, 0x6f, 0xc7,
0x5f, 0x28, 0x17, 0x72, 0x90, 0xb1, 0x37, 0xca, 0xed, 0xb8, 0x8e, 0xcf, 0xc3, 0xc6, 0x84, 0xfc,
0xdb, 0x49, 0x21, 0x7f, 0x29, 0x07, 0x3d, 0xa6, 0xe4, 0x6f, 0xc7, 0x95, 0x7c, 0x5e, 0xef, 0xdf,
0x60, 0x29, 0xff, 0xcb, 0x0c, 0x29, 0xff, 0x46, 0x5c, 0xca, 0x4f, 0xf0, 0x9a, 0xaf, 0x4b, 0xcb,
0xff, 0x3c, 0x4b, 0xcb, 0x2f, 0xc6, 0xb4, 0xfc, 0x84, 0x91, 0x25, 0xc5, 0xfc, 0xed, 0xb8, 0x98,
0xbf, 0x90, 0x83, 0x8c, 0xa9, 0xf9, 0xc5, 0x98, 0x9a, 0xcf, 0xeb, 0x54, 0x92, 0xf3, 0x8b, 0x31,
0x39, 0x9f, 0x07, 0x94, 0xf4, 0xfc, 0x62, 0x4c, 0xcf, 0xe7, 0x01, 0x25, 0x41, 0xbf, 0x18, 0x13,
0xf4, 0x79, 0xc0, 0x6f, 0x15, 0xfd, 0xd1, 0x14, 0xfd, 0x8f, 0xb3, 0x14, 0x3d, 0x4e, 0x57, 0xf4,
0x57, 0xb3, 0xd7, 0x3f, 0x5f, 0xd2, 0x4f, 0x1f, 0x81, 0xc7, 0x35, 0xfd, 0x9d, 0x84, 0xa6, 0xbf,
0x98, 0x03, 0xfe, 0x0f, 0x15, 0xf5, 0xbf, 0x9d, 0x24, 0xea, 0x6f, 0xc9, 0xa2, 0x7e, 0xc2, 0x5b,
0x64, 0x5c, 0xd5, 0xdf, 0x8d, 0xab, 0xfa, 0xcb, 0x53, 0x60, 0x63, 0xb2, 0x7e, 0x33, 0x4d, 0xd6,
0xb7, 0xa7, 0x60, 0xc9, 0xd4, 0xf5, 0xef, 0x8c, 0xeb, 0xfa, 0xab, 0x53, 0xf0, 0xa5, 0x0a, 0xfb,
0xcd, 0x34, 0x61, 0x3f, 0xcd, 0xe8, 0x32, 0x95, 0xfd, 0x9b, 0x31, 0x65, 0xff, 0xe2, 0x34, 0xcb,
0xf5, 0xcd, 0x93, 0xf6, 0xff, 0xa8, 0x42, 0x3d, 0xf8, 0x40, 0xae, 0xfd, 0xaa, 0x08, 0xb5, 0xe0,
0x8a, 0x41, 0xd2, 0x6f, 0x4f, 0x41, 0x95, 0xda, 0x84, 0x37, 0x0b, 0x45, 0x09, 0xdd, 0x05, 0x85,
0xfe, 0x12, 0x4e, 0xf9, 0xf2, 0xe4, 0x17, 0xaa, 0x7c, 0xff, 0x00, 0x33, 0x1c, 0xe5, 0x75, 0x7a,
0x3d, 0x8f, 0xf8, 0xcc, 0x21, 0x67, 0xb0, 0x28, 0xd1, 0xd9, 0x5a, 0xe6, 0xc0, 0xf4, 0x99, 0x5f,
0xcd, 0x60, 0x5e, 0xd0, 0x5e, 0x03, 0x90, 0x2e, 0x28, 0x4c, 0x39, 0x46, 0x6d, 0x19, 0x66, 0x13,
0x17, 0x0d, 0x52, 0xa4, 0x7c, 0xf0, 0xe5, 0xe1, 0x81, 0xf8, 0xb0, 0xd0, 0xc0, 0x72, 0x95, 0x76,
0x00, 0x4d, 0xf9, 0x02, 0xc1, 0x91, 0x09, 0xd0, 0x4d, 0xa8, 0x07, 0x45, 0xb1, 0x58, 0x93, 0x3e,
0x84, 0x84, 0xb6, 0xda, 0xf7, 0x00, 0xa4, 0x3b, 0x3a, 0xd3, 0x6e, 0x8b, 0x94, 0x38, 0x95, 0xa7,
0x4b, 0x9c, 0xe8, 0x8a, 0xfb, 0x8e, 0xaf, 0x5b, 0xec, 0x81, 0x99, 0xc1, 0xbc, 0xa0, 0xfd, 0xac,
0x08, 0x33, 0xf1, 0x8b, 0x34, 0x5f, 0xe3, 0x10, 0x2e, 0xc1, 0xac, 0xc9, 0x3a, 0xd9, 0x74, 0x3c,
0x93, 0xad, 0x14, 0xdf, 0xfd, 0x44, 0xad, 0xf6, 0x61, 0x38, 0x26, 0xf1, 0x29, 0xfe, 0x4b, 0x8c,
0x49, 0x99, 0x32, 0x9f, 0x7c, 0x37, 0xec, 0xeb, 0x68, 0x5e, 0x87, 0x54, 0xda, 0xd7, 0xc0, 0xd9,
0x27, 0x06, 0xfb, 0x2a, 0xdb, 0xc0, 0x41, 0x51, 0xfb, 0x7b, 0x09, 0x94, 0x6d, 0x8f, 0xb8, 0xda,
0x5f, 0x4b, 0xc1, 0xb7, 0xef, 0x5b, 0xa0, 0xb0, 0xfb, 0x1f, 0xd2, 0xb7, 0xf9, 0x62, 0xe2, 0xdb,
0x7c, 0xec, 0x6a, 0x6c, 0xf4, 0x6d, 0xfe, 0x16, 0x28, 0xec, 0xc6, 0xc7, 0xd1, 0x91, 0xdf, 0x2f,
0x42, 0x23, 0xba, 0x7d, 0x71, 0x64, 0xbc, 0xfc, 0x21, 0xb4, 0x14, 0xff, 0x10, 0xfa, 0x32, 0x54,
0x5c, 0xf6, 0xc9, 0x92, 0x3b, 0x7a, 0xf2, 0x93, 0x3b, 0xeb, 0x10, 0x73, 0x13, 0x8d, 0x40, 0x53,
0xbe, 0x5b, 0x72, 0xf4, 0x61, 0x5c, 0x10, 0xd7, 0xf4, 0xd6, 0x0d, 0x6f, 0xc9, 0x75, 0xf5, 0x43,
0x91, 0x88, 0xc7, 0x2b, 0xa9, 0x84, 0xda, 0x34, 0xed, 0x7e, 0xfa, 0x95, 0x08, 0xed, 0x0f, 0x45,
0xa8, 0x89, 0x9b, 0x1a, 0xda, 0x22, 0x94, 0x37, 0xc8, 0x01, 0x1d, 0x88, 0xb8, 0xab, 0x31, 0x36,
0x90, 0x47, 0x6c, 0x16, 0xc2, 0x1e, 0x07, 0x66, 0xda, 0x6d, 0xa8, 0x0a, 0x77, 0x3c, 0x3a, 0xf6,
0x16, 0x28, 0xec, 0x4a, 0xc8, 0xd1, 0x91, 0x3f, 0xac, 0x43, 0x95, 0xdf, 0x05, 0xd1, 0xbe, 0xa8,
0x41, 0x95, 0x5f, 0x13, 0x41, 0x77, 0xa1, 0xe6, 0x8d, 0x06, 0x03, 0xdd, 0x3d, 0x14, 0x3c, 0x17,
0x26, 0xdd, 0x2a, 0x69, 0x6f, 0x71, 0x5b, 0x1c, 0x80, 0xd0, 0xeb, 0xa0, 0x74, 0xf5, 0x1e, 0x19,
0x3b, 0x91, 0x48, 0x03, 0xaf, 0xe8, 0x3d, 0x82, 0x99, 0x39, 0x7a, 0x1b, 0xea, 0x62, 0x5b, 0x82,
0x87, 0x7e, 0x72, 0xbf, 0xc1, 0x66, 0x86, 0x28, 0xed, 0x1d, 0xa8, 0x89, 0xc1, 0xa0, 0x7b, 0x50,
0xf5, 0xf8, 0xc5, 0x98, 0xa4, 0x80, 0x4b, 0x9d, 0xc2, 0xa1, 0xdd, 0xe5, 0x35, 0x58, 0xc0, 0xb4,
0x3f, 0x95, 0x40, 0xa1, 0x83, 0xfb, 0x97, 0x99, 0xd0, 0x3c, 0x80, 0xa5, 0x7b, 0xfe, 0xe6, 0xc8,
0xb2, 0x08, 0xf7, 0xf6, 0x32, 0x96, 0x6a, 0xd0, 0x65, 0x38, 0xce, 0x4b, 0xde, 0xee, 0xd6, 0xa8,
0xdb, 0x25, 0xc4, 0x60, 0xae, 0x5f, 0xc7, 0xc9, 0x6a, 0xb4, 0x04, 0x15, 0x76, 0x9d, 0x59, 0xe8,
0xaf, 0x2b, 0xb9, 0x2b, 0xdb, 0xde, 0x34, 0x6d, 0x31, 0x1a, 0x8e, 0xd4, 0x1c, 0x68, 0x84, 0x75,
0xf4, 0x21, 0x1c, 0x9a, 0xb6, 0x6d, 0xda, 0x7d, 0xe1, 0xd1, 0x41, 0x91, 0xc6, 0x25, 0xfa, 0x53,
0x8c, 0xb7, 0x82, 0x45, 0x89, 0xd6, 0xf7, 0x74, 0xd3, 0x12, 0x43, 0xac, 0x60, 0x51, 0xa2, 0x4c,
0x23, 0xe6, 0xbe, 0xfc, 0x6e, 0x76, 0x19, 0x07, 0x45, 0xed, 0x59, 0x74, 0xb5, 0x68, 0x2c, 0xfa,
0x21, 0x91, 0x13, 0xf2, 0x08, 0xc0, 0xd3, 0xbd, 0x39, 0x59, 0x17, 0x96, 0xf9, 0x1d, 0xa1, 0x48,
0xe9, 0xd1, 0x37, 0xbe, 0x6d, 0x99, 0x36, 0xcf, 0x5c, 0xeb, 0x58, 0x94, 0x12, 0x6b, 0x5c, 0x19,
0x5b, 0x63, 0xd1, 0xbe, 0x66, 0x98, 0x3e, 0x0b, 0xa9, 0x61, 0x3b, 0xaf, 0x41, 0x77, 0xa0, 0x66,
0x90, 0x7d, 0xb3, 0x4b, 0x82, 0xeb, 0x2a, 0xe7, 0x27, 0xae, 0xed, 0x2a, 0xb3, 0xc5, 0x01, 0x46,
0xf3, 0xa1, 0xca, 0xab, 0xc2, 0x29, 0x15, 0xa5, 0x29, 0x45, 0x83, 0x2e, 0x4d, 0x18, 0x74, 0x39,
0x67, 0xd0, 0x4a, 0x72, 0xd0, 0x0b, 0x0f, 0x00, 0x22, 0x77, 0x43, 0x4d, 0xa8, 0x6d, 0xdb, 0x7b,
0xb6, 0x73, 0x60, 0xb7, 0x0a, 0xb4, 0xf0, 0xb8, 0xd7, 0xa3, 0xbd, 0xb4, 0x8a, 0xb4, 0x40, 0xed,
0x4c, 0xbb, 0xdf, 0x2a, 0x21, 0x80, 0x2a, 0x2d, 0x10, 0xa3, 0x55, 0xa6, 0xbf, 0xef, 0xb3, 0xfd,
0x6b, 0x29, 0x0b, 0x3a, 0x7d, 0x55, 0x79, 0x43, 0xc7, 0xf6, 0xc8, 0xd7, 0xf4, 0x2f, 0x92, 0x85,
0x1f, 0x94, 0xa1, 0xc2, 0x02, 0x91, 0xf6, 0x45, 0x29, 0x0c, 0x99, 0x29, 0x27, 0xad, 0xd1, 0x79,
0xc8, 0xac, 0x74, 0x25, 0x2e, 0x16, 0xc2, 0xe4, 0x73, 0x90, 0x1b, 0xf2, 0x39, 0xc8, 0xac, 0x74,
0x0f, 0x2a, 0x8e, 0x88, 0x9d, 0x7f, 0xbc, 0x09, 0xf5, 0xa1, 0xeb, 0xf4, 0x5d, 0x1a, 0x2b, 0x95,
0xc4, 0x1d, 0xfe, 0x38, 0x6c, 0x53, 0x98, 0xe1, 0x10, 0xa0, 0x6d, 0x40, 0x3d, 0xa8, 0x8d, 0x84,
0x8f, 0xc8, 0xaa, 0x59, 0x81, 0x7a, 0x81, 0xe1, 0x88, 0xfd, 0x2e, 0x63, 0xf6, 0x9b, 0x3e, 0x22,
0x62, 0x9d, 0x84, 0x5b, 0x07, 0xc5, 0x85, 0x6b, 0x42, 0x86, 0xcf, 0x40, 0x63, 0xd5, 0x75, 0x86,
0xec, 0xbe, 0x50, 0xab, 0x40, 0x77, 0x67, 0x7d, 0x30, 0x74, 0x5c, 0xbf, 0x55, 0xa4, 0xbf, 0xd7,
0x9e, 0xb2, 0xdf, 0xa5, 0x85, 0xa5, 0xe0, 0x68, 0xa2, 0x0e, 0xca, 0x86, 0x63, 0x13, 0xbe, 0xdd,
0x78, 0xc4, 0x9e, 0xdc, 0x56, 0x91, 0x56, 0xd3, 0xd7, 0x41, 0xab, 0x84, 0x8e, 0x41, 0x7d, 0x45,
0xb7, 0xbb, 0xc4, 0x62, 0xbb, 0xdd, 0x80, 0xca, 0x9a, 0xeb, 0x3a, 0x6e, 0x4b, 0x79, 0x79, 0x1f,
0x66, 0xe3, 0x17, 0xcb, 0x28, 0x68, 0x53, 0xef, 0x53, 0xae, 0x3a, 0x28, 0x1d, 0x67, 0x40, 0xfd,
0xe6, 0x38, 0x34, 0x37, 0xf9, 0xed, 0x3c, 0xd6, 0x54, 0xa2, 0xdd, 0x2c, 0xb9, 0xdd, 0x5d, 0x73,
0x9f, 0xb4, 0xca, 0xb4, 0x75, 0x99, 0x3e, 0x0c, 0x5d, 0x77, 0x34, 0xd8, 0xf1, 0x5a, 0x0a, 0xaa,
0xb1, 0x0b, 0x42, 0xad, 0x0a, 0x9a, 0x05, 0x88, 0xae, 0x51, 0xb5, 0xaa, 0x94, 0x91, 0xce, 0xa9,
0x55, 0x5b, 0x9e, 0xfb, 0xf3, 0xb3, 0xf9, 0xe2, 0x27, 0xcf, 0xe6, 0x8b, 0x9f, 0x3d, 0x9b, 0x2f,
0xfe, 0xf4, 0xf3, 0xf9, 0xc2, 0x27, 0x9f, 0xcf, 0x17, 0x3e, 0xfd, 0x7c, 0xbe, 0xf0, 0x41, 0x69,
0xb8, 0xb3, 0x53, 0x65, 0x3a, 0xea, 0xd5, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x4f, 0xd7, 0x1f,
0x32, 0x5a, 0x35, 0x00, 0x00,
// 3413 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0xcb, 0x6f, 0xdc, 0xc6,
0x19, 0xdf, 0x07, 0xf7, 0xf5, 0xad, 0x25, 0x6d, 0xc6, 0xae, 0xcd, 0xd2, 0x8a, 0xec, 0xc8, 0x8f,
0x38, 0xb1, 0xbd, 0x4e, 0x9d, 0xc4, 0x72, 0x9c, 0xd8, 0x8e, 0x2c, 0xd9, 0x58, 0xc5, 0x2f, 0x65,
0xd6, 0x4a, 0x8a, 0xb4, 0x40, 0x41, 0x2d, 0x67, 0x57, 0x8c, 0xb8, 0xe4, 0x82, 0xe4, 0xca, 0x56,
0x8a, 0x9e, 0x7a, 0x68, 0x11, 0xa0, 0x45, 0x51, 0x14, 0xed, 0xb1, 0x87, 0xb6, 0x28, 0x7a, 0xe9,
0xa5, 0x08, 0xd0, 0x7f, 0xa0, 0x40, 0x7b, 0xcb, 0xa1, 0x87, 0x1c, 0x03, 0xe7, 0xd2, 0x6b, 0xff,
0x80, 0x00, 0xc5, 0x3c, 0x48, 0x0e, 0xb9, 0xe4, 0x72, 0x95, 0x26, 0x40, 0x8b, 0xe4, 0xb6, 0x33,
0xf3, 0xfd, 0x7e, 0xf3, 0xfa, 0xe6, 0x9b, 0xdf, 0x0c, 0x67, 0xe1, 0xe8, 0x68, 0xfb, 0xd2, 0xc8,
0x75, 0x7c, 0xc7, 0xbb, 0x44, 0xf6, 0x88, 0xed, 0x7b, 0x6d, 0x96, 0x42, 0x35, 0xdd, 0xde, 0xf7,
0xf7, 0x47, 0x44, 0x3b, 0x3d, 0xda, 0x1d, 0x5c, 0xb2, 0xcc, 0xed, 0x4b, 0xa3, 0xed, 0x4b, 0x43,
0xc7, 0x20, 0x56, 0x60, 0xce, 0x12, 0xc2, 0x5c, 0x7b, 0x41, 0xb2, 0x72, 0x89, 0xa5, 0xfb, 0xa6,
0x63, 0x07, 0x86, 0x41, 0x5a, 0x98, 0x2e, 0x0e, 0x1c, 0x67, 0x60, 0x11, 0x5e, 0xba, 0x3d, 0xee,
0x5f, 0xf2, 0x7c, 0x77, 0xdc, 0xf3, 0x79, 0xe9, 0xf2, 0x3f, 0x09, 0x54, 0x6e, 0xd3, 0x86, 0xa0,
0xcb, 0x50, 0x1f, 0x12, 0xcf, 0xd3, 0x07, 0xc4, 0x53, 0x8b, 0x27, 0xcb, 0xe7, 0x9a, 0x97, 0x8f,
0xb6, 0x45, 0xa3, 0xda, 0xcc, 0xa2, 0x7d, 0x9f, 0x17, 0xe3, 0xd0, 0x0e, 0x2d, 0x42, 0xa3, 0xe7,
0xd8, 0x3e, 0x79, 0xe2, 0x6f, 0x18, 0x6a, 0xe9, 0x64, 0xf1, 0x5c, 0x03, 0x47, 0x19, 0xe8, 0x15,
0x68, 0x98, 0xb6, 0xe9, 0x9b, 0xba, 0xef, 0xb8, 0x6a, 0xf9, 0x64, 0x31, 0x46, 0xc9, 0xba, 0xd3,
0x5e, 0xed, 0xf5, 0x9c, 0xb1, 0xed, 0xe3, 0xc8, 0x50, 0xfb, 0x50, 0x85, 0x9a, 0xa8, 0x09, 0xdd,
0x84, 0xa6, 0xce, 0x2d, 0xba, 0x3b, 0xce, 0x63, 0xb5, 0xc8, 0x38, 0x8e, 0x27, 0x9a, 0x25, 0x38,
0xda, 0xd4, 0xa4, 0x53, 0xc0, 0x32, 0x02, 0x6d, 0xc0, 0xbc, 0x48, 0xae, 0x13, 0x5f, 0x37, 0x2d,
0x4f, 0xfd, 0x07, 0x27, 0x59, 0xca, 0x20, 0x11, 0x66, 0x9d, 0x02, 0x4e, 0x00, 0xd1, 0x3d, 0x58,
0xd8, 0xb6, 0x9c, 0xde, 0x6e, 0x97, 0x84, 0x5c, 0x2d, 0x46, 0x75, 0x32, 0x41, 0x75, 0x8b, 0x5a,
0xb5, 0xbb, 0x44, 0x26, 0x4b, 0x42, 0x11, 0x86, 0x67, 0x82, 0x2c, 0x2c, 0xe6, 0xcb, 0x53, 0x8f,
0x32, 0xbe, 0xe5, 0x4c, 0xbe, 0xd0, 0xb2, 0x53, 0xc0, 0x93, 0x70, 0xf4, 0x3d, 0x38, 0x12, 0x65,
0x7a, 0xbe, 0x6b, 0xf6, 0x38, 0x6d, 0x9d, 0xd1, 0x9e, 0x99, 0x42, 0x1b, 0x19, 0x77, 0x0a, 0x38,
0x95, 0x04, 0xbd, 0x06, 0x0d, 0x9e, 0x4f, 0x27, 0x62, 0x89, 0x31, 0x7e, 0x3b, 0x9d, 0x91, 0x4f,
0x43, 0x64, 0x8d, 0xae, 0x40, 0x9d, 0x25, 0x56, 0x0d, 0xee, 0x24, 0xcd, 0xcb, 0x6a, 0x2a, 0x72,
0xd5, 0x30, 0x3a, 0x05, 0x1c, 0xda, 0xd2, 0xd9, 0x67, 0xbf, 0xd7, 0x89, 0x45, 0x7c, 0x22, 0x3c,
0xe8, 0x78, 0x2a, 0x94, 0x9b, 0xd0, 0xd9, 0x97, 0x10, 0x68, 0x1d, 0x9a, 0x7d, 0xd3, 0x22, 0xde,
0xd6, 0xc8, 0x72, 0x74, 0x43, 0x55, 0xa6, 0x4c, 0xd7, 0x9d, 0xc8, 0x8e, 0xb2, 0x48, 0x30, 0x74,
0x03, 0x1a, 0x43, 0xdd, 0xdd, 0xf5, 0x36, 0xec, 0xbe, 0xa3, 0x56, 0x52, 0xbd, 0x87, 0x73, 0xdc,
0x0f, 0xac, 0x68, 0xf7, 0x43, 0x08, 0xf5, 0xc1, 0x60, 0x44, 0xef, 0x98, 0xc4, 0x32, 0x3c, 0xb5,
0xca, 0x48, 0x4e, 0x64, 0x4e, 0x08, 0x37, 0xa3, 0x3e, 0x18, 0x07, 0xa2, 0xef, 0xc2, 0xe1, 0x20,
0x67, 0x6d, 0xc7, 0xb4, 0x0c, 0x97, 0xd8, 0x1b, 0x86, 0xa7, 0xd6, 0x18, 0xdf, 0xe9, 0x4c, 0x3e,
0xc9, 0xb6, 0x53, 0xc0, 0x69, 0x14, 0xc8, 0x80, 0x63, 0x41, 0xf6, 0x2d, 0xbd, 0xb7, 0x3b, 0x70,
0x9d, 0xb1, 0x6d, 0xac, 0x39, 0x96, 0xe3, 0xaa, 0x0d, 0xc6, 0x7e, 0x2e, 0x93, 0x3d, 0x61, 0xdf,
0x29, 0xe0, 0x2c, 0x2a, 0xb4, 0x06, 0x87, 0x82, 0xa2, 0x47, 0xe4, 0x89, 0xaf, 0x02, 0xa3, 0x7e,
0x36, 0x93, 0x9a, 0x1a, 0x75, 0x0a, 0x38, 0x06, 0x92, 0x49, 0xe8, 0xac, 0xa9, 0xcd, 0x1c, 0x12,
0x6a, 0x24, 0x93, 0xd0, 0xb4, 0x4c, 0x72, 0xcf, 0xb4, 0x77, 0xd5, 0xb9, 0x1c, 0x12, 0x6a, 0x24,
0x93, 0xd0, 0x34, 0x7a, 0x08, 0xad, 0xb0, 0xa7, 0x8e, 0xb3, 0x4b, 0xa7, 0x5c, 0x9d, 0x67, 0x44,
0xcf, 0x65, 0x8f, 0x96, 0x30, 0xec, 0x14, 0xf0, 0x04, 0x18, 0xdd, 0x81, 0xb9, 0x20, 0x6f, 0xd5,
0x32, 0x07, 0xb6, 0xba, 0x30, 0xc5, 0xdd, 0x28, 0x1b, 0xb3, 0xea, 0x14, 0x70, 0x1c, 0x86, 0xde,
0x14, 0x2b, 0x87, 0x06, 0x1c, 0x73, 0x4f, 0x7d, 0x86, 0xb1, 0x2c, 0x66, 0xc7, 0x29, 0x73, 0x2f,
0x5c, 0x3a, 0x1c, 0x12, 0xfa, 0xc3, 0xba, 0xee, 0xeb, 0x7b, 0x26, 0x79, 0x8c, 0x49, 0xcf, 0x71,
0x0d, 0xaf, 0x4b, 0x7c, 0x15, 0x4d, 0xf1, 0x87, 0xc0, 0xbc, 0x1d, 0xd9, 0x87, 0xfe, 0x30, 0x49,
0x85, 0x86, 0xa0, 0xa5, 0x15, 0x6d, 0x8d, 0x0c, 0xdd, 0x27, 0xaa, 0xc6, 0x2a, 0x3a, 0x3f, 0x53,
0x45, 0x1c, 0xd2, 0x29, 0xe0, 0x29, 0x84, 0x59, 0xd5, 0x6d, 0xd8, 0x1e, 0x71, 0x7d, 0xf5, 0xf8,
0x01, 0xaa, 0xe3, 0x90, 0xac, 0xea, 0x78, 0x69, 0x56, 0x75, 0x22, 0x9c, 0x2d, 0x1e, 0xa0, 0xba,
0x30, 0xbc, 0x4d, 0x21, 0x44, 0x3a, 0x1c, 0x8d, 0x95, 0x76, 0x9d, 0xb1, 0xdb, 0x23, 0x74, 0xc6,
0x4e, 0xb1, 0xaa, 0x9e, 0x9f, 0x5e, 0x55, 0x68, 0xde, 0x29, 0xe0, 0x0c, 0xa2, 0x70, 0x87, 0x09,
0x4a, 0xde, 0xa1, 0xa5, 0xc4, 0x57, 0x0f, 0x4f, 0xd9, 0x61, 0xc2, 0x0a, 0x84, 0x71, 0xb8, 0xc3,
0x24, 0x48, 0x26, 0x5c, 0x8e, 0xe6, 0x8b, 0xb1, 0x3a, 0x32, 0x8b, 0xcb, 0x45, 0xf6, 0x13, 0x2e,
0x17, 0x15, 0xa1, 0x11, 0x1c, 0x4f, 0x8c, 0x21, 0xdf, 0x3e, 0x45, 0x4d, 0x2a, 0xab, 0xe9, 0x42,
0xde, 0xac, 0xc8, 0x98, 0x4e, 0x01, 0x4f, 0xa3, 0x44, 0x03, 0x50, 0x53, 0x8b, 0xe9, 0xc0, 0x1d,
0x63, 0xd5, 0xbd, 0x30, 0x5b, 0x75, 0x7c, 0xf0, 0x32, 0xc9, 0xe4, 0x70, 0x14, 0x64, 0xab, 0xdf,
0xca, 0x09, 0x47, 0x81, 0xa1, 0x1c, 0x8e, 0x82, 0x3c, 0x1a, 0x8e, 0xc6, 0x1e, 0x71, 0x99, 0xf5,
0x5b, 0x8e, 0x69, 0xab, 0x27, 0x52, 0xc3, 0xd1, 0x96, 0x47, 0x5c, 0x41, 0x49, 0xad, 0x68, 0x38,
0x8a, 0xc1, 0x62, 0x3c, 0xf7, 0x48, 0xdf, 0x57, 0x4f, 0xe6, 0xf1, 0x50, 0xab, 0x18, 0x0f, 0xcd,
0xa0, 0xee, 0x17, 0x66, 0x74, 0x89, 0x45, 0x7a, 0x3e, 0xd6, 0xed, 0x01, 0x51, 0x9f, 0x4b, 0x75,
0x3f, 0x89, 0x4e, 0x32, 0xa6, 0xee, 0x97, 0x46, 0x82, 0xb6, 0x00, 0x85, 0xf9, 0x74, 0x9f, 0xe1,
0xd4, 0xcb, 0x8c, 0xfa, 0x54, 0x36, 0x75, 0x68, 0xda, 0x29, 0xe0, 0x14, 0x02, 0xf4, 0x02, 0x28,
0x23, 0xd3, 0x1e, 0xa8, 0x06, 0x23, 0x3a, 0x9c, 0x20, 0xda, 0x34, 0xed, 0x41, 0xa7, 0x80, 0x99,
0x09, 0x7a, 0x03, 0x60, 0xe4, 0x3a, 0x3d, 0xe2, 0x79, 0x0f, 0xc8, 0x63, 0x95, 0x30, 0x80, 0x96,
0x04, 0x70, 0x83, 0xf6, 0x03, 0x42, 0x45, 0x96, 0x64, 0x8f, 0x6e, 0xc3, 0x9c, 0x48, 0x89, 0xf0,
0xd9, 0x4f, 0xdd, 0xd2, 0x02, 0x82, 0x30, 0x60, 0xc6, 0x51, 0x54, 0x74, 0x89, 0x8c, 0x75, 0xc7,
0x26, 0xea, 0x20, 0x55, 0x74, 0x05, 0x24, 0xd4, 0x84, 0xee, 0x1c, 0x12, 0x02, 0xdd, 0x82, 0x43,
0xfe, 0x8e, 0x4b, 0x74, 0xa3, 0xeb, 0xeb, 0xfe, 0xd8, 0x53, 0xed, 0xd4, 0xcd, 0x87, 0x17, 0xb6,
0x1f, 0x31, 0x4b, 0xba, 0xb1, 0xca, 0x98, 0x5b, 0x35, 0xa8, 0xec, 0xe9, 0xd6, 0x98, 0x68, 0x1f,
0x15, 0xa1, 0x26, 0xa4, 0xb9, 0xf6, 0x00, 0x14, 0x26, 0x27, 0x8f, 0x40, 0xc5, 0xb4, 0x0d, 0xf2,
0x84, 0x1d, 0x07, 0x2a, 0x98, 0x27, 0xd0, 0x4b, 0x50, 0x13, 0x82, 0x5d, 0x68, 0xcc, 0xac, 0xa3,
0x46, 0x60, 0xa6, 0xbd, 0x07, 0xb5, 0x40, 0x8d, 0x2f, 0x42, 0x63, 0xe4, 0x3a, 0x54, 0xf4, 0x6d,
0x18, 0x8c, 0xb6, 0x81, 0xa3, 0x0c, 0xf4, 0x1d, 0xa8, 0x19, 0x42, 0xf1, 0x73, 0xea, 0x63, 0x6d,
0x7e, 0xa6, 0x6a, 0x07, 0x67, 0xaa, 0x76, 0x97, 0x9d, 0xa9, 0x70, 0x60, 0xa7, 0xfd, 0xe1, 0x06,
0x54, 0x98, 0x23, 0x68, 0x2f, 0x43, 0x99, 0x6a, 0xd9, 0x0b, 0x50, 0x65, 0xcb, 0x2b, 0x38, 0x5b,
0x1d, 0x49, 0xb4, 0x8e, 0x19, 0x63, 0x61, 0xa3, 0x7d, 0xaa, 0x88, 0xbe, 0x1e, 0x85, 0xaa, 0xeb,
0x38, 0x7e, 0xd8, 0x2a, 0x91, 0x92, 0xe8, 0x4a, 0xf9, 0x74, 0xe8, 0x5a, 0xd4, 0x81, 0x32, 0x33,
0xcf, 0x3d, 0xb2, 0x84, 0x3d, 0x41, 0xe7, 0x41, 0xa1, 0x86, 0x4c, 0x3c, 0xcf, 0x5f, 0x3e, 0x16,
0x02, 0xbb, 0x43, 0xdd, 0xf5, 0xb9, 0xaf, 0xef, 0x8f, 0x08, 0x66, 0x46, 0xe8, 0x06, 0x34, 0x9d,
0xed, 0xf7, 0x49, 0xcf, 0xa7, 0x79, 0x9e, 0x5a, 0x61, 0x95, 0x45, 0x53, 0x1f, 0x9e, 0x4c, 0x1f,
0x86, 0x46, 0x58, 0x06, 0xa0, 0x1f, 0xc0, 0xe1, 0x28, 0xb9, 0x49, 0x5c, 0x6e, 0xa7, 0x56, 0x19,
0xcf, 0xc5, 0xcc, 0xe3, 0x86, 0xc4, 0x17, 0x82, 0x70, 0x1a, 0x13, 0xba, 0x0a, 0x0d, 0x37, 0x3c,
0x6e, 0xd5, 0x18, 0xad, 0x36, 0xd9, 0xbc, 0x20, 0x00, 0xe2, 0xc8, 0x58, 0xfb, 0x21, 0x68, 0x41,
0xf6, 0xbb, 0xa6, 0xbf, 0xf3, 0x0e, 0x75, 0xcf, 0x88, 0x57, 0x83, 0x3a, 0xaf, 0x2e, 0x9c, 0xa9,
0x30, 0x8d, 0x56, 0xe5, 0x3a, 0xf9, 0x74, 0x9d, 0xca, 0xae, 0x33, 0x24, 0x97, 0x2b, 0x7f, 0x1b,
0x0e, 0xa7, 0x74, 0x71, 0x6a, 0xad, 0x4b, 0x00, 0xd1, 0x00, 0x88, 0xb3, 0xb9, 0x94, 0xa3, 0xad,
0x41, 0x53, 0x3a, 0xf3, 0x20, 0x15, 0x6a, 0xcc, 0x59, 0x42, 0xa6, 0x20, 0x49, 0x2b, 0xa1, 0xeb,
0x60, 0x53, 0xf7, 0x77, 0x58, 0xeb, 0x1b, 0x38, 0x4c, 0x6b, 0xa7, 0xa1, 0x2a, 0x36, 0x39, 0x4d,
0x9c, 0xf1, 0xe8, 0x71, 0xa4, 0xc8, 0xad, 0x82, 0xb4, 0xf6, 0x7d, 0x68, 0x84, 0x47, 0x23, 0xf4,
0x10, 0x0e, 0x89, 0xa3, 0x11, 0x0f, 0xb0, 0xd4, 0x78, 0x5e, 0x92, 0x41, 0x92, 0xff, 0xb6, 0xd7,
0x1c, 0xdb, 0xa7, 0x33, 0x4d, 0xa3, 0x29, 0x3b, 0x5d, 0xb5, 0x99, 0xcb, 0xc4, 0x08, 0xb4, 0x8f,
0x54, 0x28, 0x77, 0x89, 0xaf, 0xdd, 0x8b, 0x96, 0xf3, 0x3c, 0x94, 0xcc, 0xa0, 0x1f, 0x25, 0xf3,
0x0b, 0x2d, 0xe0, 0x2d, 0x68, 0x44, 0x07, 0xeb, 0x24, 0xdf, 0xd5, 0xc9, 0x19, 0x9d, 0xd1, 0x8b,
0x46, 0x50, 0x0f, 0x77, 0xd7, 0x24, 0xeb, 0x0a, 0x94, 0x77, 0xc9, 0xbe, 0x68, 0xe1, 0x99, 0xdc,
0x1d, 0xbb, 0x7d, 0x97, 0xec, 0x63, 0x8a, 0xd0, 0x8e, 0x43, 0xf9, 0x2e, 0xd9, 0xa7, 0x71, 0x91,
0x05, 0x4d, 0x41, 0x29, 0x22, 0xe8, 0x06, 0x54, 0xc5, 0xe1, 0x31, 0x59, 0xdf, 0x25, 0xa8, 0xf6,
0xf9, 0x79, 0x34, 0x67, 0x50, 0x84, 0x99, 0x76, 0x13, 0x9a, 0xf2, 0x91, 0x31, 0xc9, 0x77, 0x12,
0x9a, 0x3d, 0xe9, 0x50, 0xca, 0x7d, 0x45, 0xce, 0xd2, 0x08, 0x1c, 0x8a, 0xdd, 0x29, 0x24, 0x19,
0x6e, 0xc3, 0x21, 0x57, 0xbe, 0xb8, 0x28, 0x25, 0xc4, 0x8b, 0xec, 0x1b, 0x32, 0x11, 0x8e, 0xc1,
0xb4, 0xbb, 0xb0, 0x90, 0x3c, 0x78, 0x26, 0x6b, 0x3a, 0x07, 0x0b, 0xdb, 0x89, 0x63, 0x2e, 0x5f,
0x22, 0xc9, 0x6c, 0x6d, 0x03, 0x2a, 0xfc, 0x4c, 0x95, 0xa4, 0x78, 0x09, 0x2a, 0x3a, 0x3b, 0xa3,
0x95, 0x58, 0x64, 0xd4, 0x52, 0x5b, 0xc9, 0xa0, 0x98, 0x1b, 0x6a, 0x7f, 0x56, 0x40, 0x61, 0x27,
0xd8, 0x24, 0xd5, 0x15, 0x50, 0x7c, 0x7a, 0x1c, 0x2e, 0xe5, 0xdc, 0xff, 0xb0, 0x75, 0xc0, 0xa4,
0x05, 0xb3, 0x47, 0xaf, 0x41, 0xc5, 0xf3, 0xf7, 0xad, 0xe0, 0x6a, 0xe4, 0xd4, 0x74, 0x60, 0x97,
0x9a, 0x62, 0x8e, 0xa0, 0x50, 0xb6, 0x8a, 0xc4, 0xa5, 0x48, 0x0e, 0x94, 0x2d, 0x5f, 0xcc, 0x11,
0xe8, 0x26, 0xd4, 0x7a, 0x3b, 0xa4, 0xb7, 0x4b, 0x0c, 0x71, 0x1b, 0x72, 0x66, 0x3a, 0x78, 0x8d,
0x1b, 0xe3, 0x00, 0x45, 0xeb, 0xee, 0xb1, 0x21, 0xaf, 0xce, 0x52, 0x37, 0x9b, 0x06, 0xcc, 0x11,
0xda, 0xa2, 0x18, 0xc1, 0x74, 0x5f, 0xbf, 0x03, 0x15, 0xd6, 0x49, 0x74, 0x5d, 0x2e, 0x9e, 0x97,
0x4e, 0x3e, 0x99, 0xd1, 0x45, 0x0c, 0x4e, 0xc8, 0xc3, 0x7a, 0x1c, 0xe7, 0x69, 0xce, 0xc2, 0x23,
0x46, 0x8a, 0xf3, 0x9c, 0x80, 0x9a, 0xe8, 0x7c, 0xbc, 0xc1, 0xf5, 0xc0, 0xe0, 0x59, 0xa8, 0x70,
0xff, 0x4c, 0xef, 0xcf, 0xaf, 0x8b, 0x50, 0xa6, 0x87, 0xf1, 0xc9, 0xf8, 0x23, 0xe6, 0x3d, 0xcf,
0x61, 0xd6, 0xcd, 0xbd, 0xd8, 0xb4, 0x6b, 0xb7, 0x83, 0x11, 0x7a, 0x23, 0x3e, 0x42, 0x67, 0xa7,
0xf6, 0x4c, 0xa2, 0xe1, 0x0d, 0xfb, 0x79, 0x05, 0x14, 0x76, 0x8d, 0x92, 0xe6, 0xc9, 0xc1, 0x7e,
0x33, 0xad, 0x61, 0x14, 0xdc, 0x96, 0x84, 0x03, 0xf3, 0x64, 0xdd, 0xcf, 0xf7, 0x64, 0x06, 0xa4,
0x2a, 0x91, 0x75, 0x89, 0x0a, 0xd6, 0x2b, 0xa0, 0x0c, 0xcd, 0x21, 0x11, 0x8e, 0x9c, 0x53, 0xe5,
0x7d, 0x73, 0x48, 0x30, 0xb3, 0xa7, 0xb8, 0x1d, 0xdd, 0xdb, 0x11, 0x3e, 0x9c, 0x83, 0xeb, 0xe8,
0xde, 0x0e, 0x66, 0xf6, 0x14, 0x67, 0xeb, 0x43, 0x22, 0x9c, 0x37, 0x07, 0xf7, 0x40, 0xa7, 0xf5,
0x51, 0x7b, 0x8a, 0xf3, 0xcc, 0x0f, 0x88, 0xb8, 0xac, 0xcb, 0xc1, 0x75, 0xcd, 0x0f, 0x08, 0x66,
0xf6, 0xd4, 0xe5, 0x29, 0x4b, 0x86, 0x8b, 0x3c, 0x0b, 0x95, 0x77, 0x4d, 0xc3, 0xdf, 0x89, 0x17,
0x57, 0x62, 0x2b, 0x82, 0x8e, 0xd2, 0x81, 0x56, 0x84, 0x3c, 0xc8, 0x9c, 0x67, 0x1d, 0x14, 0x3a,
0x5b, 0x07, 0x73, 0x9b, 0x68, 0x92, 0x05, 0xcb, 0x22, 0x28, 0x74, 0x20, 0x33, 0xba, 0xb2, 0x08,
0x0a, 0x9d, 0x9e, 0xec, 0x52, 0x3a, 0x28, 0xf1, 0xd2, 0x72, 0x50, 0xfa, 0x61, 0x19, 0x14, 0x76,
0x25, 0x97, 0x74, 0xc8, 0xb7, 0x61, 0xce, 0xd7, 0xdd, 0x01, 0xe1, 0x52, 0x75, 0x23, 0xb8, 0x80,
0x3e, 0x3f, 0xf5, 0xa2, 0xaf, 0xfd, 0x48, 0x86, 0xe0, 0x38, 0xc3, 0xec, 0x51, 0x97, 0x51, 0xc5,
0xa2, 0xee, 0x1b, 0xe1, 0x96, 0xab, 0xe4, 0x5c, 0xd9, 0x32, 0x2c, 0xdf, 0xb8, 0xc3, 0xfd, 0xf7,
0x0c, 0xcc, 0xc5, 0x1a, 0xf6, 0xa5, 0x46, 0x41, 0xb9, 0xb1, 0x9c, 0x67, 0x25, 0x54, 0x0e, 0x17,
0xe3, 0x61, 0x30, 0x53, 0x28, 0x08, 0xe0, 0xef, 0x2a, 0x50, 0x0f, 0xaf, 0x34, 0x53, 0x54, 0xce,
0xd8, 0xb5, 0x72, 0x55, 0x4e, 0x80, 0x6f, 0x6f, 0xb9, 0x16, 0xa6, 0x08, 0xda, 0x1b, 0xdf, 0xf4,
0xc3, 0x61, 0x7f, 0x3e, 0x1f, 0xfa, 0x88, 0x9a, 0x63, 0x8e, 0x42, 0x0f, 0xa1, 0x69, 0x10, 0xaf,
0xe7, 0x9a, 0x23, 0x76, 0x2f, 0xc2, 0xc7, 0xff, 0x62, 0x3e, 0xc9, 0x7a, 0x04, 0xc2, 0x32, 0x03,
0xda, 0x80, 0x86, 0x39, 0xd4, 0x07, 0xa4, 0x13, 0x05, 0x91, 0xf3, 0xf9, 0x74, 0x1b, 0x01, 0x04,
0x47, 0x68, 0xda, 0xb6, 0xbe, 0xbe, 0x67, 0xf6, 0x1c, 0x9b, 0x91, 0x55, 0x67, 0x6d, 0xdb, 0x9d,
0x08, 0x84, 0x65, 0x06, 0x74, 0x4d, 0x84, 0x61, 0x1e, 0x6b, 0xce, 0xce, 0x30, 0x54, 0x61, 0x28,
0xa6, 0x6a, 0x72, 0xcb, 0xb5, 0xb2, 0xc3, 0x0d, 0x1b, 0xd5, 0x8c, 0xe2, 0x53, 0xd0, 0x94, 0xc6,
0x2b, 0xc3, 0xe8, 0x39, 0x68, 0x84, 0xa3, 0x90, 0xcd, 0x23, 0xf5, 0x2d, 0xc3, 0xe8, 0xba, 0x88,
0x49, 0xaf, 0xc6, 0xdd, 0xfc, 0x44, 0xc2, 0xcd, 0xa9, 0x63, 0x6f, 0xba, 0x84, 0xdd, 0xa4, 0xc9,
0xc1, 0xe8, 0x93, 0x63, 0x6c, 0x0f, 0xb3, 0xbe, 0xe4, 0x83, 0xc3, 0x23, 0x58, 0x58, 0xd7, 0x7d,
0x7d, 0x5b, 0xf7, 0x88, 0xb8, 0x9e, 0x4d, 0x63, 0x75, 0x79, 0x91, 0x38, 0x3c, 0x64, 0xb3, 0x0a,
0xbb, 0x6f, 0x54, 0xfc, 0xff, 0x8e, 0x8a, 0xff, 0x4b, 0x96, 0x8a, 0x5f, 0x89, 0xa9, 0xf8, 0x53,
0x59, 0x9f, 0x19, 0xad, 0x09, 0x19, 0x7f, 0x2d, 0xbe, 0xa1, 0x9c, 0xce, 0x41, 0xc6, 0x76, 0x94,
0x6b, 0x71, 0x1d, 0x9f, 0x87, 0x8d, 0x09, 0xf9, 0x37, 0x93, 0x42, 0xfe, 0x6c, 0x0e, 0x7a, 0x42,
0xc9, 0x5f, 0x8b, 0x2b, 0xf9, 0xbc, 0xda, 0xbf, 0xc6, 0x52, 0xfe, 0x37, 0x19, 0x52, 0xfe, 0xb5,
0xb8, 0x94, 0x9f, 0xe2, 0x35, 0x5f, 0x95, 0x96, 0xff, 0x55, 0x96, 0x96, 0x5f, 0x89, 0x69, 0xf9,
0x29, 0x2d, 0x4b, 0x8a, 0xf9, 0x6b, 0x71, 0x31, 0x7f, 0x3a, 0x07, 0x19, 0x53, 0xf3, 0x2b, 0x31,
0x35, 0x9f, 0x57, 0xa9, 0x24, 0xe7, 0x57, 0x62, 0x72, 0x3e, 0x0f, 0x28, 0xe9, 0xf9, 0x95, 0x98,
0x9e, 0xcf, 0x03, 0x4a, 0x82, 0x7e, 0x25, 0x26, 0xe8, 0xf3, 0x80, 0xdf, 0x28, 0xfa, 0x83, 0x29,
0xfa, 0x9f, 0x65, 0x29, 0x7a, 0x9c, 0xae, 0xe8, 0x2f, 0x64, 0x8f, 0x7f, 0xbe, 0xa4, 0x9f, 0x3d,
0x02, 0x4f, 0x6a, 0xfa, 0xeb, 0x09, 0x4d, 0x7f, 0x26, 0x07, 0xfc, 0x7f, 0x2a, 0xea, 0xff, 0x34,
0x4d, 0xd4, 0x5f, 0x95, 0x45, 0xfd, 0x94, 0x5d, 0x64, 0x52, 0xd5, 0xdf, 0x88, 0xab, 0xfa, 0x73,
0x33, 0x60, 0x63, 0xb2, 0x7e, 0x33, 0x4d, 0xd6, 0xb7, 0x67, 0x60, 0xc9, 0xd4, 0xf5, 0x6f, 0x4d,
0xea, 0xfa, 0x0b, 0x33, 0xf0, 0xa5, 0x0a, 0xfb, 0xcd, 0x34, 0x61, 0x3f, 0x4b, 0xeb, 0x32, 0x95,
0xfd, 0xeb, 0x31, 0x65, 0xff, 0xfc, 0x2c, 0xc3, 0xf5, 0xf5, 0x93, 0xf6, 0x7f, 0xac, 0x41, 0x3d,
0xf8, 0x40, 0xae, 0xfd, 0xb6, 0x08, 0xb5, 0xe0, 0x89, 0x41, 0xd2, 0x6f, 0x8f, 0x42, 0x95, 0xda,
0x84, 0x8f, 0x17, 0x45, 0x0a, 0xdd, 0x00, 0x85, 0xfe, 0x12, 0x4e, 0xf9, 0xe2, 0xf4, 0x0d, 0x55,
0x7e, 0x7f, 0x80, 0x19, 0x8e, 0xf2, 0x3a, 0xfd, 0xbe, 0x47, 0x7c, 0xe6, 0x90, 0x73, 0x58, 0xa4,
0x68, 0x6f, 0x2d, 0x73, 0x68, 0xfa, 0xcc, 0xaf, 0xe6, 0x30, 0x4f, 0x68, 0xaf, 0x00, 0x48, 0x0f,
0x14, 0x66, 0x6c, 0xa3, 0xf6, 0x32, 0x34, 0xa2, 0x87, 0x19, 0x29, 0x20, 0x8f, 0x15, 0x06, 0x20,
0x9e, 0xd2, 0x6e, 0xc1, 0x7c, 0xe2, 0x75, 0x42, 0x8a, 0xfe, 0x0f, 0x3e, 0x57, 0xdc, 0x15, 0x5f,
0x23, 0x1a, 0x58, 0xce, 0xd2, 0x1e, 0x43, 0x53, 0x7e, 0x75, 0x70, 0x60, 0x02, 0x74, 0x05, 0xea,
0x41, 0x52, 0x8c, 0xf0, 0xb4, 0xaf, 0x27, 0xa1, 0xad, 0xf6, 0x23, 0x00, 0xe9, 0xed, 0xd0, 0xac,
0x73, 0x29, 0x9d, 0xb6, 0xca, 0xb3, 0x9d, 0xb6, 0xe8, 0x34, 0xf9, 0x8e, 0xaf, 0x5b, 0x6c, 0x95,
0xcd, 0x61, 0x9e, 0xd0, 0x7e, 0x59, 0x84, 0xb9, 0xf8, 0x03, 0x9f, 0xaf, 0xb0, 0x09, 0x67, 0x61,
0xde, 0x64, 0x95, 0x6c, 0x3a, 0x9e, 0xc9, 0x46, 0x8a, 0xbb, 0x4c, 0x22, 0x57, 0x7b, 0x3f, 0x6c,
0x93, 0xf8, 0x7e, 0xff, 0x05, 0xda, 0xa4, 0xcc, 0x78, 0x08, 0x7d, 0x3b, 0xac, 0xeb, 0x60, 0xae,
0x8a, 0x54, 0x5a, 0xd7, 0xd0, 0xd9, 0x23, 0x06, 0xfb, 0x94, 0xdb, 0xc0, 0x41, 0x52, 0xfb, 0x77,
0x09, 0x94, 0x2d, 0x8f, 0xb8, 0xda, 0xbf, 0x4a, 0xc1, 0x07, 0xf3, 0xab, 0xa0, 0xb0, 0x47, 0x23,
0xd2, 0x07, 0xfd, 0x62, 0xe2, 0x83, 0x7e, 0xec, 0xc9, 0x6e, 0xf4, 0x41, 0xff, 0x2a, 0x28, 0xec,
0x99, 0xc8, 0xc1, 0x91, 0x3f, 0x2e, 0x42, 0x23, 0x7a, 0xb2, 0x71, 0x60, 0xbc, 0xfc, 0xf5, 0xb4,
0x14, 0xff, 0x7a, 0xfa, 0x22, 0x54, 0x5c, 0xf6, 0x9d, 0x93, 0x3b, 0x7a, 0xf2, 0x3b, 0x3d, 0xab,
0x10, 0x73, 0x13, 0x8d, 0x40, 0x53, 0x7e, 0x90, 0x72, 0xf0, 0x66, 0x9c, 0x16, 0xcf, 0x07, 0x37,
0x0c, 0x6f, 0xd5, 0x75, 0xf5, 0x7d, 0x71, 0x7a, 0x8f, 0x67, 0x52, 0xdd, 0xb5, 0x69, 0xda, 0x83,
0xf4, 0x77, 0x14, 0xda, 0x5f, 0x8b, 0x50, 0x13, 0xcf, 0x3b, 0xb4, 0x15, 0x28, 0x3f, 0x20, 0x8f,
0x69, 0x43, 0xc4, 0x03, 0x8f, 0x89, 0x86, 0xdc, 0x67, 0xbd, 0x10, 0xf6, 0x38, 0x30, 0xd3, 0xae,
0x41, 0x55, 0xb8, 0xe3, 0xc1, 0xb1, 0x57, 0x41, 0x61, 0xef, 0x48, 0x0e, 0x8e, 0xfc, 0x69, 0x1d,
0xaa, 0xfc, 0x01, 0x89, 0xf6, 0x79, 0x0d, 0xaa, 0xfc, 0x6d, 0x09, 0xba, 0x01, 0x35, 0x6f, 0x3c,
0x1c, 0xea, 0xee, 0xbe, 0xe0, 0x39, 0x3d, 0xed, 0x29, 0x4a, 0xbb, 0xcb, 0x6d, 0x71, 0x00, 0x42,
0xaf, 0x82, 0xd2, 0xd3, 0xfb, 0x64, 0xe2, 0x1a, 0x23, 0x0d, 0xbc, 0xa6, 0xf7, 0x09, 0x66, 0xe6,
0xe8, 0x4d, 0xa8, 0x8b, 0x69, 0x09, 0x16, 0xfd, 0xf4, 0x7a, 0x83, 0xc9, 0x0c, 0x51, 0xda, 0x5b,
0x50, 0x13, 0x8d, 0x41, 0x37, 0xa1, 0xea, 0xf1, 0xd7, 0x34, 0x49, 0xd5, 0x97, 0xda, 0x85, 0x7d,
0xbb, 0xc7, 0x73, 0xb0, 0x80, 0x69, 0x7f, 0x2b, 0x81, 0x42, 0x1b, 0xf7, 0x5f, 0x33, 0xa1, 0x25,
0x00, 0x4b, 0xf7, 0xfc, 0xcd, 0xb1, 0x65, 0x11, 0xee, 0xed, 0x65, 0x2c, 0xe5, 0xa0, 0x73, 0xb0,
0xc0, 0x53, 0xde, 0x4e, 0x77, 0xdc, 0xeb, 0x11, 0x62, 0x30, 0xd7, 0xaf, 0xe3, 0x64, 0x36, 0x5a,
0x85, 0x0a, 0x7b, 0x66, 0x2d, 0x44, 0xdb, 0xf9, 0xdc, 0x91, 0x6d, 0x6f, 0x9a, 0xb6, 0x68, 0x0d,
0x47, 0x6a, 0x0e, 0x34, 0xc2, 0x3c, 0xba, 0x08, 0x47, 0xa6, 0x6d, 0x9b, 0xf6, 0x40, 0x78, 0x74,
0x90, 0xa4, 0x71, 0x89, 0xfe, 0x14, 0xed, 0xad, 0x60, 0x91, 0xa2, 0xf9, 0x7d, 0xdd, 0xb4, 0x44,
0x13, 0x2b, 0x58, 0xa4, 0x28, 0xd3, 0x98, 0xb9, 0x2f, 0x7f, 0x33, 0x5e, 0xc6, 0x41, 0x52, 0x7b,
0x1a, 0xbd, 0x47, 0x9a, 0x88, 0x7e, 0x48, 0x1c, 0x24, 0x79, 0x04, 0xe0, 0x67, 0xc4, 0x45, 0x59,
0x4c, 0x96, 0xf9, 0xc3, 0xa2, 0x48, 0x1e, 0x52, 0x99, 0x60, 0x5b, 0xa6, 0xcd, 0x8f, 0xbb, 0x75,
0x2c, 0x52, 0x89, 0x31, 0xae, 0x4c, 0x8c, 0xb1, 0x28, 0xbf, 0x6d, 0x98, 0x3e, 0x0b, 0xa9, 0x61,
0x39, 0xcf, 0x41, 0xd7, 0xa1, 0x66, 0x90, 0x3d, 0xb3, 0x47, 0x82, 0x37, 0x2e, 0xa7, 0xa6, 0x8e,
0xed, 0x3a, 0xb3, 0xc5, 0x01, 0x46, 0xf3, 0xa1, 0xca, 0xb3, 0xc2, 0x2e, 0x15, 0xa5, 0x2e, 0x45,
0x8d, 0x2e, 0x4d, 0x69, 0x74, 0x39, 0xa7, 0xd1, 0x4a, 0xb2, 0xd1, 0xcb, 0x77, 0x01, 0x22, 0x77,
0x43, 0x4d, 0xa8, 0x6d, 0xd9, 0xbb, 0xb6, 0xf3, 0xd8, 0x6e, 0x15, 0x68, 0xe2, 0x61, 0xbf, 0x4f,
0x6b, 0x69, 0x15, 0x69, 0x82, 0xda, 0x99, 0xf6, 0xa0, 0x55, 0x42, 0x00, 0x55, 0x9a, 0x20, 0x46,
0xab, 0x4c, 0x7f, 0xdf, 0x61, 0xf3, 0xd7, 0x52, 0x96, 0x75, 0xba, 0x55, 0x79, 0x23, 0xc7, 0xf6,
0xc8, 0x57, 0xf4, 0xef, 0x96, 0xe5, 0x9f, 0x94, 0xa1, 0xc2, 0x02, 0x91, 0xf6, 0x79, 0x29, 0x0c,
0x99, 0x29, 0xd7, 0xb3, 0xd1, 0x25, 0xca, 0xbc, 0xf4, 0x8e, 0x2e, 0x16, 0xc2, 0xe4, 0xcb, 0x93,
0xcb, 0xf2, 0xe5, 0xc9, 0xbc, 0xf4, 0x78, 0x2a, 0x8e, 0x88, 0x5d, 0x9a, 0xbc, 0x0e, 0xf5, 0x91,
0xeb, 0x0c, 0x5c, 0x1a, 0x2b, 0x95, 0xc4, 0x7f, 0x0b, 0xe2, 0xb0, 0x4d, 0x61, 0x86, 0x43, 0x80,
0xf6, 0x00, 0xea, 0x41, 0x6e, 0x24, 0x7c, 0xc4, 0x51, 0x9c, 0x25, 0xa8, 0x17, 0x18, 0x8e, 0x98,
0xef, 0x32, 0x66, 0xbf, 0xe9, 0x12, 0x11, 0xe3, 0x24, 0xdc, 0x3a, 0x48, 0x2e, 0x5f, 0x14, 0xda,
0x7d, 0x0e, 0x1a, 0xeb, 0xae, 0x33, 0x62, 0x8f, 0x8c, 0x5a, 0x05, 0x3a, 0x3b, 0x1b, 0xc3, 0x91,
0xe3, 0xfa, 0xad, 0x22, 0xfd, 0x7d, 0xfb, 0x09, 0xfb, 0x5d, 0x5a, 0x5e, 0x0d, 0xee, 0x33, 0xea,
0xa0, 0x3c, 0x70, 0x6c, 0xc2, 0xa7, 0x1b, 0x8f, 0xd9, 0xca, 0x6d, 0x15, 0x69, 0x36, 0xdd, 0x0e,
0x5a, 0x25, 0x74, 0x08, 0xea, 0x6b, 0xba, 0xdd, 0x23, 0x16, 0x9b, 0xed, 0x06, 0x54, 0x6e, 0xbb,
0xae, 0xe3, 0xb6, 0x94, 0x17, 0x7f, 0x5f, 0x84, 0xf9, 0xf8, 0x73, 0x34, 0x8a, 0xda, 0xd4, 0x07,
0x94, 0xac, 0x0e, 0x4a, 0xc7, 0x19, 0x52, 0xc7, 0x59, 0x80, 0xe6, 0x26, 0x7f, 0xd3, 0xc7, 0x8a,
0x4a, 0xb4, 0x9e, 0x55, 0xb7, 0xb7, 0x63, 0xee, 0x91, 0x56, 0x99, 0x96, 0xde, 0xa2, 0xab, 0xa1,
0xe7, 0x8e, 0x87, 0xdb, 0x5e, 0x4b, 0x41, 0x35, 0xf6, 0xac, 0xa8, 0x55, 0x41, 0xf3, 0x00, 0xd1,
0xe3, 0xab, 0x56, 0x95, 0x32, 0xd2, 0x4e, 0xb5, 0x6a, 0xe8, 0x30, 0x2c, 0xdc, 0xd7, 0xdd, 0x5d,
0xe2, 0x8f, 0x2c, 0xbd, 0x47, 0x58, 0x71, 0x1d, 0x1d, 0x83, 0xc3, 0x52, 0x66, 0x20, 0x63, 0x5b,
0x8d, 0x5b, 0x8b, 0x7f, 0x7f, 0xba, 0x54, 0xfc, 0xf8, 0xe9, 0x52, 0xf1, 0xd3, 0xa7, 0x4b, 0xc5,
0x5f, 0x7c, 0xb6, 0x54, 0xf8, 0xf8, 0xb3, 0xa5, 0xc2, 0x27, 0x9f, 0x2d, 0x15, 0xde, 0x2b, 0x8d,
0xb6, 0xb7, 0xab, 0x4c, 0x76, 0xbd, 0xfc, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x50, 0xbc, 0x87,
0x0b, 0x21, 0x36, 0x00, 0x00,
}
func (m *Event) Marshal() (dAtA []byte, err error) {
@ -7761,6 +7837,29 @@ func (m *EventMessageValueOfUserBlockTextRange) MarshalToSizedBuffer(dAtA []byte
}
return len(dAtA) - i, nil
}
func (m *EventMessageValueOfBlockDataviewSourceSet) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *EventMessageValueOfBlockDataviewSourceSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.BlockDataviewSourceSet != nil {
{
size, err := m.BlockDataviewSourceSet.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintEvents(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x2
i--
dAtA[i] = 0x9a
}
return len(dAtA) - i, nil
}
func (m *EventMessageValueOfPing) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
@ -8342,20 +8441,20 @@ func (m *EventBlockMarksInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
var l int
_ = l
if len(m.MarksInRange) > 0 {
dAtA42 := make([]byte, len(m.MarksInRange)*10)
var j41 int
dAtA43 := make([]byte, len(m.MarksInRange)*10)
var j42 int
for _, num := range m.MarksInRange {
for num >= 1<<7 {
dAtA42[j41] = uint8(uint64(num)&0x7f | 0x80)
dAtA43[j42] = uint8(uint64(num)&0x7f | 0x80)
num >>= 7
j41++
j42++
}
dAtA42[j41] = uint8(num)
j41++
dAtA43[j42] = uint8(num)
j42++
}
i -= j41
copy(dAtA[i:], dAtA42[:j41])
i = encodeVarintEvents(dAtA, i, uint64(j41))
i -= j42
copy(dAtA[i:], dAtA43[:j42])
i = encodeVarintEvents(dAtA, i, uint64(j42))
i--
dAtA[i] = 0xa
}
@ -11279,6 +11378,43 @@ func (m *EventBlockDataviewViewDelete) MarshalToSizedBuffer(dAtA []byte) (int, e
return len(dAtA) - i, nil
}
func (m *EventBlockDataviewSourceSet) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *EventBlockDataviewSourceSet) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *EventBlockDataviewSourceSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Source) > 0 {
i -= len(m.Source)
copy(dAtA[i:], m.Source)
i = encodeVarintEvents(dAtA, i, uint64(len(m.Source)))
i--
dAtA[i] = 0x12
}
if len(m.Id) > 0 {
i -= len(m.Id)
copy(dAtA[i:], m.Id)
i = encodeVarintEvents(dAtA, i, uint64(len(m.Id)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *EventBlockDataviewRelationDelete) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -12859,6 +12995,18 @@ func (m *EventMessageValueOfUserBlockTextRange) Size() (n int) {
}
return n
}
func (m *EventMessageValueOfBlockDataviewSourceSet) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.BlockDataviewSourceSet != nil {
l = m.BlockDataviewSourceSet.Size()
n += 2 + l + sovEvents(uint64(l))
}
return n
}
func (m *EventMessageValueOfPing) Size() (n int) {
if m == nil {
return 0
@ -14326,6 +14474,23 @@ func (m *EventBlockDataviewViewDelete) Size() (n int) {
return n
}
func (m *EventBlockDataviewSourceSet) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Id)
if l > 0 {
n += 1 + l + sovEvents(uint64(l))
}
l = len(m.Source)
if l > 0 {
n += 1 + l + sovEvents(uint64(l))
}
return n
}
func (m *EventBlockDataviewRelationDelete) Size() (n int) {
if m == nil {
return 0
@ -16093,6 +16258,41 @@ func (m *EventMessage) Unmarshal(dAtA []byte) error {
}
m.Value = &EventMessageValueOfUserBlockTextRange{v}
iNdEx = postIndex
case 35:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockDataviewSourceSet", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvents
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthEvents
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthEvents
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := &EventBlockDataviewSourceSet{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Value = &EventMessageValueOfBlockDataviewSourceSet{v}
iNdEx = postIndex
case 100:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Ping", wireType)
@ -25521,6 +25721,120 @@ func (m *EventBlockDataviewViewDelete) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *EventBlockDataviewSourceSet) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvents
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: SourceSet: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: SourceSet: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvents
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthEvents
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthEvents
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Id = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEvents
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthEvents
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthEvents
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Source = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipEvents(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthEvents
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *EventBlockDataviewRelationDelete) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View file

@ -2399,6 +2399,8 @@ message Rpc {
string homeBlockId = 2; // home dashboard block id
string archiveBlockId = 3; // archive block id
string profileBlockId = 4; // profile block id
string marketplaceTypeId = 5; // marketplace type id
string marketplaceRelationId = 6; // marketplace relation id
string gatewayUrl = 101; // gateway url for fetching static files
message Error {
Code code = 1;

View file

@ -45,6 +45,7 @@ message Event {
Block.Dataview.RecordsInsert blockDataviewRecordsInsert = 27;
Block.Dataview.RecordsDelete blockDataviewRecordsDelete = 28;
Block.Dataview.SourceSet blockDataviewSourceSet = 35;
Block.Dataview.ViewSet blockDataviewViewSet = 19;
Block.Dataview.ViewDelete blockDataviewViewDelete = 20;
Block.Dataview.RelationDelete blockDataviewRelationDelete = 24;
@ -513,6 +514,11 @@ message Event {
string viewId = 2; // view id to remove
}
message SourceSet {
string id = 1; // dataview block's id
string source = 2;
}
message RelationDelete {
string id = 1; // dataview block's id
string relationKey = 2; // relation key to remove
@ -684,6 +690,9 @@ enum SmartBlockType {
ObjectType = 6; // have relations list
File = 7;
MarketplaceType = 8;
MarketplaceRelation = 9;
}
message ResponseEvent {

View file

@ -12,7 +12,7 @@ import (
const (
relPbPkg = "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
typePrefix = "https://anytype.io/schemas/object/bundled/"
typePrefix = "_ot"
)
type Relation struct {

View file

@ -2,6 +2,8 @@ package bundle
import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
types2 "github.com/gogo/protobuf/types"
"strings"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
@ -81,6 +83,17 @@ func GetRelation(rk RelationKey) (*relation.Relation, error) {
return nil, ErrNotFound
}
// MustGetLayout returns built-in layout by predefined Layout constant
// PANICS IN CASE RELATION KEY IS NOT EXISTS DO NOT USE WITH ARBITRARY STRING
func MustGetLayout(lk relation.ObjectTypeLayout) *relation.Layout {
if v, exists := Layouts[lk]; exists {
return pbtypes.CopyLayout(&v)
}
// we can safely panic in case RelationKey is a generated constant
panic(ErrNotFound)
}
func ListRelations() []*relation.Relation {
var rels []*relation.Relation
for _, rel := range relations {
@ -105,6 +118,12 @@ func HasRelation(key string) bool {
return exists
}
func HasObjectType(key string) bool {
_, exists := types[TypeKey(key)]
return exists
}
func EqualWithRelation(key string, rel *relation.Relation) (equal bool, exists bool) {
v, exists := relations[RelationKey(key)]
if !exists {
@ -122,3 +141,39 @@ func ListTypes() ([]*relation.ObjectType, error) {
return otypes, nil
}
func ListTypesKeys() []TypeKey {
var keys []TypeKey
for k, _ := range types {
keys = append(keys, k)
}
return keys
}
func GetDetailsForRelation(bundled bool, rel *relation.Relation) ([]*relation.Relation, *types2.Struct) {
var prefix string
if bundled {
prefix = addr.BundledRelationURLPrefix
} else {
prefix = addr.CustomRelationURLPrefix
}
d := &types2.Struct{Fields: map[string]*types2.Value{
RelationKeyName.String(): pbtypes.String(rel.Name),
RelationKeyDescription.String(): pbtypes.String(rel.Description),
RelationKeyId.String(): pbtypes.String(prefix + rel.Key),
RelationKeyType.String(): pbtypes.StringList([]string{TypeKeyRelation.URL()}),
RelationKeyCreator.String(): pbtypes.String(rel.Creator),
RelationKeyLayout.String(): pbtypes.Float64(float64(relation.ObjectType_relation)),
RelationKeyRelationFormat.String(): pbtypes.Float64(float64(rel.Format)),
RelationKeyIsHidden.String(): pbtypes.Bool(rel.Hidden),
RelationKeyMpAddedToLibrary.String(): pbtypes.Bool(true), // temp
}}
var rels []*relation.Relation
for k := range d.Fields {
rels = append(rels, MustGetRelation(RelationKey(k)))
}
return rels, d
}

View file

@ -8,12 +8,6 @@ import "github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/relation"
var (
Layouts = map[relation.ObjectTypeLayout]relation.Layout{
relation.ObjectType_action: {
Id: relation.ObjectType_action,
Name: "Action",
RequiredRelations: []*relation.Relation{relations[RelationKeyDone], relations[RelationKeyName]},
},
relation.ObjectType_basic: {
Id: relation.ObjectType_basic,
@ -35,6 +29,12 @@ var (
Id: relation.ObjectType_file,
Name: "File",
},
relation.ObjectType_image: {
Id: relation.ObjectType_image,
Name: "Image",
RequiredRelations: []*relation.Relation{relations[RelationKeyIconImage]},
},
relation.ObjectType_objectType: {
Id: relation.ObjectType_objectType,
@ -58,5 +58,11 @@ var (
Name: "Set",
RequiredRelations: []*relation.Relation{relations[RelationKeySetOf], relations[RelationKeyName]},
},
relation.ObjectType_todo: {
Id: relation.ObjectType_todo,
Name: "to-do",
RequiredRelations: []*relation.Relation{relations[RelationKeyDone], relations[RelationKeyName]},
},
}
)

View file

@ -31,13 +31,20 @@
"name": "Dashboard"
},
{
"id": "action",
"name": "Action",
"id": "todo",
"name": "to-do",
"requiredRelations": [
"done",
"name"
]
},
{
"id": "image",
"name": "Image",
"requiredRelations": [
"iconImage"
]
},
{
"id": "profile",
"name": "Profile",

View file

@ -30,6 +30,7 @@ const (
RelationKeyAudioAlbum RelationKey = "audioAlbum"
RelationKeyStatus RelationKey = "status"
RelationKeyDurationInSeconds RelationKey = "durationInSeconds"
RelationKeyIsHidden RelationKey = "isHidden"
RelationKeyAperture RelationKey = "aperture"
RelationKeyLastModifiedDate RelationKey = "lastModifiedDate"
RelationKeyRecommendedRelations RelationKey = "recommendedRelations"
@ -46,12 +47,14 @@ const (
RelationKeyAddedDate RelationKey = "addedDate"
RelationKeyAssignee RelationKey = "assignee"
RelationKeyExposure RelationKey = "exposure"
RelationKeyTemplateType RelationKey = "templateType"
RelationKeyAudioGenre RelationKey = "audioGenre"
RelationKeyName RelationKey = "name"
RelationKeyFocalRatio RelationKey = "focalRatio"
RelationKeyPriority RelationKey = "priority"
RelationKeyFileMimeType RelationKey = "fileMimeType"
RelationKeyType RelationKey = "type"
RelationKeyRelationFormat RelationKey = "relationFormat"
RelationKeyLayout RelationKey = "layout"
RelationKeyAudioAlbumTrackNumber RelationKey = "audioAlbumTrackNumber"
RelationKeyPlaceOfBirth RelationKey = "placeOfBirth"
@ -314,6 +317,7 @@ var (
DataSource: relation.Relation_details,
Description: "Done checkbox used to render action layout. ",
Format: relation.RelationFormat_checkbox,
Hidden: true,
Key: "done",
MaxCount: 1,
Name: "Done",
@ -428,6 +432,7 @@ var (
Format: relation.RelationFormat_emoji,
Hidden: true,
Key: "iconEmoji",
MaxCount: 1,
Name: "Emoji",
ReadOnly: false,
Scope: relation.Relation_type,
@ -452,10 +457,23 @@ var (
Format: relation.RelationFormat_object,
Hidden: true,
Key: "id",
MaxCount: 1,
Name: "Anytype ID",
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyIsHidden: {
DataSource: relation.Relation_details,
Description: "Specify if object is hidden",
Format: relation.RelationFormat_checkbox,
Hidden: true,
Key: "isHidden",
MaxCount: 1,
Name: "Hidden",
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyLastModifiedBy: {
DataSource: relation.Relation_derived,
@ -531,6 +549,7 @@ var (
Format: relation.RelationFormat_checkbox,
Hidden: true,
Key: "mpAddedToLibrary",
MaxCount: 1,
Name: "Added to library",
ReadOnly: false,
Scope: relation.Relation_type,
@ -562,7 +581,6 @@ var (
DataSource: relation.Relation_details,
Description: "Used to order tasks in list/canban",
Format: relation.RelationFormat_number,
Hidden: true,
Key: "priority",
MaxCount: 1,
Name: "Priority",
@ -586,12 +604,25 @@ var (
DataSource: relation.Relation_details,
Description: "List of recommended relations",
Format: relation.RelationFormat_object,
Hidden: true,
Key: "recommendedRelations",
Name: "Recommended relations",
ObjectTypes: []string{TypePrefix + "relation"},
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyRelationFormat: {
DataSource: relation.Relation_details,
Description: "Type of the underlying value",
Format: relation.RelationFormat_number,
Hidden: true,
Key: "relationFormat",
MaxCount: 1,
Name: "Relation Format",
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyReleasedYear: {
DataSource: relation.Relation_details,
@ -646,6 +677,19 @@ var (
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyTemplateType: {
DataSource: relation.Relation_details,
Description: "Type that is used for templating",
Format: relation.RelationFormat_object,
Hidden: true,
Key: "templateType",
MaxCount: 1,
Name: "Template's Type",
ObjectTypes: []string{TypePrefix + "objectType"},
ReadOnly: false,
Scope: relation.Relation_type,
},
RelationKeyThumbnailImage: {
DataSource: relation.Relation_details,

View file

@ -51,7 +51,7 @@
{
"description": "Done checkbox used to render action layout. ",
"format": "checkbox",
"hidden": false,
"hidden": true,
"key": "done",
"maxCount": 1,
"name": "Done",
@ -180,6 +180,16 @@
"readonly": false,
"source": "details"
},
{
"description": "Specify if object is hidden",
"format": "checkbox",
"hidden": true,
"key": "isHidden",
"maxCount": 1,
"name": "Hidden",
"readonly": false,
"source": "details"
},
{
"format": "shorttext",
"hidden": false,
@ -202,7 +212,7 @@
{
"description": "List of recommended relations",
"format": "object",
"hidden": false,
"hidden": true,
"key": "recommendedRelations",
"maxCount": 0,
"name": "Recommended relations",
@ -269,7 +279,7 @@
"format": "emoji",
"hidden": true,
"key": "iconEmoji",
"maxCount": 0,
"maxCount": 1,
"name": "Emoji",
"readonly": false,
"source": "details"
@ -349,6 +359,19 @@
"readonly": false,
"source": "details"
},
{
"description": "Type that is used for templating",
"format": "object",
"hidden": true,
"key": "templateType",
"maxCount": 1,
"name": "Template's Type",
"objectTypes": [
"objectType"
],
"readonly": false,
"source": "details"
},
{
"description": "Audio record's genre name",
"format": "shorttext",
@ -381,7 +404,7 @@
{
"description": "Used to order tasks in list/canban",
"format": "number",
"hidden": true,
"hidden": false,
"key": "priority",
"maxCount": 1,
"name": "Priority",
@ -410,6 +433,16 @@
"readonly": false,
"source": "derived"
},
{
"description": "Type of the underlying value",
"format": "number",
"hidden": true,
"key": "relationFormat",
"maxCount": 1,
"name": "Relation Format",
"readonly": false,
"source": "details"
},
{
"description": "Anytype layout ID(from pb enum)",
"format": "number",
@ -472,7 +505,7 @@
"format": "object",
"hidden": true,
"key": "id",
"maxCount": 0,
"maxCount": 1,
"name": "Anytype ID",
"readonly": false,
"source": "derived"
@ -555,7 +588,7 @@
"format": "checkbox",
"hidden": true,
"key": "mpAddedToLibrary",
"maxCount": 0,
"maxCount": 1,
"name": "Added to library",
"readonly": false,
"source": "account"

View file

@ -16,7 +16,7 @@ func (tk TypeKey) URL() string {
}
const (
TypePrefix = "https://anytype.io/schemas/object/bundled/"
TypePrefix = "_ot"
)
const (
TypeKeyNote TypeKey = "note"
@ -27,6 +27,7 @@ const (
TypeKeyVideo TypeKey = "video"
TypeKeyDashboard TypeKey = "dashboard"
TypeKeyObjectType TypeKey = "objectType"
TypeKeyTemplate TypeKey = "template"
TypeKeySet TypeKey = "set"
TypeKeyPage TypeKey = "page"
TypeKeyImage TypeKey = "image"
@ -42,6 +43,7 @@ var (
TypeKeyAudio: {
Description: "",
Hidden: true,
IconEmoji: "🎵",
Layout: relation.ObjectType_basic,
Name: "Audio",
@ -51,6 +53,7 @@ var (
TypeKeyContact: {
Description: "",
IconEmoji: "📇",
Layout: relation.ObjectType_profile,
Name: "Contact",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType]},
@ -77,6 +80,7 @@ var (
TypeKeyFile: {
Description: "",
IconEmoji: "🗂️",
Layout: relation.ObjectType_basic,
Name: "File",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeyFileMimeType], relations[RelationKeySizeInBytes], relations[RelationKeyAddedDate], relations[RelationKeyFileExt]},
@ -95,7 +99,7 @@ var (
Description: "",
IconEmoji: "🌅",
Layout: relation.ObjectType_basic,
Layout: relation.ObjectType_image,
Name: "Image",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeyFileMimeType], relations[RelationKeyWidthInPixels], relations[RelationKeyCamera], relations[RelationKeyHeightInPixels], relations[RelationKeySizeInBytes], relations[RelationKeyCameraIso], relations[RelationKeyAperture], relations[RelationKeyExposure], relations[RelationKeyAddedDate], relations[RelationKeyFocalRatio], relations[RelationKeyFileExt]},
Url: TypePrefix + "image",
@ -115,12 +119,13 @@ var (
Hidden: true,
Layout: relation.ObjectType_objectType,
Name: "Type",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeyRecommendedRelations], relations[RelationKeyRecommendedLayout], relations[RelationKeyMpAddedToLibrary]},
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeyRecommendedRelations], relations[RelationKeyRecommendedLayout], relations[RelationKeyMpAddedToLibrary], relations[RelationKeyIsHidden]},
Url: TypePrefix + "objectType",
},
TypeKeyPage: {
Description: "Base type to start with",
IconEmoji: "📄",
Layout: relation.ObjectType_basic,
Name: "Draft",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType]},
@ -150,25 +155,36 @@ var (
Hidden: true,
Layout: relation.ObjectType_relation,
Name: "Relation",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyLayout], relations[RelationKeyDescription], relations[RelationKeyMpAddedToLibrary]},
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyLayout], relations[RelationKeyDescription], relations[RelationKeyCreator], relations[RelationKeyMpAddedToLibrary], relations[RelationKeyRelationFormat], relations[RelationKeyIsHidden]},
Url: TypePrefix + "relation",
},
TypeKeySet: {
Description: "",
Hidden: true,
Layout: relation.ObjectType_set,
Name: "All sets",
Name: "Set",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeySetOf]},
Url: TypePrefix + "set",
},
TypeKeyTask: {
Description: "",
Layout: relation.ObjectType_action,
IconEmoji: "✔️",
Layout: relation.ObjectType_todo,
Name: "Task",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyDescription], relations[RelationKeyType], relations[RelationKeyCreator], relations[RelationKeyCreatedDate], relations[RelationKeyLayout], relations[RelationKeyLastModifiedBy], relations[RelationKeyIconImage], relations[RelationKeyIconEmoji], relations[RelationKeyCoverId], relations[RelationKeyLastModifiedDate], relations[RelationKeyLastOpenedDate], relations[RelationKeyCoverX], relations[RelationKeyCoverY], relations[RelationKeyCoverScale], relations[RelationKeyToBeDeletedDate], relations[RelationKeyFeaturedRelations], relations[RelationKeyCoverType], relations[RelationKeyAssignee], relations[RelationKeyDueDate], relations[RelationKeyAttachments], relations[RelationKeyStatus], relations[RelationKeyDone], relations[RelationKeyPriority], relations[RelationKeyLinkedTasks], relations[RelationKeyLinkedProjects], relations[RelationKeyTag]},
Url: TypePrefix + "task",
},
TypeKeyTemplate: {
Description: "Special type to create objects from",
Hidden: true,
Layout: relation.ObjectType_basic,
Name: "Template",
Relations: []*relation.Relation{relations[RelationKeyId], relations[RelationKeyName], relations[RelationKeyLayout], relations[RelationKeyDescription], relations[RelationKeyTemplateType]},
Url: TypePrefix + "template",
},
TypeKeyVideo: {
Description: "",

View file

@ -27,6 +27,7 @@
]
},
{
"emoji": "📇",
"id": "contact",
"layout": "profile",
"name": "Contact",
@ -81,8 +82,9 @@
]
},
{
"emoji": "✔️",
"id": "task",
"layout": "action",
"layout": "todo",
"name": "Task",
"relations": [
"id",
@ -125,7 +127,10 @@
"name",
"layout",
"description",
"mpAddedToLibrary"
"creator",
"mpAddedToLibrary",
"relationFormat",
"isHidden"
]
},
{
@ -223,13 +228,29 @@
"coverType",
"recommendedRelations",
"recommendedLayout",
"mpAddedToLibrary"
"mpAddedToLibrary",
"isHidden"
]
},
{
"description": "Special type to create objects from",
"hidden": true,
"id": "template",
"layout": "basic",
"name": "Template",
"relations": [
"id",
"name",
"layout",
"description",
"templateType"
]
},
{
"hidden": true,
"id": "set",
"layout": "set",
"name": "All sets",
"name": "Set",
"relations": [
"id",
"name",
@ -255,6 +276,7 @@
},
{
"description": "Base type to start with",
"emoji": "📄",
"id": "page",
"layout": "basic",
"name": "Draft",
@ -283,7 +305,7 @@
{
"emoji": "🌅",
"id": "image",
"layout": "basic",
"layout": "image",
"name": "Image",
"relations": [
"id",
@ -347,6 +369,7 @@
},
{
"emoji": "🎵",
"hidden": true,
"id": "audio",
"layout": "basic",
"name": "Audio",
@ -412,6 +435,7 @@
]
},
{
"emoji": "🗂️",
"id": "file",
"layout": "basic",
"name": "File",

View file

@ -36,6 +36,8 @@ type Config struct {
CafeP2PAddr string `json:"cafe_p2p_addr,omitempty" envconfig:"cafe_p2p_addr"`
CafeGRPCAddr string `json:"cafe_grpc_addr,omitempty" envconfig:"cafe_grpc_addr"`
WebGatewayBaseUrl string `json:"web_gateway_base_url,omitempty" envconfig:"web_gateway_base_url"`
DisablePubsub bool `json:"disable_pubsub,omitempty" envconfig:"disable_pubsub"`
DisableDirectConnection bool `json:"disable_direct_connection,omitempty" envconfig:"disable_direct_connection"`
}
var mu = sync.Mutex{}

View file

@ -3,6 +3,8 @@ package core
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"github.com/libp2p/go-tcp-transport"
"io"
"path/filepath"
"strings"
@ -22,6 +24,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/net/litenet"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pin"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/libp2p/go-libp2p-core/peer"
pstore "github.com/libp2p/go-libp2p-core/peerstore"
"github.com/libp2p/go-libp2p/p2p/discovery"
@ -29,6 +32,7 @@ import (
tcn "github.com/textileio/go-threads/core/net"
"github.com/textileio/go-threads/core/thread"
tnet "github.com/textileio/go-threads/net"
tq "github.com/textileio/go-threads/net/queue"
"github.com/textileio/go-threads/util"
"google.golang.org/grpc"
)
@ -48,8 +52,6 @@ const (
CName = "anytype"
DefaultWebGatewaySnapshotURI = "/%s/snapshotId/%s#key=%s"
pullInterval = 3 * time.Minute
)
var BootstrapNodes = []string{
@ -70,6 +72,7 @@ type PredefinedBlockIds struct {
type Service interface {
Account() string
Device() string
CafePeer() ma.Multiaddr
Start() error
Stop() error
@ -105,7 +108,7 @@ type Service interface {
SyncStatus() tcn.SyncInfo
FileStatus() pin.FilePinService
SubscribeForNewRecords() (ch chan SmartblockRecordWithThreadID, err error)
SubscribeForNewRecords(ctx context.Context) (ch chan SmartblockRecordWithThreadID, err error)
ProfileInfo
@ -191,6 +194,14 @@ func (a *Anytype) Account() string {
return a.opts.Account.Address()
}
func (a *Anytype) CafePeer() ma.Multiaddr {
if a.opts.CafeP2PAddr == nil {
return nil
}
return a.opts.CafeP2PAddr
}
func (a *Anytype) Device() string {
if a.opts.Device == nil {
return ""
@ -454,6 +465,18 @@ func (a *Anytype) InitNewSmartblocksChan(ch chan<- string) error {
}
func (a *Anytype) startNetwork(useHostAddr bool) (net.NetBoostrapper, error) {
var (
unaryServerInterceptor grpc.UnaryServerInterceptor
unaryClientInterceptor grpc.UnaryClientInterceptor
)
if metrics.Enabled {
unaryServerInterceptor = grpc_prometheus.UnaryServerInterceptor
unaryClientInterceptor = grpc_prometheus.UnaryClientInterceptor
grpc_prometheus.EnableHandlingTimeHistogram()
grpc_prometheus.EnableClientHandlingTimeHistogram()
}
var opts = []litenet.NetOption{
litenet.WithInMemoryDS(a.opts.InMemoryDS),
litenet.WithOffline(a.opts.Offline),
@ -461,19 +484,22 @@ func (a *Anytype) startNetwork(useHostAddr bool) (net.NetBoostrapper, error) {
litenet.WithNetPubSub(a.opts.NetPubSub),
litenet.WithNetSyncTracking(a.opts.SyncTracking),
litenet.WithNetGRPCServerOptions(
grpc.MaxRecvMsgSize(5 << 20), // 5Mb max message size
grpc.MaxRecvMsgSize(5<<20), // 5Mb max message size
grpc.UnaryInterceptor(unaryServerInterceptor),
),
litenet.WithNetGRPCDialOptions(
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(5<<20),
grpc.MaxCallSendMsgSize(5<<20),
),
// gRPC metrics
//grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor),
//grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor),
grpc.WithUnaryInterceptor(unaryClientInterceptor),
),
}
if a.opts.CafeP2PAddr != nil {
opts = append(opts, litenet.WithPermanentConnection(a.opts.CafeP2PAddr))
}
if useHostAddr {
opts = append(opts, litenet.WithNetHostAddr(a.opts.HostAddr))
}
@ -481,8 +507,8 @@ func (a *Anytype) startNetwork(useHostAddr bool) (net.NetBoostrapper, error) {
return litenet.DefaultNetwork(a.opts.Repo, a.opts.Device, []byte(ipfsPrivateNetworkKey), opts...)
}
func (a *Anytype) SubscribeForNewRecords() (ch chan SmartblockRecordWithThreadID, err error) {
ctx, cancel := context.WithCancel(context.Background())
func (a *Anytype) SubscribeForNewRecords(ctx context.Context) (ch chan SmartblockRecordWithThreadID, err error) {
ctx, cancel := context.WithCancel(ctx)
ch = make(chan SmartblockRecordWithThreadID)
threadsCh, err := a.t.Subscribe(ctx)
if err != nil {
@ -542,14 +568,33 @@ func (a *Anytype) SubscribeForNewRecords() (ch chan SmartblockRecordWithThreadID
}
func init() {
// redefine thread pulling interval
tnet.PullInterval = pullInterval
/* adjust ThreadsDB parameters */
// redefine timeouts for threads
tnet.DialTimeout = 10 * time.Second
// thread pulling cycle
tnet.PullStartAfter = 5 * time.Second
tnet.InitialPullInterval = 20 * time.Second
tnet.PullInterval = 3 * time.Minute
// communication timeouts
tnet.DialTimeout = 20 * time.Second // we can set safely set a long dial timeout because unavailable peer are cached for some time and local network timeouts are overridden with 5s
tcp.DefaultConnectTimeout = tnet.DialTimeout // override default tcp dial timeout because it has a priority over the passing context's deadline
tnet.PushTimeout = 30 * time.Second
tnet.PullTimeout = 2 * time.Minute
// event bus input buffer
tnet.EventBusCapacity = 3
// exchange edges
tnet.MaxThreadsExchanged = 10
tnet.ExchangeCompressionTimeout = 20 * time.Second
tnet.QueuePollInterval = 1 * time.Second
// thread packer queue
tq.InBufSize = 5
tq.OutBufSize = 2
/* logs */
// apply log levels in go-threads and go-ipfs deps
logging.ApplyLevelsFromEnv()
}

View file

@ -122,8 +122,9 @@ func (i *image) Exif() (*mill.ImageExifSchema, error) {
func (i *image) Details() (*types.Struct, error) {
details := &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String(i.hash),
"type": pbtypes.StringList([]string{bundle.TypeKeyImage.URL()}),
"id": pbtypes.String(i.hash),
"type": pbtypes.StringList([]string{bundle.TypeKeyImage.URL()}),
"layout": pbtypes.Float64(float64(relation.ObjectType_image)),
},
}

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"io/ioutil"
"os"
"path/filepath"
@ -11,7 +12,6 @@ import (
"strings"
"time"
"github.com/anytypeio/go-anytype-middleware/core/block/database/objects"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/threads"
@ -55,7 +55,14 @@ var migrations = []migration{
skipMigration, // 14
skipMigration, // 15
skipMigration, // 16
reindexAll, // 17
skipMigration, // 17
skipMigration, // 18
skipMigration, // 19
skipMigration, // 20
skipMigration, // 21
skipMigration, // 22
skipMigration, // 23
reindexAll, // 24
}
func (a *Anytype) getRepoVersion() (int, error) {
@ -430,72 +437,166 @@ func removeBundleRelationsFromDs(a *Anytype, lastMigration bool) error {
})
}
func ReindexAll(a *Anytype) (int, error) {
ids, err := a.localStore.Objects.ListIds()
if err != nil {
return 0, err
}
total := len(ids)
var migrated int
for _, id := range ids {
sbt, err := smartblock.SmartBlockTypeFromID(id)
if err != nil {
return 0, fmt.Errorf("migration reindexAll:failed to extract smartblock type: %w", err)
}
if sbt == smartblock.SmartBlockTypeArchive {
// remove archive we have accidentally indexed
err = a.localStore.Objects.DeleteObject(id)
if err != nil {
log.Errorf("migration reindexAll: failed to delete archive from index: %s", err.Error())
}
total--
continue
}
for _, idx := range a.localStore.Objects.Indexes() {
//if idx.Name == "objtype_relkey_setid" {
// skip it because we can't reindex relations in sets for now
// continue
//}
err = localstore.EraseIndex(idx, a.t.Datastore().(ds.TxnDatastore))
if err != nil {
log.Errorf("migration reindexAll: failed to delete archive from index: %s", err.Error())
}
}
oi, err := a.localStore.Objects.GetByIDs(id)
if err != nil {
log.Errorf("migration reindexAll: failed to get objects by id: %s", err.Error())
continue
}
if len(oi) < 1 {
log.Errorf("migration reindexAll: failed to get objects: not found")
continue
}
o := oi[0]
var objType string
if pbtypes.HasField(o.Details, bundle.RelationKeyType.String()) {
objTypes := pbtypes.GetStringList(o.Details, bundle.RelationKeyType.String())
if len(objTypes) > 0 {
objType = objTypes[0]
}
}
if strings.HasPrefix(objType, addr.OldCustomObjectTypeURLPrefix) {
objType = strings.TrimPrefix(objType, addr.OldCustomObjectTypeURLPrefix)
} else if strings.HasPrefix(objType, addr.OldBundledObjectTypeURLPrefix) {
objType = addr.BundledObjectTypeURLPrefix + strings.TrimPrefix(objType, addr.OldBundledObjectTypeURLPrefix)
} else if bundle.HasObjectType(objType) {
objType = addr.BundledObjectTypeURLPrefix + objType
}
if sbt == smartblock.SmartBlockTypeIndexedRelation {
err = a.localStore.Objects.DeleteObject(id)
if err != nil {
log.Errorf("deletion of indexed relation failed: %s", err.Error())
}
// will be reindexed below
continue
}
o.Details.Fields[bundle.RelationKeyType.String()] = pbtypes.String(objType)
err = a.localStore.Objects.CreateObject(id, o.Details, o.Relations, nil, o.Snippet)
if err != nil {
log.Errorf("migration reindexAll: createObject failed: %s", err.Error())
continue
}
migrated++
}
relations, _ := a.localStore.Objects.ListRelations("")
for _, rel := range relations {
if bundle.HasRelation(rel.Key) {
rel.Creator = a.ProfileID()
} else {
rel.Creator = addr.AnytypeProfileId
}
}
var indexedRelations int
var divided [][]*pbrelation.Relation
chunkSize := 30
for i := 0; i < len(relations); i += chunkSize {
end := i + chunkSize
if end > len(relations) {
end = len(relations)
}
divided = append(divided, relations[i:end])
}
for _, chunk := range divided {
err = a.localStore.Objects.StoreRelations(chunk)
if err != nil {
log.Errorf("reindex relations failed: %s", err.Error())
} else {
indexedRelations += len(chunk)
}
}
log.Debugf("%d relations reindexed", indexedRelations)
migrated += indexedRelations
if migrated != total {
log.Errorf("migration reindexAll: %d/%d completed", migrated, len(ids))
} else {
log.Debugf("migration reindexAll completed for %d objects", migrated)
}
return migrated, nil
}
func reindexAll(a *Anytype, lastMigration bool) error {
return doWithRunningNode(a, true, !lastMigration, func() error {
ids, err := a.localStore.Objects.ListIds()
_, err := ReindexAll(a)
return err
})
}
func reindexStoredRelations(a *Anytype, lastMigration bool) error {
return doWithRunningNode(a, true, !lastMigration, func() error {
rels, err := a.localStore.Objects.ListRelations("")
if err != nil {
return err
}
total := len(ids)
var migrated int
for _, id := range ids {
sbt, err := smartblock.SmartBlockTypeFromID(id)
if err != nil {
return fmt.Errorf("migration reindexAll:failed to extract smartblock type: %w", err)
migrate := func(old string) (new string, hasChanges bool) {
if strings.HasPrefix(old, addr.OldCustomObjectTypeURLPrefix) {
new = strings.TrimPrefix(old, addr.OldCustomObjectTypeURLPrefix)
hasChanges = true
} else if strings.HasPrefix(old, addr.OldBundledObjectTypeURLPrefix) {
new = addr.BundledObjectTypeURLPrefix + strings.TrimPrefix(old, addr.OldBundledObjectTypeURLPrefix)
hasChanges = true
} else {
new = old
}
if sbt == smartblock.SmartBlockTypeArchive {
// remove archive we have accidentally indexed
err = a.localStore.Objects.DeleteObject(id)
if err != nil {
log.Errorf("migration reindexAll: failed to delete archive from index: %s", err.Error())
}
total--
continue
}
for _, idx := range a.localStore.Objects.Indexes() {
if idx.Name == "objtype_relkey_setid" {
// skip it because we can't reindex relations in sets for now
continue
}
err = localstore.EraseIndex(idx, a.t.Datastore().(ds.TxnDatastore))
if err != nil {
log.Errorf("migration reindexAll: failed to delete archive from index: %s", err.Error())
}
}
oi, err := a.localStore.Objects.GetByIDs(id)
if err != nil {
log.Errorf("migration reindexAll: failed to get objects by id: %s", err.Error())
continue
}
if len(oi) < 1 {
log.Errorf("migration reindexAll: failed to get objects: not found")
continue
}
o := oi[0]
var objType string
if pbtypes.HasField(o.Details, bundle.RelationKeyType.String()) {
objTypes := pbtypes.GetStringList(o.Details, bundle.RelationKeyType.String())
if len(objTypes) > 0 {
objType = objTypes[0]
}
}
o.Details.Fields[bundle.RelationKeyType.String()] = pbtypes.String(objType)
err = a.localStore.Objects.CreateObject(id, o.Details, o.Relations, nil, o.Snippet)
if err != nil {
log.Errorf("migration reindexAll: failed to get objects by id: %s", err.Error())
continue
}
migrated++
return
}
if migrated != total {
log.Errorf("migration reindexAll: %d/%d completed", migrated, len(ids))
} else {
log.Debugf("migration reindexAll completed for %d objects", migrated)
for i, rel := range rels {
if len(rel.ObjectTypes) == 0 {
continue
}
var newOts []string
var hasChanges2 bool
for _, ot := range rel.ObjectTypes {
newOt, hasChanges1 := migrate(ot)
hasChanges2 = hasChanges2 || hasChanges1
newOts = append(newOts, newOt)
}
if hasChanges2 {
rels[i].ObjectTypes = newOts
}
}
return nil
return a.localStore.Objects.StoreRelations(rels)
})
}
@ -545,10 +646,9 @@ func addMissingLayout(a *Anytype, lastMigration bool) error {
layout = t.Layout
}
} else {
otId := strings.TrimPrefix(otUrl, objects.CustomObjectTypeURLPrefix)
oi, err := a.localStore.Objects.GetByIDs(otId)
oi, err := a.localStore.Objects.GetByIDs(otUrl)
if err != nil {
log.Errorf("migration addMissingLayout: failed to get objects by id: %s", err.Error())
log.Errorf("migration addMissingLayout: failed to get objects type by id: %s", err.Error())
continue
} else if len(oi) == 0 {
log.Errorf("migration addMissingLayout: failed to get custom type '%s'", otUrl)
@ -565,7 +665,7 @@ func addMissingLayout(a *Anytype, lastMigration bool) error {
o.Details.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Float64(float64(layout))
err = a.localStore.Objects.UpdateObject(id, o.Details, o.Relations, nil, o.Snippet)
if err != nil {
log.Errorf("migration addMissingLayout: failed to get objects by id: %s", err.Error())
log.Errorf("migration addMissingLayout: failed to UpdateObject: %s", err.Error())
continue
}
migrated++

View file

@ -87,7 +87,7 @@ func WithRootPathAndAccount(rootPath string, account string) ServiceOption {
WithInMemoryDS(cfg.InMemoryDS),
WithFullTextSearch(cfg.FullTextSearch),
WithNetDebug(false),
WithNetPubSub(true),
WithNetPubSub(!cfg.DisablePubsub),
WithNetSyncTracking(true),
}

View file

@ -3,6 +3,7 @@ package core
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"strings"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
@ -235,6 +236,7 @@ func (block *smartBlock) GetSnapshots(offset vclock.VClock, limit int, metaOnly
}
func (block *smartBlock) PushRecord(payload proto.Marshaler) (id string, err error) {
metrics.ChangeCreatedCounter.Inc()
payloadB, err := payload.Marshal()
if err != nil {
return "", err
@ -279,6 +281,7 @@ func (block *smartBlock) SubscribeForRecords(ch chan SmartblockRecordEnvelope) (
return
}
metrics.ChangeReceivedCounter.Inc()
rec, err := block.decodeRecord(ctx, val.Value(), false)
if err != nil {
log.Errorf("failed to decode thread record: %s", err.Error())

View file

@ -2,26 +2,48 @@ package smartblock
import (
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
"github.com/textileio/go-threads/core/thread"
"strings"
)
type SmartBlockType uint64
const (
SmartBlockTypePage SmartBlockType = 0x10
SmartBlockTypeProfilePage SmartBlockType = 0x11
SmartBlockTypeHome SmartBlockType = 0x20
SmartBlockTypeArchive SmartBlockType = 0x30
SmartBlockTypeDatabase SmartBlockType = 0x40
SmartBlockTypeSet SmartBlockType = 0x41
SmartBlockTypeObjectType SmartBlockType = 0x60
SmartBlockTypeFile SmartBlockType = 0x100
SmartBlockTypePage SmartBlockType = 0x10
SmartBlockTypeProfilePage SmartBlockType = 0x11
SmartBlockTypeHome SmartBlockType = 0x20
SmartBlockTypeArchive SmartBlockType = 0x30
SmartBlockTypeDatabase SmartBlockType = 0x40
SmartBlockTypeSet SmartBlockType = 0x41
SmartBlockTypeObjectType SmartBlockType = 0x60
SmartBlockTypeFile SmartBlockType = 0x100
SmartblockTypeMarketplaceType SmartBlockType = 0x110
SmartblockTypeMarketplaceRelation SmartBlockType = 0x111
SmartBlockTypeBundledRelation SmartBlockType = 0x200 // temp
SmartBlockTypeIndexedRelation SmartBlockType = 0x201 // temp
SmartBlockTypeBundledObjectType SmartBlockType = 0x202 // temp
)
func SmartBlockTypeFromID(id string) (SmartBlockType, error) {
if strings.HasPrefix(id, addr.BundledRelationURLPrefix) {
return SmartBlockTypeBundledRelation, nil
}
if strings.HasPrefix(id, addr.CustomRelationURLPrefix) {
return SmartBlockTypeIndexedRelation, nil
}
if strings.HasPrefix(id, addr.BundledObjectTypeURLPrefix) {
return SmartBlockTypeBundledObjectType, nil
}
if strings.HasPrefix(id, addr.AnytypeProfileId) {
return SmartBlockTypeProfilePage, nil
}
c, err := cid.Decode(id)
// TODO: discard this fragile condition as soon as we will move to the multiaddr with prefix
if err == nil && c.Prefix().Codec == 0x70 && c.Prefix().MhType == multihash.SHA2_256 {
@ -59,6 +81,8 @@ func (sbt SmartBlockType) ToProto() model.ObjectInfoType {
return model.ObjectInfo_Archive
case SmartBlockTypeSet:
return model.ObjectInfo_Set
case SmartblockTypeMarketplaceType:
return model.ObjectInfo_Set
default:
return model.ObjectInfo_Page
}

View file

@ -3,6 +3,7 @@ package threads
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"path/filepath"
"time"
@ -54,6 +55,7 @@ func (s *service) threadsDbInit() error {
}
s.db = d
s.dbCloser = store
s.threadsCollection = s.db.GetCollection(ThreadInfoCollectionName)
err = s.threadsDbListen()
@ -114,8 +116,12 @@ func (s *service) threadsDbListen() error {
continue
}
metrics.ExternalThreadReceivedCounter.Inc()
go func() {
s.processNewExternalThreadUntilSuccess(tid, ti)
if s.processNewExternalThreadUntilSuccess(tid, ti) != nil {
log.With("thread", tid.String()).Error("processNewExternalThreadUntilSuccess failed: %s", err.Error())
return
}
ch := s.getNewThreadChan()
if ch != nil {
@ -135,23 +141,29 @@ func (s *service) threadsDbListen() error {
// processNewExternalThreadUntilSuccess tries to add the new thread from remote peer until success
// supposed to be run in goroutine
func (s *service) processNewExternalThreadUntilSuccess(tid thread.ID, ti threadInfo) {
func (s *service) processNewExternalThreadUntilSuccess(tid thread.ID, ti threadInfo) error {
log := log.With("thread", tid.String())
log.With("threadAddrs", ti.Addrs).Info("got new thread")
start := time.Now()
var attempt int
for {
metrics.ExternalThreadHandlingAttempts.Inc()
attempt++
<-s.newThreadProcessingLimiter
err := s.processNewExternalThread(tid, ti)
if err != nil {
s.newThreadProcessingLimiter <- struct{}{}
log.Errorf("processNewExternalThread failed after %d attempt: %s", attempt, err.Error())
} else {
s.newThreadProcessingLimiter <- struct{}{}
metrics.ServedThreads.Inc()
metrics.ExternalThreadHandlingDuration.Observe(time.Since(start).Seconds())
log.Debugf("processNewExternalThread succeed after %d attempt", attempt)
return
return nil
}
select {
case <-s.ctx.Done():
return
return context.Canceled
case <-time.After(time.Duration(5*attempt) * time.Second):
continue
}
@ -179,12 +191,12 @@ func (s *service) processNewExternalThread(tid thread.ID, ti threadInfo) error {
var localThreadInfo thread.Info
var replAddrWithThread ma.Multiaddr
if s.replicatorAddr != nil {
replAddrWithThread, err = util2.MultiAddressAddThread(s.replicatorAddr, tid)
if err != nil {
return err
}
if !util2.MultiAddressHasReplicator(multiAddrs, s.replicatorAddr) {
replAddrWithThread, err = util2.MultiAddressAddThread(s.replicatorAddr, tid)
if err != nil {
return err
}
log.Warn("processNewExternalThread: cafe addr not found among thread addresses, will add it")
multiAddrs = append(multiAddrs, replAddrWithThread)
}
@ -264,8 +276,9 @@ func (s *service) processNewExternalThread(tid thread.ID, ti threadInfo) error {
return fmt.Errorf("failed to add thread from any provided remote address")
}
if s.replicatorAddr != nil && !util2.MultiAddressHasReplicator(localThreadInfo.Addrs, s.replicatorAddr) {
_, err = s.t.AddReplicator(s.ctx, tid, replAddrWithThread)
if s.replicatorAddr != nil {
// add replicator for own logs
_, err = s.t.AddReplicator(s.ctx, tid, s.replicatorAddr)
if err != nil {
log.Errorf("processNewExternalThread failed to add the replicator: %s", err.Error())
}

View file

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"strings"
"time"
@ -68,15 +69,27 @@ func (s *service) addMissingThreadsFromCollection() error {
}
if _, err = s.t.GetThread(context.Background(), tid); err != nil && errors.Is(err, logstore.ErrThreadNotFound) {
metrics.ExternalThreadReceivedCounter.Add(1)
missingThreadsAdded++
go func() {
s.processNewExternalThreadUntilSuccess(tid, ti)
if s.processNewExternalThreadUntilSuccess(tid, ti) != nil {
log.With("thread", tid.String()).Error("processNewExternalThreadUntilSuccess failed: %s", err.Error())
return
}
ch := s.getNewThreadChan()
if ch != nil {
select {
case <-s.ctx.Done():
case ch <- tid.String():
}
}
}()
}
}
if missingThreadsAdded > 0 {
log.Warnf("addMissingThreadsFromCollection: adding %d missing threads in background...", missingThreadsAdded)
log.Warnf("addMissingThreadsFromCollection: processing %d missing threads in background...", missingThreadsAdded)
}
return nil
}

View file

@ -5,6 +5,7 @@ import (
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/wallet"
@ -27,6 +28,9 @@ const (
threadDerivedIndexSetPages threadDerivedIndex = 20
threadDerivedIndexMarketplaceType threadDerivedIndex = 30
threadDerivedIndexMarketplaceRelation threadDerivedIndex = 31
anytypeThreadSymmetricKeyPathPrefix = "m/SLIP-0021/anytype"
// TextileAccountPathFormat is a path format used for Anytype keypair
// derivation as described in SEP-00XX. Use with `fmt.Sprintf` and `DeriveForPath`.
@ -39,11 +43,13 @@ const (
)
type DerivedSmartblockIds struct {
Account string
Profile string
Home string
Archive string
SetPages string
Account string
Profile string
Home string
Archive string
SetPages string
MarketplaceType string
MarketplaceRelation string
}
var threadDerivedIndexToThreadName = map[threadDerivedIndex]string{
@ -52,10 +58,12 @@ var threadDerivedIndexToThreadName = map[threadDerivedIndex]string{
threadDerivedIndexArchive: "archive",
}
var threadDerivedIndexToSmartblockType = map[threadDerivedIndex]smartblock.SmartBlockType{
threadDerivedIndexProfilePage: smartblock.SmartBlockTypeProfilePage,
threadDerivedIndexHome: smartblock.SmartBlockTypeHome,
threadDerivedIndexArchive: smartblock.SmartBlockTypeArchive,
threadDerivedIndexSetPages: smartblock.SmartBlockTypeSet,
threadDerivedIndexProfilePage: smartblock.SmartBlockTypeProfilePage,
threadDerivedIndexHome: smartblock.SmartBlockTypeHome,
threadDerivedIndexArchive: smartblock.SmartBlockTypeArchive,
threadDerivedIndexSetPages: smartblock.SmartBlockTypeSet,
threadDerivedIndexMarketplaceType: smartblock.SmartblockTypeMarketplaceType,
threadDerivedIndexMarketplaceRelation: smartblock.SmartblockTypeMarketplaceRelation,
}
var ErrAddReplicatorsAttemptsExceeded = fmt.Errorf("add replicatorAddr attempts exceeded")
@ -100,13 +108,19 @@ func (s *service) EnsurePredefinedThreads(ctx context.Context, newAccount bool)
// pull only after threadsDbInit to handle all events
_, err = s.pullThread(ctxPull, account.ID)
if err != nil {
log.Errorf("account pull failed")
log.Errorf("account pull failed: %s", err.Error())
return
} else {
log.Infof("account pull done")
}
err = s.threadsDbMigration(account.ID.String())
if !justCreated {
ids, _ := s.t.Logstore().Threads()
metrics.ServedThreads.Set(float64(len(ids)))
err = s.threadsDbMigration(account.ID.String())
} else {
metrics.ServedThreads.Set(0)
}
}()
if justCreated {
@ -160,6 +174,20 @@ func (s *service) EnsurePredefinedThreads(ctx context.Context, newAccount bool)
}
ids.SetPages = setPages.ID.String()
// marketplace
marketplace, _, err := s.derivedThreadEnsure(cctx, threadDerivedIndexMarketplaceType, newAccount, true)
if err != nil {
return ids, err
}
ids.MarketplaceType = marketplace.ID.String()
// marketplace library
marketplaceLib, _, err := s.derivedThreadEnsure(cctx, threadDerivedIndexMarketplaceRelation, newAccount, true)
if err != nil {
return ids, err
}
ids.MarketplaceRelation = marketplaceLib.ID.String()
return ids, nil
}
@ -305,6 +333,8 @@ func (s *service) derivedThreadCreate(index threadDerivedIndex) (thread.Info, er
return thread.Info{}, err
}
metrics.ServedThreads.Inc()
metrics.ThreadAdded.Inc()
// because this thread just have been created locally we can safely put all networking in the background
go func() {
if s.replicatorAddr == nil {
@ -371,6 +401,9 @@ func (s *service) derivedThreadAddExistingFromLocalOrRemote(ctx context.Context,
return
}
metrics.ServedThreads.Inc()
metrics.ThreadAdded.Inc()
justCreated = true
if s.replicatorAddr != nil {

View file

@ -3,6 +3,8 @@ package threads
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"io"
"sync"
"time"
@ -22,16 +24,20 @@ import (
var log = logging.Logger("anytype-threads")
const simultaneousRequests = 20
type service struct {
t net2.NetBoostrapper
db *db.DB
threadsCollection *db.Collection
threadsGetter ThreadsGetter
device wallet.Keypair
account wallet.Keypair
repoRootPath string
newHeadProcessor func(id thread.ID) error
newThreadChan chan<- string
t net2.NetBoostrapper
db *db.DB
dbCloser io.Closer
threadsCollection *db.Collection
threadsGetter ThreadsGetter
device wallet.Keypair
account wallet.Keypair
repoRootPath string
newHeadProcessor func(id thread.ID) error
newThreadChan chan<- string
newThreadProcessingLimiter chan struct{}
replicatorAddr ma.Multiaddr
ctx context.Context
@ -41,16 +47,22 @@ type service struct {
func New(threadsAPI net2.NetBoostrapper, threadsGetter ThreadsGetter, repoRootPath string, deviceKeypair wallet.Keypair, accountKeypair wallet.Keypair, newHeadProcessor func(id thread.ID) error, newThreadChan chan string, replicatorAddr ma.Multiaddr) Service {
ctx, cancel := context.WithCancel(context.Background())
limiter := make(chan struct{}, simultaneousRequests)
for i := 0; i < cap(limiter); i++ {
limiter <- struct{}{}
}
return &service{
t: threadsAPI,
threadsGetter: threadsGetter,
device: deviceKeypair,
repoRootPath: repoRootPath,
account: accountKeypair,
newHeadProcessor: newHeadProcessor,
replicatorAddr: replicatorAddr,
ctx: ctx,
ctxCancel: cancel,
t: threadsAPI,
threadsGetter: threadsGetter,
device: deviceKeypair,
repoRootPath: repoRootPath,
account: accountKeypair,
newHeadProcessor: newHeadProcessor,
replicatorAddr: replicatorAddr,
newThreadProcessingLimiter: limiter,
ctx: ctx,
ctxCancel: cancel,
}
}
@ -104,12 +116,20 @@ func (s *service) Threads() net2.NetBoostrapper {
func (s *service) Close() error {
// close global service context to stop all work
s.ctxCancel()
// lock in order to wait for work to finish and, e.g. db to init
s.Lock()
defer s.Unlock()
if db := s.db; db != nil {
return db.Close()
if s.db != nil {
err := s.db.Close()
if err != nil {
return err
}
}
if s.dbCloser != nil {
// close underlying badger datastore, because threadsDb does not close datastore it have constructed with
err := s.dbCloser.Close()
if err != nil {
return err
}
}
return nil
}
@ -138,6 +158,9 @@ func (s *service) CreateThread(blockType smartblock.SmartBlockType) (thread.Info
return thread.Info{}, err
}
metrics.ServedThreads.Inc()
metrics.ThreadAdded.Inc()
var replAddrWithThread ma.Multiaddr
if s.replicatorAddr != nil {
replAddrWithThread, err = util2.MultiAddressAddThread(s.replicatorAddr, thrdId)
@ -166,9 +189,11 @@ func (s *service) CreateThread(blockType smartblock.SmartBlockType) (thread.Info
if replAddrWithThread != nil {
go func() {
attempt := 0
start := time.Now()
// todo: rewrite to job queue in badger
for {
attempt++
metrics.ThreadAddReplicatorAttempts.Inc()
p, err := s.t.AddReplicator(context.TODO(), thrd.ID, replAddrWithThread)
if err != nil {
log.Errorf("failed to add log replicator after %d attempt: %s", attempt, err.Error())
@ -180,6 +205,7 @@ func (s *service) CreateThread(blockType smartblock.SmartBlockType) (thread.Info
continue
}
metrics.ThreadAddReplicatorDuration.Observe(time.Since(start).Seconds())
log.With("thread", thrd.ID.String()).Infof("added log replicator after %d attempt: %s", attempt, p.String())
return
}

View file

@ -3,6 +3,7 @@ package threads
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-middleware/metrics"
"time"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/util"
@ -83,21 +84,33 @@ func (s *service) addMissingReplicators() error {
}
for _, threadId := range threadsIds {
ctx, _ := context.WithTimeout(s.ctx, time.Second*30)
thrd, err := s.t.GetThread(ctx, threadId)
thrdLogs, err := s.t.Logstore().GetManagedLogs(threadId)
if err != nil {
log.Errorf("failed to get thread %s: %s", threadId.String(), err.Error())
continue
}
if !util.MultiAddressHasReplicator(thrd.Addrs, s.replicatorAddr) {
err = s.addReplicatorWithAttempts(s.ctx, thrd, s.replicatorAddr, 0)
if err != nil {
log.Errorf("failed to add missing replicator for %s: %s", thrd.ID, err.Error())
} else {
log.Warnf("added missing replicator for %s", thrd.ID)
for _, thrdLog := range thrdLogs {
if !util.MultiAddressHasReplicator(thrdLog.Addrs, s.replicatorAddr) {
ctx, cancel := context.WithTimeout(s.ctx, time.Second*30)
thrd, err := s.t.GetThread(ctx, threadId)
cancel()
if err != nil {
log.Errorf("addMissingReplicators failed to get thread %s: %s", threadId, err.Error())
continue
}
err = s.addReplicatorWithAttempts(s.ctx, thrd, s.replicatorAddr, 0)
if err != nil {
log.Errorf("failed to add missing replicator for %s: %s", thrd.ID, err.Error())
} else {
log.Warnf("added missing replicator for %s", thrd.ID)
// we can break here, because thread.addReplicator has successfully added replicator for each managed log
break
}
}
}
}
return nil
}
@ -110,19 +123,23 @@ func (s *service) addReplicatorWithAttempts(ctx context.Context, thrd thread.Inf
return fmt.Errorf("replicatorAddr is nil")
}
if util.MultiAddressHasReplicator(thrd.Addrs, replicatorAddr) {
return nil
}
cafeAddrWithThread, err := util.MultiAddressAddThread(replicatorAddr, thrd.ID)
pId, err := s.device.PeerId()
if err != nil {
return err
}
ownLog := util.GetLog(thrd.Logs, pId)
if util.MultiAddressHasReplicator(ownLog.Addrs, replicatorAddr) {
return nil
}
log.With("thread", thrd.ID.String()).Debugf("no cafe addr found for own log")
start := time.Now()
for {
start := time.Now()
_, err = s.t.AddReplicator(ctx, thrd.ID, cafeAddrWithThread)
metrics.ThreadAddReplicatorAttempts.Inc()
_, err = s.t.AddReplicator(ctx, thrd.ID, replicatorAddr)
if err == nil {
metrics.ThreadAddReplicatorDuration.Observe(time.Since(start).Seconds())
return
}

View file

@ -1,6 +1,7 @@
package database
import (
"fmt"
"time"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
@ -33,6 +34,7 @@ type Reader interface {
GetRelation(key string) (relation *pbrelation.Relation, err error)
// ListRelations returns both indexed and bundled relations
ListRelations(objType string) (relations []*pbrelation.Relation, err error)
AggregateRelationsFromObjectsOfType(objType string) (relations []*pbrelation.Relation, err error)
@ -60,12 +62,13 @@ type Database interface {
}
type Query struct {
FullText string
Relations []*model.BlockContentDataviewRelation // relations used to provide relations options
Filters []*model.BlockContentDataviewFilter // filters results. apply sequentially
Sorts []*model.BlockContentDataviewSort // order results. apply hierarchically
Limit int // maximum number of results
Offset int // skip given number of results
FullText string
Relations []*model.BlockContentDataviewRelation // relations used to provide relations options
Filters []*model.BlockContentDataviewFilter // filters results. apply sequentially
Sorts []*model.BlockContentDataviewSort // order results. apply hierarchically
Limit int // maximum number of results
Offset int // skip given number of results
WithSystemObjects bool
}
func (q Query) DSQuery(sch *schema.Schema) (qq query.Query, err error) {
@ -79,6 +82,7 @@ func (q Query) DSQuery(sch *schema.Schema) (qq query.Query, err error) {
if f.hasOrders() {
qq.Orders = []query.Order{f}
}
qq.String()
return
}
@ -115,7 +119,7 @@ func newFilters(q Query, sch *schema.Schema) (f *filters, err error) {
Value: pbtypes.String(sch.ObjType.Url),
},
}
if sch.ObjType.Url == "https://anytype.io/schemas/object/bundled/page" {
if sch.ObjType.Url == bundle.TypeKeyPage.URL() {
relTypeFilter = append(relTypeFilter, filter.Empty{
Key: bundle.RelationKeyType.String(),
})
@ -204,6 +208,15 @@ func (f *filters) hasOrders() bool {
return f.order != nil
}
func (f *filters) String() string {
var fs string
if f.filter != nil {
fs = fmt.Sprintf("WHERE %v", f.filter.String())
}
// TODO: order to string
return fs
}
func dateOnly(v *types.Value) *types.Value {
tm := time.Unix(int64(v.GetNumberValue()), 0)
tm = time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, tm.Location())

View file

@ -108,6 +108,7 @@ type Getter interface {
type Filter interface {
FilterObject(g Getter) bool
String() string
}
type AndFilters []Filter
@ -121,6 +122,14 @@ func (a AndFilters) FilterObject(g Getter) bool {
return true
}
func (a AndFilters) String() string {
var andS []string
for _, f := range a {
andS = append(andS, f.String())
}
return fmt.Sprintf("(%s)", strings.Join(andS, " AND "))
}
type OrFilters []Filter
func (a OrFilters) FilterObject(g Getter) bool {
@ -135,6 +144,14 @@ func (a OrFilters) FilterObject(g Getter) bool {
return false
}
func (a OrFilters) String() string {
var orS []string
for _, f := range a {
orS = append(orS, f.String())
}
return fmt.Sprintf("(%s)", strings.Join(orS, " OR "))
}
type Not struct {
Filter
}
@ -146,6 +163,10 @@ func (n Not) FilterObject(g Getter) bool {
return !n.Filter.FilterObject(g)
}
func (n Not) String() string {
return fmt.Sprintf("NOT(%s)", n.Filter.String())
}
type Eq struct {
Key string
Cond model.BlockContentDataviewFilterCondition
@ -181,6 +202,23 @@ func (e Eq) filterObject(v *types.Value) bool {
return false
}
func (e Eq) String() string {
var eq string
switch e.Cond {
case model.BlockContentDataviewFilter_Equal:
eq = "="
case model.BlockContentDataviewFilter_Greater:
eq = ">"
case model.BlockContentDataviewFilter_GreaterOrEqual:
eq = ">="
case model.BlockContentDataviewFilter_Less:
eq = "<"
case model.BlockContentDataviewFilter_LessOrEqual:
eq = "<="
}
return fmt.Sprintf("%s %s '%s'", e.Key, eq, e.Value)
}
type In struct {
Key string
Value *types.ListValue
@ -197,6 +235,10 @@ func (i In) FilterObject(g Getter) bool {
return false
}
func (i In) String() string {
return fmt.Sprintf("%v IN(%v)", i.Key, i.Value)
}
type Like struct {
Key string
Value *types.Value
@ -214,6 +256,10 @@ func (l Like) FilterObject(g Getter) bool {
return strings.Contains(strings.ToLower(valStr), strings.ToLower(l.Value.GetStringValue()))
}
func (l Like) String() string {
return fmt.Sprintf("%v LIKE '%v'", l.Key, l.Value)
}
type Empty struct {
Key string
}
@ -243,6 +289,10 @@ func (e Empty) FilterObject(g Getter) bool {
return false
}
func (e Empty) String() string {
return fmt.Sprintf("%v IS EMPTY", e.Key)
}
type AllIn struct {
Key string
Value *types.ListValue
@ -272,3 +322,7 @@ func (l AllIn) FilterObject(g Getter) bool {
}
return true
}
func (l AllIn) String() string {
return fmt.Sprintf("%s ALLIN(%v)", l.Key, l.Value)
}

View file

@ -0,0 +1,11 @@
package addr
const (
BundledRelationURLPrefix = "_br"
BundledObjectTypeURLPrefix = "_ot"
CustomRelationURLPrefix = "_ir"
AnytypeProfileId = "_anytype_profile"
OldCustomObjectTypeURLPrefix = "https://anytype.io/schemas/object/custom/"
OldBundledObjectTypeURLPrefix = "https://anytype.io/schemas/object/bundled/"
)

View file

@ -3,6 +3,7 @@ package localstore
import (
"encoding/binary"
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"strings"
"sync"
"time"
@ -176,11 +177,6 @@ var (
_ ObjectStore = (*dsObjectStore)(nil)
)
const (
CustomObjectTypeURLPrefix = "https://anytype.io/schemas/object/custom/"
BundledObjectTypeURLPrefix = "https://anytype.io/schemas/object/bundled/"
)
type relationOption struct {
relationKey string
optionId string
@ -469,12 +465,18 @@ func (m *dsObjectStore) Query(sch *schema.Schema, q database.Query) (records []d
dsq.Offset = 0
dsq.Limit = 0
dsq.Prefix = pagesDetailsBase.String() + "/"
dsq.Filters = append([]query.Filter{filterNotSystemObjects}, dsq.Filters...)
if !q.WithSystemObjects {
dsq.Filters = append([]query.Filter{filterNotSystemObjects}, dsq.Filters...)
}
if q.FullText != "" {
if dsq, err = m.makeFTSQuery(q.FullText, dsq); err != nil {
return
}
}
for _, f := range dsq.Filters {
log.Warnf("query filter: %+v", f)
}
res, err := txn.Query(dsq)
if err != nil {
return nil, 0, fmt.Errorf("error when querying ds: %w", err)
@ -986,6 +988,8 @@ func diffSlices(a, b []string) (removed []string, added []string) {
}
func (m *dsObjectStore) CreateObject(id string, details *types.Struct, relations *pbrelation.Relations, links []string, snippet string) error {
m.l.Lock()
defer m.l.Unlock()
txn, err := m.ds.NewTransaction(false)
if err != nil {
return fmt.Errorf("error creating txn in datastore: %w", err)
@ -1006,6 +1010,8 @@ func (m *dsObjectStore) CreateObject(id string, details *types.Struct, relations
}
func (m *dsObjectStore) UpdateObject(id string, details *types.Struct, relations *pbrelation.Relations, links []string, snippet string) error {
m.l.Lock()
defer m.l.Unlock()
txn, err := m.ds.NewTransaction(false)
if err != nil {
return fmt.Errorf("error creating txn in datastore: %w", err)
@ -1044,8 +1050,6 @@ func (m *dsObjectStore) UpdateObject(id string, details *types.Struct, relations
}
func (m *dsObjectStore) updateObject(txn ds.Txn, id string, before model.ObjectInfo, details *types.Struct, relations *pbrelation.Relations, links []string, snippet string) error {
m.l.Lock()
defer m.l.Unlock()
sbt, err := smartblock.SmartBlockTypeFromID(id)
if err != nil {
return fmt.Errorf("failed to extract smartblock type: %w", err)
@ -1074,6 +1078,7 @@ func (m *dsObjectStore) updateObject(txn ds.Txn, id string, before model.ObjectI
}
if relations != nil && relations.Relations != nil {
// intentionally do not pass txn, as this tx may be huge
if err = m.updateRelations(txn, before.ObjectTypeUrls, objTypes, id, before.Relations, relations); err != nil {
return err
}
@ -1218,34 +1223,71 @@ func (m *dsObjectStore) UpdateRelationsInSet(setId, objTypeBefore, objTypeAfter
return txn.Commit()
}
func (m *dsObjectStore) StoreRelations(relations []*pbrelation.Relation) error {
m.l.Lock()
defer m.l.Unlock()
if relations == nil {
return nil
}
txn, err := m.ds.NewTransaction(false)
if err != nil {
return fmt.Errorf("error creating txn in datastore: %w", err)
}
defer txn.Discard()
err = m.storeRelations(txn, relations)
if err != nil {
return err
}
return txn.Commit()
}
func (m *dsObjectStore) storeRelations(txn ds.Txn, relations []*pbrelation.Relation) error {
var relBytes []byte
var err error
for _, relation := range relations {
if bundle.HasRelation(relation.Key) {
// do not store bundled relations
continue
}
relCopy := pbtypes.CopyRelation(relation)
relCopy.SelectDict = nil
var bundled = true
if !bundle.HasRelation(relation.Key) {
bundled = false
// do not store bundled relations
relationKey := relationsBase.ChildString(relation.Key)
relBytes, err = proto.Marshal(relCopy)
if err != nil {
return err
}
relationKey := relationsBase.ChildString(relation.Key)
relBytes, err = proto.Marshal(relCopy)
err = txn.Put(relationKey, relBytes)
if err != nil {
return err
}
}
_, details := bundle.GetDetailsForRelation(bundled, relCopy)
err = m.updateObject(txn, addr.CustomRelationURLPrefix+relation.Key, model.ObjectInfo{
Relations: &pbrelation.Relations{},
Details: &types.Struct{Fields: map[string]*types.Value{}},
}, details, nil, nil, relation.Description)
if err != nil {
return err
}
err = txn.Put(relationKey, relBytes)
if err != nil {
return err
}
}
return nil
}
func (m *dsObjectStore) updateRelations(txn ds.Txn, objTypesBefore []string, objTypesAfter []string, id string, relationsBefore *pbrelation.Relations, relationsAfter *pbrelation.Relations) error {
if relationsAfter == nil {
return nil
}
if relationsBefore != nil && pbtypes.RelationsEqual(relationsBefore.Relations, relationsAfter.Relations) {
return nil
}
relationsKey := pagesRelationsBase.ChildString(id)
var err error
for _, relation := range relationsAfter.Relations {
if relation.Format == pbrelation.RelationFormat_status || relation.Format == pbrelation.RelationFormat_tag {
var relBefore *pbrelation.Relation
@ -1269,13 +1311,13 @@ func (m *dsObjectStore) updateRelations(txn ds.Txn, objTypesBefore []string, obj
}
}
err = AddIndexesWithTxn(m, txn, relation, id)
err := AddIndexesWithTxn(m, txn, relation, id)
if err != nil {
return err
}
}
err = UpdateIndexWithTxn(indexObjectTypeRelationObjectId, txn, &relationObjectType{
err := UpdateIndexWithTxn(indexObjectTypeRelationObjectId, txn, &relationObjectType{
relationKeys: pbtypes.GetRelationKeys(relationsBefore.Relations),
objectTypes: objTypesBefore,
}, &relationObjectType{
@ -1349,26 +1391,13 @@ func (m *dsObjectStore) listIdsOfType(txn ds.Txn, ot string) ([]string, error) {
}
func (m *dsObjectStore) listRelationsKeys(txn ds.Txn) ([]string, error) {
res, err := GetKeysByIndexParts(txn, pagesPrefix, indexRelationObject.Name, nil, "", false, 0)
txn, err := m.ds.NewTransaction(true)
if err != nil {
return nil, err
return nil, fmt.Errorf("error creating txn in datastore: %w", err)
}
defer txn.Discard()
keys, err := ExtractKeysFromResults(res)
if err != nil {
return nil, err
}
var relKeys []string
for _, key := range keys {
relKey, err := CarveKeyParts(key, -2, -1)
if err != nil {
return nil, err
}
relKeys = append(relKeys, relKey)
}
return relKeys, nil
return findByPrefix(txn, relationsBase.String()+"/", 0)
}
func (m *dsObjectStore) getRelation(txn ds.Txn, key string) (*pbrelation.Relation, error) {
@ -1490,7 +1519,7 @@ func getOption(txn ds.Txn, optionId string) (*pbrelation.RelationOption, error)
} else if err := proto.Unmarshal(val, &opt); err != nil {
return nil, fmt.Errorf("failed to unmarshal option: %w", err)
}
return &opt, nil
}
@ -1671,20 +1700,75 @@ func extractIdFromKey(key string) (id string) {
// temp func until we move to the proper ids
func objTypeCompactEncode(objType string) (string, error) {
if strings.HasPrefix(objType, BundledObjectTypeURLPrefix) {
return "0" + strings.TrimPrefix(objType, BundledObjectTypeURLPrefix), nil
} else if strings.HasPrefix(objType, CustomObjectTypeURLPrefix) {
return "1" + strings.TrimPrefix(objType, CustomObjectTypeURLPrefix), nil
if strings.HasPrefix(objType, addr.BundledObjectTypeURLPrefix) {
return objType, nil
}
if strings.HasPrefix(objType, "ba") {
return objType, nil
}
return "", fmt.Errorf("invalid objType")
}
// temp func until we move to the proper ids
func objTypeCompactDecode(objTypeCompact string) (string, error) {
if strings.HasPrefix(objTypeCompact, "0") {
return BundledObjectTypeURLPrefix + strings.TrimPrefix(objTypeCompact, "0"), nil
} else if strings.HasPrefix(objTypeCompact, "1") {
return CustomObjectTypeURLPrefix + strings.TrimPrefix(objTypeCompact, "1"), nil
func GetObjectType(store ObjectStore, url string) (*pbrelation.ObjectType, error) {
objectType := &pbrelation.ObjectType{}
if strings.HasPrefix(url, addr.BundledObjectTypeURLPrefix) {
var err error
objectType, err = bundle.GetTypeByUrl(url)
if err != nil {
if err == bundle.ErrNotFound {
return nil, fmt.Errorf("unknown object type")
}
return nil, err
}
return objectType, nil
} else if !strings.HasPrefix(url, "b") {
return nil, fmt.Errorf("incorrect object type URL format")
}
return "", fmt.Errorf("invalid objType")
ois, err := store.GetByIDs(url)
if err != nil {
return nil, err
}
if len(ois) == 0 {
return nil, fmt.Errorf("object type not found in the index")
}
details := ois[0].Details
var relations []*pbrelation.Relation
if ois[0].Relations != nil {
relations = ois[0].Relations.Relations
}
for _, relId := range pbtypes.GetStringList(details, bundle.RelationKeyRecommendedRelations.String()) {
rk, err := pbtypes.RelationIdToKey(relId)
if err != nil {
log.Errorf("GetObjectType failed to get relation key from id: %s", err.Error())
continue
}
r := pbtypes.GetRelation(relations, rk)
if r == nil {
log.Errorf("invalid state of objectType %s: missing recommended relation %s", url, rk)
continue
}
relCopy := pbtypes.CopyRelation(r)
relCopy.Scope = pbrelation.Relation_type
objectType.Relations = append(objectType.Relations, relCopy)
}
objectType.Url = url
if details != nil && details.Fields != nil {
if v, ok := details.Fields[bundle.RelationKeyName.String()]; ok {
objectType.Name = v.GetStringValue()
}
if v, ok := details.Fields[bundle.RelationKeyRecommendedLayout.String()]; ok {
objectType.Layout = pbrelation.ObjectTypeLayout(int(v.GetNumberValue()))
}
if v, ok := details.Fields[bundle.RelationKeyIconEmoji.String()]; ok {
objectType.IconEmoji = v.GetStringValue()
}
}
return objectType, err
}

View file

@ -215,7 +215,7 @@ func TestDsObjectStore_RelationsIndex(t *testing.T) {
id1 := getId()
id2 := getId()
id3 := getId()
require.NoError(t, ds.UpdateObject(id1, newDet("one", "https://anytype.io/schemas/object/bundled/a1"), &pbrelation.Relations{Relations: []*pbrelation.Relation{
require.NoError(t, ds.UpdateObject(id1, newDet("one", "_ota1"), &pbrelation.Relations{Relations: []*pbrelation.Relation{
{
Key: "rel1",
Format: pbrelation.RelationFormat_status,
@ -235,7 +235,7 @@ func TestDsObjectStore_RelationsIndex(t *testing.T) {
},
}}, nil, "s1"))
require.NoError(t, ds.UpdateObject(id2, newDet("two", "https://anytype.io/schemas/object/bundled/a2"), &pbrelation.Relations{Relations: []*pbrelation.Relation{
require.NoError(t, ds.UpdateObject(id2, newDet("two", "_ota2"), &pbrelation.Relations{Relations: []*pbrelation.Relation{
{
Key: "rel1",
Format: pbrelation.RelationFormat_status,
@ -267,20 +267,20 @@ func TestDsObjectStore_RelationsIndex(t *testing.T) {
},
},
}}, nil, "s2"))
require.NoError(t, ds.UpdateObject(id3, newDet("three", "https://anytype.io/schemas/object/bundled/a2"), nil, nil, "s3"))
require.NoError(t, ds.UpdateObject(id3, newDet("three", "_ota2"), nil, nil, "s3"))
restOpts, err := ds.GetAggregatedOptions("rel1", pbrelation.RelationFormat_status, "https://anytype.io/schemas/object/bundled/ffff")
restOpts, err := ds.GetAggregatedOptions("rel1", pbrelation.RelationFormat_status, "_otffff")
require.NoError(t, err)
require.Len(t, restOpts, 6)
rels, err := ds.AggregateRelationsFromObjectsOfType("https://anytype.io/schemas/object/bundled/a1")
rels, err := ds.AggregateRelationsFromObjectsOfType("_ota1")
require.NoError(t, err)
require.Len(t, rels, 2)
require.Equal(t, "rel1", rels[0].Key)
require.Equal(t, "rel2", rels[1].Key)
rels, err = ds.ListRelations("https://anytype.io/schemas/object/bundled/a1")
rels, err = ds.ListRelations("_ota1")
require.NoError(t, err)
require.Len(t, rels, len(bundle.ListRelationsKeys())+4)

View file

@ -62,6 +62,8 @@ type ObjectStore interface {
CreateObject(id string, details *types.Struct, relations *pbrelation.Relations, links []string, snippet string) error
UpdateObject(id string, details *types.Struct, relations *pbrelation.Relations, links []string, snippet string) error
StoreRelations(relations []*pbrelation.Relation) error
DeleteObject(id string) error
RemoveRelationFromCache(key string) error

View file

@ -1,6 +1,7 @@
package logging
import (
"fmt"
"net/url"
"os"
"path/filepath"
@ -61,12 +62,15 @@ func getLoggingConfig() logging.Config {
}
func init() {
// should it be in goroutine?
tlsWriter, err := gelf.NewTLSWriter(graylogHost, nil)
if err != nil {
log.Error(err)
return
fmt.Printf("failed to init gelf tls: %s", err.Error())
} else {
tlsWriter.MaxReconnect = 1
tlsWriter.ReconnectDelay = 1 // bug within geld, will be multiplied by time.Second
gelfSinkWrapper.gelfWriter = tlsWriter
}
gelfSinkWrapper.gelfWriter = tlsWriter
err = zap.RegisterSink(graylogScheme, func(url *url.URL) (zap.Sink, error) {
// init tlsWriter outside to make sure it is available
@ -76,7 +80,6 @@ func init() {
if err != nil {
log.Error("failed to register zap sink", err.Error())
}
cfg := getLoggingConfig()
logging.SetupLogging(cfg)
}

View file

@ -9,7 +9,6 @@ import (
"path/filepath"
"time"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
ipfslite "github.com/hsanjuan/ipfs-lite"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/sync"
@ -18,12 +17,15 @@ import (
connmgr "github.com/libp2p/go-libp2p-connmgr"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/peerstore"
"github.com/libp2p/go-libp2p-core/pnet"
dht "github.com/libp2p/go-libp2p-kad-dht"
"github.com/libp2p/go-libp2p-peerstore/pstoreds"
swarm "github.com/libp2p/go-libp2p-swarm"
libp2ptls "github.com/libp2p/go-libp2p-tls"
"github.com/libp2p/go-tcp-transport"
ma "github.com/multiformats/go-multiaddr"
"github.com/textileio/go-threads/core/app"
"github.com/textileio/go-threads/core/logstore"
@ -34,6 +36,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/ipfs"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/ipfs/ipfsliteinterface"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
tnet "github.com/anytypeio/go-anytype-middleware/pkg/lib/net"
)
@ -44,6 +47,24 @@ const (
defaultLogstorePath = "logstore"
)
var permanentConnectionRetryDelay = time.Second * 5
type NoCloserDatastore struct {
datastore.Batching
}
func (ncd *NoCloserDatastore) NewTransaction(readOnly bool) (datastore.Txn, error) {
if v, ok := ncd.Batching.(datastore.TxnDatastore); ok {
return v.NewTransaction(readOnly)
}
return nil, fmt.Errorf("not implementing datastore.TxnDatastore")
}
func (ncd *NoCloserDatastore) Close() error {
log.Errorf("NoCloserDatastore close called()")
return nil
}
func DefaultNetwork(
repoPath string,
privKey crypto.PrivKey,
@ -80,8 +101,9 @@ func DefaultNetwork(
}
}
noCloserDs := &NoCloserDatastore{ds}
ctx, cancel := context.WithCancel(context.Background())
pstore, err := pstoreds.NewPeerstore(ctx, ds, pstoreds.DefaultOpts())
pstore, err := pstoreds.NewPeerstore(ctx, noCloserDs, pstoreds.DefaultOpts())
if err != nil {
ds.Close()
cancel()
@ -98,10 +120,11 @@ func DefaultNetwork(
privKey,
pnet,
[]ma.Multiaddr{config.HostAddr},
ds,
noCloserDs,
libp2p.ConnectionManager(connmgr.NewConnManager(100, 400, time.Minute)),
libp2p.Peerstore(pstore),
libp2p.Security(libp2ptls.ID, libp2ptls.New),
libp2p.Transport(tcp.NewTCPTransport), // connection timeout overridden in core.go init
)
if err != nil {
@ -110,7 +133,7 @@ func DefaultNetwork(
return nil, err
}
lite, err := ipfslite.New(ctx, ds, h, d, &ipfslite.Config{Offline: config.Offline})
lite, err := ipfslite.New(ctx, noCloserDs, h, d, &ipfslite.Config{Offline: config.Offline})
if err != nil {
cancel()
ds.Close()
@ -127,7 +150,8 @@ func DefaultNetwork(
ds.Close()
return nil, err
}
tstore, err := lstoreds.NewLogstore(ctx, logstore, lstoreds.DefaultOpts())
tstore, err := lstoreds.NewLogstore(ctx, &NoCloserDatastore{logstore}, lstoreds.DefaultOpts())
if err != nil {
cancel()
if err := logstore.Close(); err != nil {
@ -158,6 +182,48 @@ func DefaultNetwork(
return nil, err
}
if config.PermanentConnection != nil {
pidStr, _ := config.PermanentConnection.ValueForProtocol(ma.P_P2P)
pid, err := peer.Decode(pidStr)
if err != nil {
cancel()
if err := logstore.Close(); err != nil {
return nil, err
}
ds.Close()
return nil, fmt.Errorf("PermanentConnection invalid addr: %s", err.Error())
}
go func() {
for {
state := h.Network().Connectedness(pid)
// do not handle CanConnect purposefully
if state == network.NotConnected || state == network.CannotConnect {
if swrm, ok := h.Network().(*swarm.Swarm); ok {
// clear backoff in order to connect more aggressively
swrm.Backoff().Clear(pid)
}
err = h.Connect(ctx, peer.AddrInfo{
ID: pid,
Addrs: []ma.Multiaddr{config.PermanentConnection},
})
if err != nil {
log.Warnf("PermanentConnection failed: %s", err.Error())
} else {
log.Debugf("PermanentConnection %s reconnected succesfully", pid.String())
}
}
select {
case <-ctx.Done():
return
case <-time.After(permanentConnectionRetryDelay):
continue
}
}
}()
}
return &netBoostrapper{
cancel: cancel,
Net: api,
@ -173,13 +239,15 @@ func DefaultNetwork(
}
type NetConfig struct {
HostAddr ma.Multiaddr
GRPCServerOptions []grpc.ServerOption
GRPCDialOptions []grpc.DialOption
SyncTracking bool
Debug bool
Offline bool
InMemoryDS bool // should be used for tests only
HostAddr ma.Multiaddr
GRPCServerOptions []grpc.ServerOption
GRPCDialOptions []grpc.DialOption
SyncTracking bool
Debug bool
Offline bool
InMemoryDS bool // should be used for tests only
PermanentConnection ma.Multiaddr // explicitly watch the connection to this peer and reconnect in case the connection has failed
PubSub bool
}
@ -193,6 +261,13 @@ func WithNetHostAddr(addr ma.Multiaddr) NetOption {
}
}
func WithPermanentConnection(addr ma.Multiaddr) NetOption {
return func(c *NetConfig) error {
c.PermanentConnection = addr
return nil
}
}
func WithInMemoryDS(inMemoryDS bool) NetOption {
return func(c *NetConfig) error {
c.InMemoryDS = inMemoryDS
@ -245,11 +320,12 @@ func WithNetSyncTracking(enabled bool) NetOption {
type netBoostrapper struct {
cancel context.CancelFunc
app.Net
ipfs ipfs.IPFS
pstore peerstore.Peerstore
logstoreDatastore datastore.Batching
litestore datastore.Batching
logstore logstore.Logstore
ipfs ipfs.IPFS
pstore peerstore.Peerstore
logstore logstore.Logstore
logstoreDatastore datastore.Batching // need to close
litestore datastore.Batching // need to close
host host.Host
wanDht *dht.IpfsDHT
@ -288,20 +364,17 @@ func (tsb *netBoostrapper) Close() error {
if err := tsb.wanDht.Close(); err != nil {
return err
}
log.Debug("closing libp2p host...")
if err := tsb.host.Close(); err != nil {
return err
}
log.Debug("closing pstore...")
if err := tsb.pstore.Close(); err != nil {
return err
}
log.Debug("closing litestore...")
if err := tsb.litestore.Close(); err != nil {
return err
}
log.Debug("closing logstore...")
return tsb.logstoreDatastore.Close()
if err := tsb.logstoreDatastore.Close(); err != nil {
return err
}
return nil
}
func LoadKey(repoPath string) (crypto.PrivKey, error) {

View file

@ -16,14 +16,15 @@ message ObjectType {
enum Layout {
basic = 0;
profile = 1;
action = 2;
todo = 2;
set = 3;
objectType = 4;
relation = 5;
file = 6;
dashboard = 7;
image = 8;
database = 8; // to be released later
database = 20; // to be released later
}
}
@ -58,7 +59,10 @@ message Relation {
repeated Option selectDict = 12; // default dictionary with unique values to choose for select/multiSelect format
int32 maxCount = 13; // max number of values can be set for this relation. 0 means no limit. 1 means the value can be stored in non-repeated field
string description = 14;
Scope scope = 20; // on-store should be only local
// on-store fields, injected only locally
Scope scope = 20; // scope from which this relation have been aggregated
string creator = 21; // creator profile id
message Option {
string id = 1; // id generated automatically if omitted

View file

@ -90,37 +90,40 @@ type ObjectTypeLayout int32
const (
ObjectType_basic ObjectTypeLayout = 0
ObjectType_profile ObjectTypeLayout = 1
ObjectType_action ObjectTypeLayout = 2
ObjectType_todo ObjectTypeLayout = 2
ObjectType_set ObjectTypeLayout = 3
ObjectType_objectType ObjectTypeLayout = 4
ObjectType_relation ObjectTypeLayout = 5
ObjectType_file ObjectTypeLayout = 6
ObjectType_dashboard ObjectTypeLayout = 7
ObjectType_database ObjectTypeLayout = 8
ObjectType_image ObjectTypeLayout = 8
ObjectType_database ObjectTypeLayout = 20
)
var ObjectTypeLayout_name = map[int32]string{
0: "basic",
1: "profile",
2: "action",
3: "set",
4: "objectType",
5: "relation",
6: "file",
7: "dashboard",
8: "database",
0: "basic",
1: "profile",
2: "todo",
3: "set",
4: "objectType",
5: "relation",
6: "file",
7: "dashboard",
8: "image",
20: "database",
}
var ObjectTypeLayout_value = map[string]int32{
"basic": 0,
"profile": 1,
"action": 2,
"todo": 2,
"set": 3,
"objectType": 4,
"relation": 5,
"file": 6,
"dashboard": 7,
"database": 8,
"image": 8,
"database": 20,
}
func (x ObjectTypeLayout) String() string {
@ -443,6 +446,7 @@ type Relation struct {
MaxCount int32 `protobuf:"varint,13,opt,name=maxCount,proto3" json:"maxCount,omitempty"`
Description string `protobuf:"bytes,14,opt,name=description,proto3" json:"description,omitempty"`
Scope RelationScope `protobuf:"varint,20,opt,name=scope,proto3,enum=anytype.relation.RelationScope" json:"scope,omitempty"`
Creator string `protobuf:"bytes,21,opt,name=creator,proto3" json:"creator,omitempty"`
}
func (m *Relation) Reset() { *m = Relation{} }
@ -569,6 +573,13 @@ func (m *Relation) GetScope() RelationScope {
return Relation_object
}
func (m *Relation) GetCreator() string {
if m != nil {
return m.Creator
}
return ""
}
type RelationOption struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"`
@ -700,63 +711,64 @@ func init() {
}
var fileDescriptor_d7f8dc729d740ffd = []byte{
// 886 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x41, 0x8f, 0xdb, 0x44,
0x14, 0x8e, 0xed, 0xd8, 0x89, 0x5f, 0xda, 0x68, 0x3a, 0xac, 0x2a, 0x6b, 0x55, 0x45, 0xc1, 0x08,
0x11, 0x50, 0x71, 0xa4, 0xac, 0x54, 0x55, 0x20, 0x0e, 0x40, 0x17, 0x71, 0x40, 0x5a, 0xc9, 0x5b,
0x81, 0xd4, 0xdb, 0xd8, 0x9e, 0x24, 0xb3, 0x3b, 0xf1, 0x98, 0xf1, 0xb8, 0x5a, 0xf3, 0x2b, 0xb8,
0xf3, 0x23, 0x38, 0xf1, 0x1f, 0x38, 0xf6, 0x82, 0xc4, 0x11, 0xed, 0xfe, 0x0e, 0x24, 0x34, 0xe3,
0xd8, 0x49, 0xba, 0xdd, 0x96, 0xde, 0xe6, 0xcd, 0x7c, 0xf3, 0xe6, 0xf3, 0x7b, 0xdf, 0xfb, 0x0c,
0x9f, 0x16, 0x97, 0xab, 0x39, 0x67, 0xc9, 0xbc, 0x48, 0xe6, 0x92, 0x72, 0xa2, 0x98, 0xc8, 0xe7,
0x85, 0x14, 0x4a, 0x94, 0x5d, 0x1c, 0x99, 0x18, 0x23, 0x92, 0xd7, 0xaa, 0x2e, 0x68, 0xd4, 0xee,
0x1f, 0x3f, 0x5a, 0x09, 0xb1, 0xe2, 0xb4, 0xc1, 0x27, 0xd5, 0x72, 0x5e, 0x2a, 0x59, 0xa5, 0xaa,
0xc1, 0x87, 0xff, 0xda, 0x00, 0x67, 0xc9, 0x05, 0x4d, 0xd5, 0xf3, 0xba, 0xa0, 0x18, 0x81, 0x53,
0x49, 0x1e, 0x58, 0x53, 0x6b, 0xe6, 0xc7, 0x7a, 0x89, 0x31, 0xf4, 0x73, 0xb2, 0xa1, 0x81, 0x6d,
0xb6, 0xcc, 0x1a, 0x3f, 0x05, 0xbf, 0x4d, 0x5f, 0x06, 0xce, 0xd4, 0x99, 0x8d, 0x16, 0xc7, 0xd1,
0xeb, 0x0f, 0x47, 0xf1, 0x76, 0x11, 0xef, 0xc0, 0xf8, 0x4b, 0xf0, 0x38, 0xa9, 0x45, 0xa5, 0x82,
0xfe, 0xd4, 0x9a, 0x8d, 0x17, 0x1f, 0xdd, 0xbe, 0xb6, 0x63, 0x13, 0xfd, 0x60, 0xa0, 0xf1, 0xf6,
0x0a, 0x7e, 0x04, 0x3e, 0x4b, 0x45, 0x7e, 0xba, 0x11, 0x17, 0x2c, 0x70, 0x0d, 0x9f, 0xdd, 0x06,
0x9e, 0xc2, 0x28, 0xa3, 0x65, 0x2a, 0x59, 0xa1, 0xd3, 0x04, 0x9e, 0x39, 0xdf, 0xdf, 0xc2, 0x0f,
0xc1, 0x5b, 0xb3, 0x2c, 0xa3, 0x79, 0x30, 0x98, 0x5a, 0xb3, 0x61, 0xbc, 0x8d, 0xc2, 0x5f, 0xc0,
0x6b, 0x5e, 0xc2, 0x3e, 0xb8, 0x09, 0x29, 0x59, 0x8a, 0x7a, 0x78, 0x04, 0x83, 0x42, 0x8a, 0x25,
0xe3, 0x14, 0x59, 0x18, 0xc0, 0x23, 0xa9, 0xce, 0x81, 0x6c, 0x3c, 0x00, 0xa7, 0xa4, 0x0a, 0x39,
0x78, 0x0c, 0x20, 0x3a, 0xae, 0xa8, 0x8f, 0xef, 0xc1, 0xb0, 0xfd, 0x08, 0xe4, 0xe2, 0x21, 0xf4,
0xcd, 0x65, 0x0f, 0xdf, 0x07, 0x3f, 0x23, 0xe5, 0x3a, 0x11, 0x44, 0x66, 0x68, 0xa0, 0x61, 0x19,
0x51, 0x24, 0x21, 0x25, 0x45, 0xc3, 0xf0, 0x37, 0xab, 0x7b, 0xfc, 0x04, 0x6c, 0x96, 0x99, 0xd2,
0xff, 0xcf, 0xba, 0xd8, 0x2c, 0x7b, 0x63, 0x7b, 0xbe, 0x87, 0x07, 0x92, 0xfe, 0x5c, 0x31, 0x49,
0xb3, 0xf8, 0x3d, 0xda, 0x74, 0xfb, 0x52, 0x58, 0xc3, 0x83, 0x36, 0xf8, 0x89, 0xa9, 0xf5, 0x8f,
0x84, 0x57, 0x14, 0x3f, 0xd9, 0x7d, 0xa7, 0x61, 0xfb, 0xf6, 0xac, 0x1d, 0x16, 0x3f, 0x06, 0xf7,
0xa5, 0x4e, 0x60, 0xb8, 0x8e, 0x16, 0x0f, 0xa3, 0x46, 0x98, 0x51, 0x2b, 0xcc, 0xc8, 0xa4, 0x8f,
0x1b, 0x50, 0xf8, 0x97, 0x07, 0xc3, 0x36, 0x89, 0x96, 0xe5, 0x25, 0xad, 0x5b, 0x59, 0x5e, 0xd2,
0x1a, 0x3f, 0x05, 0x6f, 0x29, 0xe4, 0x86, 0x28, 0x93, 0x6d, 0xbc, 0x98, 0xde, 0x4d, 0xe1, 0x3b,
0x83, 0x8b, 0xb7, 0xf8, 0xae, 0x62, 0xce, 0x5e, 0xc5, 0xbe, 0x80, 0x7b, 0x19, 0x5d, 0x92, 0x8a,
0x2b, 0xc3, 0xc1, 0x88, 0xf3, 0x6e, 0x86, 0x07, 0x58, 0x7c, 0x0a, 0xa0, 0xfb, 0x79, 0x2e, 0x2a,
0x99, 0x52, 0x23, 0xcb, 0xf1, 0xe2, 0xe3, 0xbb, 0xd9, 0x44, 0xcf, 0x3a, 0x70, 0xbc, 0x77, 0x71,
0x4f, 0x9c, 0xde, 0xbe, 0x38, 0xf1, 0xb1, 0xae, 0x36, 0xc9, 0xce, 0x72, 0x5e, 0x6f, 0x65, 0xdb,
0xc5, 0xf8, 0x08, 0xdc, 0x4d, 0xc5, 0x15, 0x0b, 0x86, 0xe6, 0xa0, 0x09, 0xf4, 0x20, 0xec, 0x74,
0x59, 0x06, 0xfe, 0xd4, 0xd1, 0x83, 0xb0, 0xb7, 0x85, 0xbf, 0x06, 0x28, 0x29, 0xa7, 0xa9, 0x7a,
0xc6, 0x52, 0x15, 0xdc, 0x33, 0xca, 0xf8, 0xf0, 0x2d, 0x94, 0xcf, 0xcc, 0xfc, 0xc4, 0x7b, 0x97,
0x34, 0xad, 0x0d, 0xb9, 0xfa, 0x56, 0x54, 0xb9, 0x0a, 0xee, 0x4f, 0xad, 0x99, 0x1b, 0x77, 0xf1,
0xeb, 0x93, 0x38, 0xbe, 0x3d, 0x89, 0x4f, 0xc0, 0x2d, 0x53, 0x51, 0xd0, 0xe0, 0xe8, 0x5d, 0xcd,
0x8b, 0xce, 0x35, 0x2e, 0x6e, 0xe0, 0xc7, 0xbf, 0x5b, 0xe0, 0x35, 0x64, 0xf0, 0xb8, 0x9b, 0x16,
0xbf, 0x1d, 0x04, 0x45, 0xaf, 0x54, 0x3b, 0x08, 0x7a, 0xad, 0xeb, 0x93, 0x0a, 0x2e, 0xe4, 0xb6,
0xd7, 0x4d, 0x80, 0xbf, 0x6a, 0x1f, 0x6f, 0x2c, 0xe8, 0x93, 0x77, 0x7e, 0xf8, 0x01, 0x87, 0xf0,
0x31, 0xb8, 0x26, 0xd6, 0x66, 0xc1, 0x45, 0x4a, 0x38, 0xea, 0x1d, 0x8c, 0xbe, 0x71, 0x8b, 0x46,
0x6b, 0xc8, 0x0e, 0x5f, 0xb4, 0x68, 0x00, 0xaf, 0x69, 0x01, 0xea, 0x69, 0x6f, 0xd0, 0x0f, 0x22,
0x0b, 0x1f, 0x01, 0x2a, 0xa9, 0x3a, 0x5b, 0x3e, 0x5f, 0xd3, 0x73, 0xb2, 0xa1, 0xc6, 0x49, 0x6c,
0x1c, 0xc0, 0x51, 0x83, 0x2d, 0x0f, 0x4f, 0x1c, 0xed, 0x4a, 0x9c, 0x25, 0x92, 0xc8, 0x1a, 0xf5,
0xc3, 0x13, 0x80, 0x9d, 0x98, 0xf4, 0x51, 0x46, 0x15, 0x61, 0xbc, 0x6c, 0xdc, 0x2b, 0xa3, 0x92,
0xbd, 0xa4, 0x19, 0xb2, 0x74, 0x40, 0xd2, 0x54, 0xb7, 0x06, 0xd9, 0xe1, 0x29, 0xf8, 0xdd, 0x7c,
0x1f, 0x1a, 0xb9, 0xf5, 0x1e, 0x46, 0xfe, 0xd9, 0x1f, 0x16, 0x8c, 0x0f, 0x07, 0x4c, 0x17, 0x81,
0x8b, 0x7c, 0xa5, 0x2b, 0x8f, 0x7a, 0xda, 0xf5, 0xca, 0xb5, 0x90, 0xca, 0x84, 0xa6, 0x26, 0x79,
0xb5, 0x49, 0xa8, 0x44, 0xb6, 0x5e, 0x97, 0x8a, 0xa8, 0xaa, 0x44, 0x8e, 0x76, 0x53, 0x45, 0x56,
0x68, 0xa4, 0x6b, 0x92, 0x11, 0xa5, 0x7d, 0xb4, 0x75, 0x4e, 0x57, 0x67, 0x4c, 0xd7, 0x34, 0xbd,
0x4c, 0xc4, 0x15, 0xf2, 0x34, 0xb4, 0x92, 0x1c, 0x0d, 0x74, 0xe1, 0xe9, 0x86, 0x30, 0x8e, 0x86,
0x7a, 0x59, 0xac, 0x45, 0x4e, 0x91, 0xdf, 0xec, 0x8a, 0x0b, 0x86, 0x60, 0xaf, 0xd6, 0x99, 0xe6,
0xd1, 0xb1, 0x46, 0xf4, 0x9b, 0xcf, 0xff, 0xbc, 0x9e, 0x58, 0xaf, 0xae, 0x27, 0xd6, 0x3f, 0xd7,
0x13, 0xeb, 0xd7, 0x9b, 0x49, 0xef, 0xd5, 0xcd, 0xa4, 0xf7, 0xf7, 0xcd, 0xa4, 0xf7, 0xe2, 0x83,
0x37, 0xfc, 0x64, 0x13, 0xcf, 0x8c, 0xfe, 0xc9, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x76,
0x1f, 0x20, 0x82, 0x07, 0x00, 0x00,
// 904 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcd, 0x8e, 0xe3, 0x44,
0x10, 0x8e, 0xe3, 0xc4, 0x89, 0x2b, 0xb3, 0x51, 0x6f, 0x13, 0x56, 0xd6, 0x68, 0x15, 0x05, 0x23,
0x44, 0x40, 0x8b, 0x23, 0x65, 0xa4, 0xd5, 0x0a, 0xc4, 0x01, 0xd8, 0x41, 0x1c, 0x90, 0x46, 0xf2,
0xac, 0x40, 0xda, 0x5b, 0xdb, 0xee, 0x24, 0x3d, 0xd3, 0x76, 0x9b, 0x76, 0x7b, 0x35, 0x3e, 0x73,
0xe0, 0xca, 0x9d, 0x87, 0xe0, 0xc4, 0x3b, 0x70, 0xdc, 0x23, 0x47, 0x34, 0xf3, 0x00, 0xbc, 0x02,
0xea, 0x76, 0xec, 0x24, 0x3b, 0x3b, 0xfb, 0x73, 0xeb, 0xea, 0xfe, 0xaa, 0xfc, 0xa5, 0xea, 0xab,
0x2f, 0xf0, 0x59, 0x7e, 0xb9, 0x5e, 0x70, 0x16, 0x2d, 0xf2, 0x68, 0x21, 0x29, 0x27, 0x8a, 0x89,
0x6c, 0x91, 0x4b, 0xa1, 0x44, 0xd1, 0xc6, 0x81, 0x89, 0x31, 0x22, 0x59, 0xa5, 0xaa, 0x9c, 0x06,
0xcd, 0xfd, 0xf1, 0xc3, 0xb5, 0x10, 0x6b, 0x4e, 0x6b, 0x7c, 0x54, 0xae, 0x16, 0x85, 0x92, 0x65,
0xac, 0x6a, 0xbc, 0xff, 0x9b, 0x0d, 0x70, 0x16, 0x5d, 0xd0, 0x58, 0x3d, 0xab, 0x72, 0x8a, 0x11,
0xd8, 0xa5, 0xe4, 0x9e, 0x35, 0xb3, 0xe6, 0x6e, 0xa8, 0x8f, 0x18, 0x43, 0x2f, 0x23, 0x29, 0xf5,
0xba, 0xe6, 0xca, 0x9c, 0xf1, 0x13, 0x70, 0x9b, 0xf2, 0x85, 0x67, 0xcf, 0xec, 0xf9, 0x68, 0x79,
0x1c, 0xbc, 0xfa, 0xe1, 0x20, 0xdc, 0x1e, 0xc2, 0x1d, 0x18, 0x7f, 0x05, 0x0e, 0x27, 0x95, 0x28,
0x95, 0xd7, 0x9b, 0x59, 0xf3, 0xf1, 0xf2, 0xe3, 0xdb, 0x69, 0x3b, 0x36, 0xc1, 0x8f, 0x06, 0x1a,
0x6e, 0x53, 0xf0, 0x43, 0x70, 0x59, 0x2c, 0xb2, 0xd3, 0x54, 0x5c, 0x30, 0xaf, 0x6f, 0xf8, 0xec,
0x2e, 0xf0, 0x0c, 0x46, 0x09, 0x2d, 0x62, 0xc9, 0x72, 0x5d, 0xc6, 0x73, 0xcc, 0xfb, 0xfe, 0x15,
0x7e, 0x00, 0xce, 0x86, 0x25, 0x09, 0xcd, 0xbc, 0xc1, 0xcc, 0x9a, 0x0f, 0xc3, 0x6d, 0xe4, 0xff,
0x6a, 0x81, 0x53, 0x7f, 0x0a, 0xbb, 0xd0, 0x8f, 0x48, 0xc1, 0x62, 0xd4, 0xc1, 0x23, 0x18, 0xe4,
0x52, 0xac, 0x18, 0xa7, 0xc8, 0xc2, 0x43, 0xe8, 0x29, 0x91, 0x08, 0xd4, 0xc5, 0x03, 0xb0, 0x0b,
0xaa, 0x90, 0x8d, 0xc7, 0x00, 0xa2, 0xa5, 0x8a, 0x7a, 0xf8, 0x08, 0x86, 0xcd, 0x6f, 0x40, 0x7d,
0x9d, 0x60, 0x52, 0x1d, 0x7c, 0x0f, 0xdc, 0x84, 0x14, 0x9b, 0x48, 0x10, 0x99, 0xa0, 0x81, 0xfe,
0x02, 0x4b, 0xc9, 0x9a, 0xa2, 0xa1, 0xce, 0x48, 0x88, 0x22, 0x11, 0x29, 0x28, 0x9a, 0xf8, 0x7f,
0xec, 0x58, 0x9c, 0x40, 0x97, 0x25, 0x66, 0x08, 0xef, 0xd8, 0xa1, 0x2e, 0x4b, 0x5e, 0x3b, 0xa8,
0x1f, 0xe0, 0xbe, 0xa4, 0xbf, 0x94, 0x4c, 0xd2, 0x24, 0x7c, 0x8f, 0x81, 0xdd, 0x4e, 0xf2, 0x2b,
0xb8, 0xdf, 0x04, 0x3f, 0x33, 0xb5, 0xf9, 0x89, 0xf0, 0x92, 0xe2, 0xc7, 0xbb, 0x9f, 0x6c, 0xd8,
0xbe, 0xb9, 0x6a, 0x8b, 0xc5, 0x8f, 0xa0, 0xff, 0x42, 0x17, 0x30, 0x5c, 0x47, 0xcb, 0x07, 0x41,
0x2d, 0xd1, 0xa0, 0x91, 0x68, 0x60, 0xca, 0x87, 0x35, 0xc8, 0xff, 0xcf, 0x81, 0x61, 0x53, 0x44,
0x0b, 0xf4, 0x92, 0x56, 0x8d, 0x40, 0x2f, 0x69, 0x85, 0x9f, 0x80, 0xb3, 0x12, 0x32, 0x25, 0xca,
0x54, 0x1b, 0x2f, 0x67, 0x77, 0x53, 0xf8, 0xde, 0xe0, 0xc2, 0x2d, 0xbe, 0xed, 0x98, 0xbd, 0xd7,
0xb1, 0x2f, 0xe1, 0x28, 0xa1, 0x2b, 0x52, 0x72, 0x65, 0x38, 0x18, 0x99, 0xde, 0xcd, 0xf0, 0x00,
0x8b, 0x4f, 0x01, 0xf4, 0x3c, 0xcf, 0x45, 0x29, 0x63, 0x6a, 0x04, 0x3a, 0x5e, 0x7e, 0x72, 0x37,
0x9b, 0xe0, 0x69, 0x0b, 0x0e, 0xf7, 0x12, 0xf7, 0x64, 0xea, 0xec, 0xcb, 0x14, 0x1f, 0xeb, 0x6e,
0x93, 0xe4, 0x2c, 0xe3, 0xd5, 0x56, 0xc0, 0x6d, 0x8c, 0x27, 0xd0, 0x4f, 0x4b, 0xae, 0x98, 0x37,
0x34, 0x0f, 0x75, 0xa0, 0x57, 0x62, 0x27, 0xd1, 0xc2, 0x73, 0x67, 0xb6, 0x5e, 0x89, 0xbd, 0x2b,
0xfc, 0x0d, 0x40, 0x41, 0x39, 0x8d, 0xd5, 0x53, 0x16, 0x2b, 0xef, 0xc8, 0x28, 0xe3, 0xa3, 0x37,
0x50, 0x3e, 0x33, 0x9b, 0x14, 0xee, 0x25, 0x69, 0x5a, 0x29, 0xb9, 0xfa, 0x4e, 0x94, 0x99, 0xf2,
0xee, 0xcd, 0xac, 0x79, 0x3f, 0x6c, 0xe3, 0x57, 0x77, 0x72, 0x7c, 0x7b, 0x27, 0x1f, 0x43, 0xbf,
0x88, 0x45, 0x4e, 0xbd, 0xc9, 0xdb, 0x86, 0x17, 0x9c, 0x6b, 0x5c, 0x58, 0xc3, 0xb1, 0x07, 0x83,
0x58, 0x52, 0xa2, 0x84, 0xf4, 0x3e, 0x34, 0x55, 0x9b, 0xf0, 0xf8, 0x4f, 0x0b, 0x9c, 0x9a, 0x26,
0x1e, 0xb7, 0x7b, 0xe4, 0x36, 0x2b, 0xa2, 0xe8, 0x95, 0x6a, 0x56, 0x44, 0x9f, 0x75, 0xe7, 0x62,
0xc1, 0x85, 0xdc, 0xaa, 0xa0, 0x0e, 0xf0, 0xd7, 0x0d, 0xad, 0xda, 0xa6, 0x3e, 0x7d, 0x6b, 0x4b,
0x0e, 0xd8, 0xf9, 0x8f, 0xa0, 0x6f, 0x62, 0xbd, 0xed, 0x5c, 0xc4, 0x84, 0xa3, 0xce, 0x81, 0x3f,
0x58, 0x18, 0x1a, 0xd5, 0xa2, 0xae, 0xff, 0xbc, 0x41, 0x03, 0x38, 0xf5, 0x70, 0x50, 0xc7, 0x38,
0x8e, 0x36, 0x16, 0x0b, 0x4f, 0x00, 0x15, 0x54, 0x9d, 0xad, 0x9e, 0x6d, 0xe8, 0x39, 0x49, 0xa9,
0xb1, 0x9b, 0x2e, 0xf6, 0x60, 0x52, 0x63, 0x8b, 0xc3, 0x17, 0x5b, 0x1b, 0x17, 0x67, 0x91, 0x24,
0xb2, 0x42, 0x3d, 0xff, 0x04, 0x60, 0x27, 0x33, 0xfd, 0x94, 0x50, 0x45, 0x18, 0x2f, 0x6a, 0x83,
0x4b, 0xa8, 0x64, 0x2f, 0x68, 0x82, 0x2c, 0x1d, 0x90, 0x38, 0xd6, 0x43, 0x43, 0x5d, 0xff, 0x14,
0xdc, 0x76, 0xf3, 0x0f, 0xcd, 0xde, 0x7a, 0x0f, 0xb3, 0xff, 0xfc, 0x2f, 0x0b, 0xc6, 0x87, 0xab,
0xa7, 0x9b, 0xc0, 0x45, 0xb6, 0xd6, 0x9d, 0x47, 0x1d, 0x6d, 0x8d, 0xc5, 0x46, 0x48, 0x65, 0x42,
0xd3, 0x93, 0xac, 0x4c, 0x23, 0x2a, 0x51, 0x57, 0x9f, 0x0b, 0x45, 0x54, 0x59, 0x20, 0x5b, 0x5b,
0xae, 0x22, 0x6b, 0x34, 0xd2, 0x3d, 0x49, 0x88, 0xd2, 0x66, 0xdb, 0xd8, 0x6b, 0x5f, 0x57, 0x8c,
0x37, 0x34, 0xbe, 0x8c, 0xc4, 0x15, 0x72, 0x34, 0xb4, 0x94, 0xbc, 0xb6, 0x59, 0x9a, 0x12, 0xc6,
0xd1, 0x50, 0x1f, 0xf3, 0x8d, 0xc8, 0x28, 0x72, 0xeb, 0x5b, 0x71, 0xc1, 0x10, 0xec, 0xf5, 0x3a,
0xd1, 0x3c, 0x5a, 0xd6, 0x88, 0x7e, 0xfb, 0xc5, 0xdf, 0xd7, 0x53, 0xeb, 0xe5, 0xf5, 0xd4, 0xfa,
0xf7, 0x7a, 0x6a, 0xfd, 0x7e, 0x33, 0xed, 0xbc, 0xbc, 0x99, 0x76, 0xfe, 0xb9, 0x99, 0x76, 0x9e,
0x7f, 0xf0, 0x9a, 0x3f, 0xe2, 0xc8, 0x31, 0xa6, 0x70, 0xf2, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff,
0x68, 0xdf, 0xe6, 0x99, 0xa6, 0x07, 0x00, 0x00,
}
func (m *ObjectType) Marshal() (dAtA []byte, err error) {
@ -955,6 +967,15 @@ func (m *Relation) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Creator) > 0 {
i -= len(m.Creator)
copy(dAtA[i:], m.Creator)
i = encodeVarintRelation(dAtA, i, uint64(len(m.Creator)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0xaa
}
if m.Scope != 0 {
i = encodeVarintRelation(dAtA, i, uint64(m.Scope))
i--
@ -1294,6 +1315,10 @@ func (m *Relation) Size() (n int) {
if m.Scope != 0 {
n += 2 + sovRelation(uint64(m.Scope))
}
l = len(m.Creator)
if l > 0 {
n += 2 + l + sovRelation(uint64(l))
}
return n
}
@ -2213,6 +2238,38 @@ func (m *Relation) Unmarshal(dAtA []byte) error {
break
}
}
case 21:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRelation
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRelation
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthRelation
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Creator = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRelation(dAtA[iNdEx:])

Some files were not shown because too many files have changed in this diff Show more