1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-08 05:47:07 +09:00

GO-4459 Merge branch 'go-4144-fix-nested-filters' into go-4459-implement-first-stage-of-anytype-rest-api

This commit is contained in:
Jannis Metrikat 2025-01-02 13:29:03 +01:00
commit 1c1b9da344
No known key found for this signature in database
GPG key ID: B223CAC5AAF85615
7 changed files with 180 additions and 104 deletions

View file

@ -4,6 +4,7 @@
package main
import (
"bufio"
"context"
"errors"
"fmt"
@ -13,6 +14,7 @@ import (
_ "net/http/pprof"
"os"
"os/signal"
"runtime"
"strconv"
"syscall"
"time"
@ -225,8 +227,25 @@ func main() {
startReportMemory(mw)
shutdown := func() {
server.Stop()
proxy.Close()
mw.AppShutdown(context.Background(), &pb.RpcAppShutdownRequest{})
}
// do not change this, js client relies on this msg to ensure that server is up and parse address
fmt.Println(grpcWebStartedMessagePrefix + webaddr)
if runtime.GOOS == "windows" {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
message := scanner.Text()
if message == "shutdown" {
fmt.Println("[anytype-heart] Shutdown: received shutdown msg, closing components...")
// Perform cleanup or exit
shutdown()
return
}
}
}
// run rest api server
var mwInternal core.MiddlewareInternal = mw
@ -242,9 +261,8 @@ func main() {
}
continue
}
server.Stop()
proxy.Close()
mw.AppShutdown(context.Background(), &pb.RpcAppShutdownRequest{})
fmt.Printf("[anytype-heart] Shutdown: received OS signal (%s), closing components...\n", sig.String())
shutdown()
return
}
}

View file

@ -274,7 +274,6 @@ func Bootstrap(a *app.App, components ...app.Component) {
Register(recordsbatcher.New()).
Register(configfetcher.New()).
Register(process.New()).
Register(core.New()).
Register(core.NewTempDirService()).
Register(treemanager.New()).
Register(block.New()).

View file

@ -39,7 +39,14 @@ func New() *Middleware {
}
func (mw *Middleware) AppShutdown(cctx context.Context, request *pb.RpcAppShutdownRequest) *pb.RpcAppShutdownResponse {
mw.applicationService.Stop()
err := mw.applicationService.Stop()
// using fmt.Println instead of log because we want it only in stdout
if err != nil {
fmt.Println("[anytype-heart] Shutdown: error during closing components: ", err)
} else {
fmt.Println("[anytype-heart] Shutdown: graceful shutdown finished")
}
// intentionally do not pass the error to the client
return &pb.RpcAppShutdownResponse{
Error: &pb.RpcAppShutdownResponseError{
Code: pb.RpcAppShutdownResponseError_NULL,

View file

@ -1,99 +0,0 @@
package core
import (
"context"
"fmt"
"sync"
"github.com/anyproto/any-sync/app"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
)
var log = logging.Logger("anytype-core")
const (
CName = "anytype"
)
type Service interface {
Stop() error
IsStarted() bool
app.ComponentRunnable
}
var _ app.Component = (*Anytype)(nil)
var _ Service = (*Anytype)(nil)
type Anytype struct {
lock sync.RWMutex
isStarted bool // use under the lock
shutdownStartsCh chan struct {
} // closed when node shutdown starts
}
func New() *Anytype {
return &Anytype{
shutdownStartsCh: make(chan struct{}),
}
}
func (a *Anytype) Init(ap *app.App) (err error) {
return
}
func (a *Anytype) Name() string {
return CName
}
func (a *Anytype) Run(ctx context.Context) (err error) {
a.start()
return nil
}
// TODO: refactor to call tech space
func (a *Anytype) IsStarted() bool {
return true
a.lock.Lock()
defer a.lock.Unlock()
return a.isStarted
}
func (a *Anytype) HandlePeerFound(p peer.AddrInfo) {
// TODO: [MR] mdns
}
func (a *Anytype) start() {
a.lock.Lock()
defer a.lock.Unlock()
if a.isStarted {
return
}
a.isStarted = true
}
func (a *Anytype) Close(ctx context.Context) (err error) {
metrics.Service.Close()
return a.Stop()
}
func (a *Anytype) Stop() error {
fmt.Printf("stopping the library...\n")
defer fmt.Println("library has been successfully stopped")
a.lock.Lock()
defer a.lock.Unlock()
a.isStarted = false
if a.shutdownStartsCh != nil {
close(a.shutdownStartsCh)
}
return nil
}

View file

@ -8,8 +8,11 @@ import (
"github.com/anyproto/any-sync/app"
"github.com/anyproto/anytype-heart/core/wallet"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
)
var log = logging.Logger("anytype-core")
const tmpDir = "tmp"
type TempDirProvider interface {

View file

@ -190,6 +190,7 @@ func FiltersFromProto(filters []*model.BlockContentDataviewFilter) []FilterReque
QuickOption: f.QuickOption,
Format: f.Format,
IncludeTime: f.IncludeTime,
NestedFilters: FiltersFromProto(f.NestedFilters),
})
}
return res

View file

@ -361,3 +361,150 @@ func Test_NewFilters(t *testing.T) {
assert.Len(t, filters.FilterObj.(FiltersAnd)[0].(FiltersOr), 2)
})
}
func TestFiltersFromProto(t *testing.T) {
t.Run("no filters", func(t *testing.T) {
// given
var protoFilters []*model.BlockContentDataviewFilter
// when
result := FiltersFromProto(protoFilters)
// then
assert.NotNil(t, result)
assert.Len(t, result, 0)
})
t.Run("single filter without nesting", func(t *testing.T) {
// given
protoFilters := []*model.BlockContentDataviewFilter{
{
Id: "filter1",
Operator: model.BlockContentDataviewFilter_No,
RelationKey: "relationKey1",
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.String("value1").ToProto(),
Format: model.RelationFormat_shorttext,
},
}
// when
result := FiltersFromProto(protoFilters)
// then
assert.Len(t, result, 1)
assert.Equal(t, "filter1", result[0].Id)
assert.Equal(t, domain.RelationKey("relationKey1"), result[0].RelationKey)
assert.Equal(t, model.BlockContentDataviewFilter_Equal, result[0].Condition)
assert.Equal(t, domain.String("value1"), result[0].Value)
assert.Equal(t, model.RelationFormat_shorttext, result[0].Format)
assert.Empty(t, result[0].NestedFilters)
})
t.Run("nested filters", func(t *testing.T) {
// given
protoFilters := []*model.BlockContentDataviewFilter{
{
Id: "filter1",
Operator: model.BlockContentDataviewFilter_And,
NestedFilters: []*model.BlockContentDataviewFilter{
{
Id: "nestedFilter1",
Operator: model.BlockContentDataviewFilter_No,
RelationKey: "relationKey2",
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: domain.String("value2").ToProto(),
Format: model.RelationFormat_date,
},
{
Id: "nestedFilter2",
Operator: model.BlockContentDataviewFilter_No,
RelationKey: "relationKey3",
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.String("value3").ToProto(),
Format: model.RelationFormat_status,
},
},
},
}
// when
result := FiltersFromProto(protoFilters)
// then
assert.Len(t, result, 1)
assert.Equal(t, "filter1", result[0].Id)
assert.NotNil(t, result[0].NestedFilters)
nested := result[0].NestedFilters
assert.Len(t, nested, 2)
assert.Equal(t, "nestedFilter1", nested[0].Id)
assert.Equal(t, domain.RelationKey("relationKey2"), nested[0].RelationKey)
assert.Equal(t, model.BlockContentDataviewFilter_NotEqual, nested[0].Condition)
assert.Equal(t, domain.String("value2"), nested[0].Value)
assert.Equal(t, model.RelationFormat_date, nested[0].Format)
assert.Equal(t, "nestedFilter2", nested[1].Id)
assert.Equal(t, domain.RelationKey("relationKey3"), nested[1].RelationKey)
assert.Equal(t, model.BlockContentDataviewFilter_Equal, nested[1].Condition)
assert.Equal(t, domain.String("value3"), nested[1].Value)
assert.Equal(t, model.RelationFormat_status, nested[1].Format)
})
t.Run("deeply nested filters", func(t *testing.T) {
// given
protoFilters := []*model.BlockContentDataviewFilter{
{
Id: "filter1",
Operator: model.BlockContentDataviewFilter_And,
NestedFilters: []*model.BlockContentDataviewFilter{
{
Id: "nestedFilter1",
Operator: model.BlockContentDataviewFilter_Or,
NestedFilters: []*model.BlockContentDataviewFilter{
{
Id: "deepNestedFilter1",
Operator: model.BlockContentDataviewFilter_No,
RelationKey: "relationKey3",
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.String("value3").ToProto(),
Format: model.RelationFormat_status,
},
{
Id: "deepNestedFilter2",
Operator: model.BlockContentDataviewFilter_No,
RelationKey: "relationKey4",
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: domain.String("value4").ToProto(),
Format: model.RelationFormat_shorttext,
},
},
},
},
},
}
// when
result := FiltersFromProto(protoFilters)
// then
assert.Len(t, result, 1)
assert.NotNil(t, result[0].NestedFilters)
nested := result[0].NestedFilters
assert.Len(t, nested, 1)
deepNested := nested[0].NestedFilters
assert.Len(t, deepNested, 2)
assert.Equal(t, "deepNestedFilter1", deepNested[0].Id)
assert.Equal(t, domain.RelationKey("relationKey3"), deepNested[0].RelationKey)
assert.Equal(t, model.BlockContentDataviewFilter_Equal, deepNested[0].Condition)
assert.Equal(t, domain.String("value3"), deepNested[0].Value)
assert.Equal(t, model.RelationFormat_status, deepNested[0].Format)
assert.Equal(t, "deepNestedFilter2", deepNested[1].Id)
assert.Equal(t, domain.RelationKey("relationKey4"), deepNested[1].RelationKey)
assert.Equal(t, model.BlockContentDataviewFilter_NotEqual, deepNested[1].Condition)
assert.Equal(t, domain.String("value4"), deepNested[1].Value)
assert.Equal(t, model.RelationFormat_shorttext, deepNested[1].Format)
})
}