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

GO-5143 Merge branch 'go-4932-type-dataview-widget' into go-5143-sync-rellinks-on-view-relations-update

This commit is contained in:
kirillston 2025-03-11 17:25:43 +03:00
commit bd6fbef0a8
No known key found for this signature in database
GPG key ID: BE4BF014F0ECDFE8
69 changed files with 4906 additions and 2748 deletions

34
.github/scripts/is_runner_busy.sh vendored Executable file
View file

@ -0,0 +1,34 @@
#!/bin/bash
# The script checks whether a runner with a specific label is busy
OWNER_REPO=$1
CHECK_LABELS=$2
if [[ -z $OWNER_REPO ]]; then
cat <<EOF 1>&2
Usage: $0 OWNER/REPO CHECK_LABELS
example: $0 anyproto/test-concurrency "self-hosted ubuntu-latest"
EOF
exit 1
fi
EXIT_CODE=0
# get current runners id
# gh api repos/anyproto/test-concurrency/actions/runs --jq '.workflow_runs[] | select(.status!="completed") | {id, name, status, created_at, html_url}'
for RUN_ID in $(gh api repos/${OWNER_REPO}/actions/runs --jq '.workflow_runs[] | select(.status!="completed") | .id'); do
# get runner_name
LABELS=$(gh api repos/${OWNER_REPO}/actions/runs/${RUN_ID}/jobs --jq '[.jobs[].labels[]] | unique | .[]')
for CHECK_LABEL in $CHECK_LABELS; do
if echo "$LABELS" | grep -q "$CHECK_LABEL"; then
echo "A run='$RUN_ID' is executing on a runner with LABEL='$CHECK_LABEL' in the repository='$OWNER_REPO'" 1>&2
EXIT_CODE=$(( EXIT_CODE + 1 ))
else
continue
fi
done
done
exit $EXIT_CODE

View file

@ -22,7 +22,11 @@ permissions:
jobs:
wait_for_perftest:
uses: ./.github/workflows/reusable_wait_for_perftest.yml
build:
needs: wait_for_perftest
runs-on: ${{ github.event_name == 'push' && 'mac-mini-org-heart' || (github.event.inputs.run-on-runner || 'mac-mini-org-heart') }}
steps:
- name: validate agent

View file

@ -29,7 +29,11 @@ permissions:
contents: 'write'
jobs:
wait_for_perftest:
uses: ./.github/workflows/reusable_wait_for_perftest.yml
build:
needs: wait_for_perftest
runs-on: ${{ github.event.inputs.run-on-runner }}
steps:
- name: Install Go

View file

@ -26,7 +26,11 @@ permissions:
jobs:
wait_for_self_hosted_mac_mini:
uses: ./.github/workflows/reusable_wait_for_self_hosted_mac_mini.yml
perftests-grafana:
needs: wait_for_self_hosted_mac_mini
runs-on: ${{ github.event.inputs.run-on-runner || 'mac-mini-org-heart' }}
steps:
- name: Install Go

View file

@ -31,7 +31,11 @@ permissions:
jobs:
wait_for_self_hosted_mac_mini:
uses: ./.github/workflows/reusable_wait_for_self_hosted_mac_mini.yml
perftests-macos:
needs: wait_for_self_hosted_mac_mini
timeout-minutes: 60
runs-on: 'mac-mini-org-heart'
steps:

View file

@ -0,0 +1,22 @@
name: Reusable Workflow wait for perftests to finish
on:
workflow_call:
jobs:
wait_for_perftest:
runs-on: ubuntu-latest
steps:
- name: Wait for perftests to finish
run: |
while true; do
RUNNING=$(gh run list --repo $GITHUB_REPOSITORY --workflow perftests.yml --workflow perftests-grafana.yml --status in_progress --json status --jq 'length')
if [[ "$RUNNING" -eq 0 ]]; then
echo "perftests is finished, proceeding with build."
break
fi
echo "perftests is still running. Waiting 10 seconds..."
sleep 10
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -0,0 +1,26 @@
name: Reusable Workflow wait for self-hosted mac mini is free
on:
workflow_call:
jobs:
wait_for_self_hosted_mac_mini:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Wait for self-hosted runners to be free
run: |
while true; do
if .github/scripts/is_runner_busy.sh $GITHUB_REPOSITORY mac-mini-org-heart; then
echo "self-hosted runner 'mac-mini-org-heart' is free"
break
else
echo "self-hosted runner 'mac-mini-org-heart' is busy. waiting 10 seconds..."
sleep 10
continue
fi
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -16,7 +16,11 @@ permissions:
pull-requests: write
jobs:
wait_for_perftest:
uses: ./.github/workflows/reusable_wait_for_perftest.yml
unit-test:
needs: wait_for_perftest
runs-on: ${{ vars.RUNNER_TEST }}
env:
GOPRIVATE: github.com/anyproto

View file

@ -25,18 +25,18 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func init() { proto.RegisterFile("pb/protos/service/service.proto", fileDescriptor_93a29dc403579097) }
var fileDescriptor_93a29dc403579097 = []byte{
// 5514 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x5f, 0x6f, 0x24, 0x49,
0x52, 0xc0, 0xb7, 0x5f, 0x58, 0xa8, 0xe3, 0x16, 0xe8, 0x85, 0x65, 0x6f, 0xb9, 0x9b, 0x99, 0x9d,
0x9d, 0xb1, 0x3d, 0x63, 0xbb, 0x3d, 0x3b, 0xb3, 0xff, 0xb8, 0x43, 0x82, 0x1e, 0x7b, 0xec, 0xf5,
0x9d, 0xed, 0x35, 0xee, 0xf6, 0x8c, 0xb4, 0x12, 0x12, 0xe5, 0xaa, 0x74, 0xbb, 0x70, 0x75, 0x65,
0x5d, 0x55, 0x76, 0x7b, 0xfa, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x9c, 0xf8, 0x27, 0x78, 0x42,
0x42, 0x7c, 0x00, 0x3e, 0x06, 0x8f, 0xf7, 0xc8, 0x23, 0xda, 0xfd, 0x0a, 0x7c, 0x00, 0x54, 0xf9,
0x3f, 0xa3, 0x32, 0xb2, 0xca, 0xcb, 0xd3, 0x8c, 0x1c, 0xbf, 0x88, 0xc8, 0xac, 0x8c, 0xcc, 0x8c,
0xcc, 0xca, 0xca, 0x8e, 0xee, 0x96, 0x17, 0x3b, 0x65, 0x45, 0x19, 0xad, 0x77, 0x6a, 0x52, 0x2d,
// 5536 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x4f, 0x6f, 0x24, 0x49,
0x56, 0xc0, 0xa7, 0x2e, 0x0c, 0xe4, 0xb2, 0x03, 0xd4, 0xc0, 0x30, 0x3b, 0xec, 0x76, 0xf7, 0xf4,
0x74, 0xdb, 0xee, 0xb6, 0x5d, 0xee, 0xe9, 0x9e, 0x7f, 0xec, 0x22, 0x41, 0xb5, 0xdd, 0xf6, 0x78,
0xd7, 0xf6, 0x18, 0x57, 0xb9, 0x5b, 0x1a, 0x09, 0x89, 0x74, 0x66, 0xb8, 0x9c, 0x38, 0x2b, 0x23,
0x37, 0x33, 0xaa, 0xdc, 0xb5, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x56, 0xfc, 0x13, 0x9c, 0x90,
0xf8, 0x04, 0x1c, 0xf8, 0x10, 0x1c, 0xf7, 0xc8, 0x11, 0xcd, 0x7c, 0x06, 0xee, 0x28, 0xe3, 0x7f,
0xbc, 0x8c, 0x17, 0x99, 0x1e, 0x4e, 0xdd, 0xf2, 0xfb, 0xbd, 0xf7, 0x22, 0x32, 0x5e, 0x44, 0xbc,
0x88, 0x8c, 0x8c, 0x8a, 0xee, 0x96, 0x17, 0x3b, 0x65, 0x45, 0x19, 0xad, 0x77, 0x6a, 0x52, 0x2d,
0xb3, 0x84, 0xa8, 0x7f, 0x47, 0xfc, 0xcf, 0xc3, 0x37, 0xe3, 0x62, 0xc5, 0x56, 0x25, 0x79, 0xef,
0x5d, 0x43, 0x26, 0x74, 0x3e, 0x8f, 0x8b, 0xb4, 0x16, 0xc8, 0x7b, 0xef, 0x18, 0x09, 0x59, 0x92,
0x82, 0xc9, 0xbf, 0x3f, 0xfd, 0x8f, 0xff, 0x1d, 0x44, 0x6f, 0xed, 0xe6, 0x19, 0x29, 0xd8, 0xae,
0x82, 0xc9, 0xbf, 0x3f, 0xfd, 0xcf, 0xff, 0x1d, 0x44, 0x6f, 0xed, 0xe6, 0x19, 0x29, 0xd8, 0xae,
0xd4, 0x18, 0x7e, 0x19, 0x7d, 0x7b, 0x5c, 0x96, 0x07, 0x84, 0xbd, 0x24, 0x55, 0x9d, 0xd1, 0x62,
0xf8, 0xc1, 0x48, 0x3a, 0x18, 0x9d, 0x95, 0xc9, 0x68, 0x5c, 0x96, 0x23, 0x23, 0x1c, 0x9d, 0x91,
0x1f, 0x2f, 0x48, 0xcd, 0xde, 0x7b, 0x10, 0x86, 0xea, 0x92, 0x16, 0x35, 0x19, 0x5e, 0x46, 0xbf,
@ -179,7 +179,7 @@ var fileDescriptor_93a29dc403579097 = []byte{
0xb4, 0xf4, 0xfa, 0x47, 0xd1, 0xbb, 0x6d, 0xaf, 0x72, 0x22, 0xda, 0xe9, 0x34, 0x05, 0xe6, 0xa2,
0x27, 0xfd, 0x15, 0xa4, 0xfb, 0x7f, 0xd1, 0xfb, 0xd2, 0xc2, 0x7f, 0x42, 0xe7, 0x73, 0x52, 0xa4,
0x24, 0x55, 0x1a, 0x75, 0xb3, 0x7e, 0xfa, 0x0c, 0xb7, 0xab, 0x15, 0x46, 0xb6, 0x86, 0x2e, 0xd1,
0x6f, 0x7e, 0x03, 0x4d, 0x59, 0xb4, 0xff, 0x1c, 0x44, 0x8f, 0xbc, 0x45, 0x53, 0x81, 0xeb, 0x14,
0x6f, 0x7e, 0x03, 0x4d, 0x59, 0xb4, 0xff, 0x18, 0x44, 0x8f, 0xbc, 0x45, 0x53, 0x81, 0xeb, 0x14,
0xf1, 0x77, 0xfa, 0x38, 0xf2, 0x69, 0xea, 0xa2, 0x8e, 0xff, 0x1f, 0x16, 0x64, 0x91, 0xff, 0x75,
0x10, 0xdd, 0x37, 0x8a, 0x4d, 0x78, 0xef, 0xd2, 0xe2, 0x32, 0xcf, 0x12, 0xc6, 0xdf, 0xc8, 0x4a,
0x15, 0xfc, 0x71, 0x62, 0x1a, 0xdd, 0x8f, 0x33, 0xa0, 0x69, 0x16, 0xaf, 0x9f, 0x67, 0x35, 0xa3,
@ -365,12 +365,13 @@ var fileDescriptor_93a29dc403579097 = []byte{
0xf5, 0x24, 0xe0, 0x65, 0x53, 0x5c, 0xd9, 0x12, 0x23, 0xdf, 0xbd, 0x79, 0x30, 0xb3, 0x4e, 0x00,
0x1e, 0x9e, 0xaf, 0x0e, 0x53, 0xb8, 0x4e, 0x80, 0xfa, 0x9c, 0x41, 0xd6, 0x09, 0x18, 0xeb, 0x36,
0x9d, 0xde, 0xf7, 0x3a, 0x8a, 0x6b, 0x53, 0x39, 0x4f, 0xd3, 0x79, 0xc1, 0x50, 0xd3, 0x61, 0x0a,
0xee, 0x23, 0xb5, 0xb7, 0xd6, 0x3c, 0x8f, 0xd4, 0xb7, 0xaf, 0xb6, 0xd6, 0x85, 0x99, 0x71, 0x49,
0xaf, 0x27, 0xf9, 0x91, 0x25, 0xff, 0x6f, 0x35, 0x08, 0x21, 0x32, 0x2e, 0xb5, 0x20, 0x61, 0xfb,
0xf9, 0xfb, 0xff, 0xf5, 0xd5, 0x9d, 0xc1, 0xcf, 0xbe, 0xba, 0x33, 0xf8, 0x9f, 0xaf, 0xee, 0x0c,
0x7e, 0xfa, 0xf5, 0x9d, 0x37, 0x7e, 0xf6, 0xf5, 0x9d, 0x37, 0xfe, 0xfb, 0xeb, 0x3b, 0x6f, 0x7c,
0xf9, 0xa6, 0xfc, 0x2d, 0xea, 0x8b, 0x9f, 0xe3, 0xbf, 0x28, 0xfd, 0xec, 0xff, 0x02, 0x00, 0x00,
0xff, 0xff, 0xc9, 0x60, 0xbd, 0x6d, 0xaf, 0x7a, 0x00, 0x00,
0xee, 0x23, 0xb5, 0xb7, 0xd6, 0x3c, 0x8f, 0xd4, 0xb7, 0xaf, 0xb6, 0xd6, 0x85, 0x59, 0x89, 0x8f,
0x53, 0xc5, 0x29, 0x95, 0xc5, 0x90, 0xdf, 0x35, 0xd6, 0x20, 0xf1, 0x71, 0x8b, 0xdd, 0xa2, 0x91,
0xc4, 0xa7, 0x5b, 0xcb, 0x8c, 0x93, 0x7a, 0x7d, 0xcb, 0x8f, 0x50, 0xf9, 0x7f, 0x3b, 0x42, 0x08,
0x91, 0x71, 0xb2, 0x05, 0x09, 0xdb, 0xcf, 0xdf, 0xff, 0xaf, 0xaf, 0xee, 0x0c, 0x7e, 0xf6, 0xd5,
0x9d, 0xc1, 0xff, 0x7c, 0x75, 0x67, 0xf0, 0xd3, 0xaf, 0xef, 0xbc, 0xf1, 0xb3, 0xaf, 0xef, 0xbc,
0xf1, 0xdf, 0x5f, 0xdf, 0x79, 0xe3, 0xcb, 0x37, 0xe5, 0x6f, 0x63, 0x5f, 0xfc, 0x1c, 0xff, 0x85,
0xeb, 0x67, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x94, 0x61, 0xee, 0x3f, 0x7b, 0x00, 0x00,
}
// This is a compile-time assertion to ensure that this generated file
@ -723,6 +724,7 @@ type ClientCommandsHandler interface {
ChatGetMessagesByIds(context.Context, *pb.RpcChatGetMessagesByIdsRequest) *pb.RpcChatGetMessagesByIdsResponse
ChatSubscribeLastMessages(context.Context, *pb.RpcChatSubscribeLastMessagesRequest) *pb.RpcChatSubscribeLastMessagesResponse
ChatUnsubscribe(context.Context, *pb.RpcChatUnsubscribeRequest) *pb.RpcChatUnsubscribeResponse
ChatSubscribeToMessagePreviews(context.Context, *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse
ObjectChatAdd(context.Context, *pb.RpcObjectChatAddRequest) *pb.RpcObjectChatAddResponse
}
@ -6310,6 +6312,26 @@ func ChatUnsubscribe(b []byte) (resp []byte) {
return resp
}
func ChatSubscribeToMessagePreviews(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
if r := recover(); r != nil {
resp, _ = (&pb.RpcChatSubscribeToMessagePreviewsResponse{Error: &pb.RpcChatSubscribeToMessagePreviewsResponseError{Code: pb.RpcChatSubscribeToMessagePreviewsResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
PanicHandler(r)
}
}
}()
in := new(pb.RpcChatSubscribeToMessagePreviewsRequest)
if err := in.Unmarshal(b); err != nil {
resp, _ = (&pb.RpcChatSubscribeToMessagePreviewsResponse{Error: &pb.RpcChatSubscribeToMessagePreviewsResponseError{Code: pb.RpcChatSubscribeToMessagePreviewsResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
return resp
}
resp, _ = clientCommandsHandler.ChatSubscribeToMessagePreviews(context.Background(), in).Marshal()
return resp
}
func ObjectChatAdd(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
@ -6894,6 +6916,8 @@ func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
cd = ChatSubscribeLastMessages(data)
case "ChatUnsubscribe":
cd = ChatUnsubscribe(data)
case "ChatSubscribeToMessagePreviews":
cd = ChatSubscribeToMessagePreviews(data)
case "ObjectChatAdd":
cd = ObjectChatAdd(data)
default:
@ -10824,6 +10848,20 @@ func (h *ClientCommandsHandlerProxy) ChatUnsubscribe(ctx context.Context, req *p
call, _ := actualCall(ctx, req)
return call.(*pb.RpcChatUnsubscribeResponse)
}
func (h *ClientCommandsHandlerProxy) ChatSubscribeToMessagePreviews(ctx context.Context, req *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.ChatSubscribeToMessagePreviews(ctx, req.(*pb.RpcChatSubscribeToMessagePreviewsRequest)), nil
}
for _, interceptor := range h.interceptors {
toCall := actualCall
currentInterceptor := interceptor
actualCall = func(ctx context.Context, req any) (any, error) {
return currentInterceptor(ctx, req, "ChatSubscribeToMessagePreviews", toCall)
}
}
call, _ := actualCall(ctx, req)
return call.(*pb.RpcChatSubscribeToMessagePreviewsResponse)
}
func (h *ClientCommandsHandlerProxy) ObjectChatAdd(ctx context.Context, req *pb.RpcObjectChatAddRequest) *pb.RpcObjectChatAddResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.ObjectChatAdd(ctx, req.(*pb.RpcObjectChatAddRequest)), nil

View file

@ -53,6 +53,7 @@ func main() {
if err != nil {
log.Fatal("can't import the tree:", err)
}
defer res.Store.Close()
objectTree, err := res.CreateReadableTree(*fromRoot, "")
if err != nil {
log.Fatal("can't create readable tree:", err)

View file

@ -47,17 +47,21 @@ type (
}
useCaseInfo struct {
objects map[string]objectInfo
relations map[string]domain.RelationKey
types map[string]domain.TypeKey
templates map[string]string
options map[string]domain.RelationKey
files []string
objects map[string]objectInfo
relations map[string]domain.RelationKey
types map[string]domain.TypeKey
templates map[string]string
options map[string]domain.RelationKey
fileObjects []string
// big data
files map[string][]byte
snapshots map[string]*pb.SnapshotWithType
profile []byte
customTypesAndRelations map[string]customInfo
useCase string
profileFileFound bool
useCase string
}
cliFlags struct {
@ -68,12 +72,14 @@ type (
}
)
func (i customInfo) GetFormat() model.RelationFormat {
return i.relationFormat
}
func (f cliFlags) isUpdateNeeded() bool {
return f.analytics || f.creator || f.removeRelations || f.exclude || f.rules != ""
}
const anytypeProfileFilename = addr.AnytypeProfileId + ".pb"
var (
errIncorrectFileFound = fmt.Errorf("incorrect protobuf file was found")
errValidationFailed = fmt.Errorf("validation failed")
@ -111,7 +117,7 @@ func run() error {
if err != nil {
return err
}
if !info.profileFileFound {
if info.profile == nil {
fmt.Println("profile file does not present in archive")
}
@ -129,7 +135,7 @@ func run() error {
defer writer.Close()
}
err = processFiles(r.File, writer, info, flags, updateNeeded)
err = processFiles(info, writer, flags)
if flags.list {
listObjects(info)
@ -199,17 +205,13 @@ func collectUseCaseInfo(files []*zip.File, fileName string) (info *useCaseInfo,
types: make(map[string]domain.TypeKey, len(files)-1),
templates: make(map[string]string),
options: make(map[string]domain.RelationKey),
files: make([]string, 0),
files: make(map[string][]byte),
snapshots: make(map[string]*pb.SnapshotWithType, len(files)),
fileObjects: make([]string, 0),
customTypesAndRelations: make(map[string]customInfo),
profileFileFound: false,
}
for _, f := range files {
if f.Name == constant.ProfileFile {
info.profileFileFound = true
continue
}
if (strings.HasPrefix(f.Name, export.Files) && !strings.HasPrefix(f.Name, export.FilesObjects)) || f.FileInfo().IsDir() {
if f.FileInfo().IsDir() {
continue
}
@ -218,7 +220,17 @@ func collectUseCaseInfo(files []*zip.File, fileName string) (info *useCaseInfo,
return nil, err
}
snapshot, _, err := extractSnapshotAndType(data, f.Name)
if isPlainFile(f.Name) {
info.files[f.Name] = data
continue
}
if f.Name == constant.ProfileFile {
info.profile = data
continue
}
snapshot, err := extractSnapshotAndType(data, f.Name)
if err != nil {
return nil, fmt.Errorf("failed to extract snapshot from file %s: %w", f.Name, err)
}
@ -232,6 +244,8 @@ func collectUseCaseInfo(files []*zip.File, fileName string) (info *useCaseInfo,
SbType: smartblock.SmartBlockType(snapshot.SbType),
}
info.snapshots[f.Name] = snapshot
switch snapshot.SbType {
case model.SmartBlockType_STRelation:
uk := pbtypes.GetString(snapshot.Snapshot.Data.Details, bundle.RelationKeyUniqueKey.String())
@ -268,7 +282,7 @@ func collectUseCaseInfo(files []*zip.File, fileName string) (info *useCaseInfo,
case model.SmartBlockType_STRelationOption:
info.options[id] = domain.RelationKey(pbtypes.GetString(snapshot.Snapshot.Data.Details, bundle.RelationKeyRelationKey.String()))
case model.SmartBlockType_FileObject:
info.files = append(info.files, id)
info.fileObjects = append(info.fileObjects, id)
}
}
return
@ -287,46 +301,47 @@ func readData(f *zip.File) ([]byte, error) {
return data, nil
}
func processFiles(files []*zip.File, zw *zip.Writer, info *useCaseInfo, flags *cliFlags, writeNewFile bool) error {
var incorrectFileFound bool
for _, f := range files {
if f.Name == anytypeProfileFilename {
fmt.Println(anytypeProfileFilename, "is excluded")
continue
}
data, err := readData(f)
func processFiles(info *useCaseInfo, zw *zip.Writer, flags *cliFlags) error {
var (
incorrectFileFound bool
writeNewFile = flags.isUpdateNeeded()
)
if info.profile != nil {
data, err := processProfile(info, flags.spaceDashboardId)
if err != nil {
return err
}
var newData []byte
if f.FileInfo().IsDir() {
newData = data
} else {
newData, err = processRawData(data, f.Name, info, flags)
if err != nil {
if !(flags.exclude && errors.Is(err, errValidationFailed)) {
// just do not include object that failed validation
incorrectFileFound = true
}
continue
if writeNewFile {
if err = saveDataToZip(zw, constant.ProfileFile, data); err != nil {
return err
}
}
}
if writeNewFile {
for name, data := range info.files {
if err := saveDataToZip(zw, name, data); err != nil {
return err
}
}
}
for name, sn := range info.snapshots {
newData, err := processSnapshot(sn, info, flags)
if err != nil {
if !(flags.exclude && errors.Is(err, errValidationFailed)) {
// just do not include object that failed validation
incorrectFileFound = true
}
continue
}
if newData == nil || !writeNewFile {
continue
}
newFileName := f.Name
if strings.HasSuffix(newFileName, ".pb.json") {
// output of usecase validator is always an archive with protobufs
newFileName = strings.TrimSuffix(newFileName, ".json")
}
nf, err := zw.Create(newFileName)
if err != nil {
return fmt.Errorf("failed to create new file %s: %w", newFileName, err)
}
if _, err = io.Copy(nf, bytes.NewReader(newData)); err != nil {
return fmt.Errorf("failed to copy snapshot to new file %s: %w", newFileName, err)
if err = saveDataToZip(zw, name, newData); err != nil {
return err
}
}
@ -336,38 +351,40 @@ func processFiles(files []*zip.File, zw *zip.Writer, info *useCaseInfo, flags *c
return nil
}
func processRawData(data []byte, name string, info *useCaseInfo, flags *cliFlags) ([]byte, error) {
if name == constant.ProfileFile {
return processProfile(data, info, flags.spaceDashboardId)
func saveDataToZip(zw *zip.Writer, fileName string, data []byte) error {
if strings.HasSuffix(fileName, ".pb.json") {
// output of usecase validator is always an archive with protobufs
fileName = strings.TrimSuffix(fileName, ".json")
}
if strings.HasPrefix(name, "files") {
return data, nil
}
snapshot, isOldAccount, err := extractSnapshotAndType(data, name)
nf, err := zw.Create(fileName)
if err != nil {
return nil, err
return fmt.Errorf("failed to create new file %s: %w", fileName, err)
}
if _, err = io.Copy(nf, bytes.NewReader(data)); err != nil {
return fmt.Errorf("failed to copy snapshot to new file %s: %w", fileName, err)
}
return nil
}
func processSnapshot(s *pb.SnapshotWithType, info *useCaseInfo, flags *cliFlags) ([]byte, error) {
if flags.analytics {
insertAnalyticsData(snapshot.Snapshot, info)
insertAnalyticsData(s.Snapshot, info)
}
if flags.removeRelations {
removeAccountRelatedDetails(snapshot.Snapshot)
removeAccountRelatedDetails(s.Snapshot)
}
if flags.creator {
insertCreatorInfo(snapshot.Snapshot)
insertCreatorInfo(s.Snapshot)
}
if flags.rules != "" {
processRules(snapshot.Snapshot)
processRules(s.Snapshot)
}
if flags.validate {
if err = validate(snapshot, info); err != nil {
if err := validate(s, info); err != nil {
if errors.Is(err, errSkipObject) {
// some validators register errors mentioning that object can be excluded
return nil, nil
@ -378,51 +395,49 @@ func processRawData(data []byte, name string, info *useCaseInfo, flags *cliFlags
}
if flags.collectCustomUsageInfo {
collectCustomObjectsUsageInfo(snapshot, info)
collectCustomObjectsUsageInfo(s, info)
}
if isOldAccount {
return snapshot.Snapshot.Marshal()
if s.SbType == model.SmartBlockType_AccountOld {
return s.Snapshot.Marshal()
}
return snapshot.Marshal()
return s.Marshal()
}
func extractSnapshotAndType(data []byte, name string) (s *pb.SnapshotWithType, isOldAccount bool, err error) {
func extractSnapshotAndType(data []byte, name string) (s *pb.SnapshotWithType, err error) {
s = &pb.SnapshotWithType{}
if strings.HasSuffix(name, ".json") {
if err = jsonpb.UnmarshalString(string(data), s); err != nil {
return nil, false, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
return nil, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
}
if s.SbType == model.SmartBlockType_AccountOld {
cs := &pb.ChangeSnapshot{}
isOldAccount = true
if err = jsonpb.UnmarshalString(string(data), cs); err != nil {
return nil, false, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
return nil, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
}
s = &pb.SnapshotWithType{
Snapshot: cs,
SbType: model.SmartBlockType_Page,
SbType: model.SmartBlockType_AccountOld,
}
}
return
}
if err = s.Unmarshal(data); err != nil {
return nil, false, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
return nil, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
}
if s.SbType == model.SmartBlockType_AccountOld {
cs := &pb.ChangeSnapshot{}
isOldAccount = true
if err = cs.Unmarshal(data); err != nil {
return nil, false, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
return nil, fmt.Errorf("cannot unmarshal snapshot from file %s: %w", name, err)
}
s = &pb.SnapshotWithType{
Snapshot: cs,
SbType: model.SmartBlockType_Page,
SbType: model.SmartBlockType_AccountOld,
}
}
return s, isOldAccount, nil
return s, nil
}
func validate(snapshot *pb.SnapshotWithType, info *useCaseInfo) (err error) {
@ -478,7 +493,8 @@ func removeAccountRelatedDetails(s *pb.ChangeSnapshot) {
bundle.RelationKeyAddedDate.String(),
bundle.RelationKeySyncDate.String(),
bundle.RelationKeySyncError.String(),
bundle.RelationKeySyncStatus.String():
bundle.RelationKeySyncStatus.String(),
bundle.RelationKeyChatId.String():
delete(s.Data.Details.Fields, key)
}
@ -490,9 +506,9 @@ func insertCreatorInfo(s *pb.ChangeSnapshot) {
s.Data.Details.Fields[bundle.RelationKeyLastModifiedBy.String()] = pbtypes.String(addr.AnytypeProfileId)
}
func processProfile(data []byte, info *useCaseInfo, spaceDashboardId string) ([]byte, error) {
func processProfile(info *useCaseInfo, spaceDashboardId string) ([]byte, error) {
profile := &pb.Profile{}
if err := profile.Unmarshal(data); err != nil {
if err := profile.Unmarshal(info.profile); err != nil {
e := fmt.Errorf("cannot unmarshal profile: %w", err)
fmt.Println(e)
return nil, e
@ -564,8 +580,12 @@ func listObjects(info *useCaseInfo) {
fmt.Println("\n- File Objects:")
fmt.Println("Id: " + strings.Repeat(" ", 31) + "Name")
for _, id := range info.files {
for _, id := range info.fileObjects {
obj := info.objects[id]
fmt.Printf("%s:\t%32s\n", id[len(id)-4:], obj.Name)
}
}
func isPlainFile(name string) bool {
return strings.HasPrefix(name, export.Files) && !strings.HasPrefix(name, export.FilesObjects)
}

View file

@ -4,11 +4,10 @@ package main
import (
"fmt"
"slices"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/ipfs/go-cid"
"github.com/samber/lo"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
"github.com/anyproto/anytype-heart/core/block/simple"
@ -32,7 +31,6 @@ type keyWithIndex struct {
}
var validators = []validator{
validateRelationLinks,
validateRelationBlocks,
validateDetails,
validateObjectTypes,
@ -41,26 +39,6 @@ var validators = []validator{
validateRelationOption,
}
func validateRelationLinks(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
id := pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
linksToDelete := make([]keyWithIndex, 0)
for i, rel := range s.Snapshot.Data.RelationLinks {
if bundle.HasRelation(domain.RelationKey(rel.Key)) {
continue
}
if _, found := info.customTypesAndRelations[rel.Key]; found {
continue
}
linksToDelete = append([]keyWithIndex{{key: rel.Key, index: i}}, linksToDelete...)
}
for _, link := range linksToDelete {
fmt.Println("WARNING: object", id, "contains link to unknown relation:", link.key, ", so it was deleted from snapshot")
s.Snapshot.Data.RelationLinks = append(s.Snapshot.Data.RelationLinks[:link.index], s.Snapshot.Data.RelationLinks[link.index+1:]...)
}
return err
}
func validateRelationBlocks(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
id := pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
var relKeys []string
@ -105,16 +83,10 @@ func validateDetails(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
)
rel, e = bundle.GetRelation(domain.RelationKey(k))
if e != nil {
rel = getRelationLinkByKey(s.Snapshot.Data.RelationLinks, k)
if rel == nil {
if relation, errFound := bundle.GetRelation(domain.RelationKey(k)); errFound == nil {
s.Snapshot.Data.RelationLinks = append(s.Snapshot.Data.RelationLinks, &model.RelationLink{
Key: k,
Format: relation.Format,
})
continue
}
err = multierror.Append(err, fmt.Errorf("relation '%s' exists in details of object '%s', but not in relation links", k, id))
var found bool
rel, found = info.customTypesAndRelations[k]
if !found {
err = multierror.Append(err, fmt.Errorf("relation '%s' exists in details of object '%s', but not in the archive", k, id))
continue
}
}
@ -122,7 +94,12 @@ func validateDetails(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
continue
}
values := pbtypes.GetStringListValue(v)
var (
values = pbtypes.GetStringListValue(v)
isUpdateNeeded bool
newValues = make([]string, 0, len(values))
)
for _, val := range values {
if bundle.HasRelation(domain.RelationKey(strings.TrimPrefix(val, addr.RelationKeyToIdPrefix))) ||
bundle.HasObjectTypeByKey(domain.TypeKey(strings.TrimPrefix(val, addr.ObjectTypeKeyToIdPrefix))) || val == addr.AnytypeProfileId {
@ -135,15 +112,31 @@ func validateDetails(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
}
}
if k == bundle.RelationKeySpaceDashboardId.String() && val == "lastOpened" {
continue
}
_, found := info.objects[val]
if !found {
if isBrokenTemplate(k, val) {
fmt.Println("WARNING: object", id, "is a template with no target type included in the archive, so it will be skipped")
return errSkipObject
}
if isRecommendedRelationsKey(k) {
// we can exclude recommended relations that are not found, because the majority of types are not imported
fmt.Println("WARNING: type", id, "contains relation", val, "that is not included in the archive, so this relation will be excluded from the list")
isUpdateNeeded = true
continue
}
err = multierror.Append(err, fmt.Errorf("failed to find target id for detail '%s: %s' of object %s", k, val, id))
} else {
newValues = append(newValues, val)
}
}
if isUpdateNeeded {
s.Snapshot.Data.Details.Fields[k] = pbtypes.StringList(newValues)
}
}
return err
}
@ -162,14 +155,22 @@ func validateObjectTypes(s *pb.SnapshotWithType, info *useCaseInfo) (err error)
}
func validateBlockLinks(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
id := pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
var (
id = pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
widgetLinkBlocksToDelete []string
)
for _, b := range s.Snapshot.Data.Blocks {
switch a := simple.New(b).(type) {
case link.Block:
target := a.Model().GetLink().TargetBlockId
_, found := info.objects[target]
if !found {
if s.SbType == model.SmartBlockType_Widget && isDefaultWidget(target) {
if s.SbType == model.SmartBlockType_Widget {
if isDefaultWidget(target) {
continue
}
widgetLinkBlocksToDelete = append(widgetLinkBlocksToDelete, b.Id)
continue
}
err = multierror.Append(err, fmt.Errorf("failed to find target id for link '%s' in block '%s' of object '%s'",
@ -206,39 +207,10 @@ func validateBlockLinks(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
}
}
}
return err
}
if err == nil && len(widgetLinkBlocksToDelete) > 0 {
err = removeWidgetBlocks(s, id, widgetLinkBlocksToDelete)
}
func validateFileKeys(s *pb.SnapshotWithType, _ *useCaseInfo) (err error) {
id := pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
for _, r := range s.Snapshot.Data.RelationLinks {
if r.Format == model.RelationFormat_file || r.Key == bundle.RelationKeyCoverId.String() {
for _, hash := range pbtypes.GetStringList(s.Snapshot.GetData().GetDetails(), r.Key) {
if r.Format != model.RelationFormat_file {
_, err := cid.Parse(hash)
if err != nil {
continue
}
}
if !snapshotHasKeyForHash(s, hash) {
err = multierror.Append(err, fmt.Errorf("object '%s' has file detail '%s' has hash '%s' which keys are not in the snapshot", id, r.Key, hash))
}
}
}
}
for _, b := range s.Snapshot.Data.Blocks {
if v, ok := simple.New(b).(simple.FileHashes); ok {
hashes := v.FillFileHashes([]string{})
if len(hashes) == 0 {
continue
}
for _, hash := range hashes {
if !snapshotHasKeyForHash(s, hash) {
err = multierror.Append(err, fmt.Errorf("file block '%s' of object '%s' has hash '%s' which keys are not in the snapshot", b.Id, id, hash))
}
}
}
}
return err
}
@ -299,8 +271,14 @@ func snapshotHasKeyForHash(s *pb.SnapshotWithType, hash string) bool {
return false
}
// these relations will be overwritten on import
func isLinkRelation(k string) bool {
return k == bundle.RelationKeyLinks.String() || k == bundle.RelationKeySourceObject.String() || k == bundle.RelationKeyBacklinks.String()
return slices.Contains([]string{
bundle.RelationKeyLinks.String(),
bundle.RelationKeySourceObject.String(),
bundle.RelationKeyBacklinks.String(),
bundle.RelationKeyMentions.String(),
}, k)
}
func canRelationContainObjectValues(format model.RelationFormat) bool {
@ -316,10 +294,11 @@ func canRelationContainObjectValues(format model.RelationFormat) bool {
}
func isDefaultWidget(target string) bool {
return lo.Contains([]string{
return slices.Contains([]string{
widget.DefaultWidgetFavorite,
widget.DefaultWidgetSet,
widget.DefaultWidgetRecent,
widget.DefaultWidgetRecentOpen,
widget.DefaultWidgetCollection,
}, target)
}
@ -327,3 +306,56 @@ func isDefaultWidget(target string) bool {
func isBrokenTemplate(key, value string) bool {
return key == bundle.RelationKeyTargetObjectType.String() && value == addr.MissingObject
}
func isRecommendedRelationsKey(key string) bool {
return slices.Contains([]string{
bundle.RelationKeyRecommendedRelations.String(),
bundle.RelationKeyRecommendedFeaturedRelations.String(),
bundle.RelationKeyRecommendedHiddenRelations.String(),
bundle.RelationKeyRecommendedFileRelations.String(),
}, key)
}
// removeWidgetBlocks removes link blocks and widget blocks from Widget object.
// For each link block we should remove parent widget block and remove its id from root's children.
// Widget object blocks structure:
//
// root
// |--- widget1
// | |--- link1
// |
// |--- widget2
// |--- link2
func removeWidgetBlocks(s *pb.SnapshotWithType, rootId string, linkBlockIds []string) error {
widgetBlockIds := make([]string, 0, len(linkBlockIds))
var rootBlock *model.Block
for _, b := range s.Snapshot.Data.Blocks {
if b.Id == rootId {
rootBlock = b
continue
}
// widget block has only one child - link block
if len(b.ChildrenIds) != 1 {
continue
}
if slices.Contains(linkBlockIds, b.ChildrenIds[0]) {
widgetBlockIds = append(widgetBlockIds, b.Id)
}
}
if rootBlock == nil {
return fmt.Errorf("root block not found")
}
rootBlock.ChildrenIds = slices.DeleteFunc(rootBlock.ChildrenIds, func(id string) bool {
return slices.Contains(widgetBlockIds, id)
})
blocksToDelete := slices.Concat(widgetBlockIds, linkBlockIds)
s.Snapshot.Data.Blocks = slices.DeleteFunc(s.Snapshot.Data.Blocks, func(b *model.Block) bool {
return slices.Contains(blocksToDelete, b.Id)
})
return nil
}

View file

@ -0,0 +1,153 @@
//go:build !nogrpcserver && !_test
package main
import (
"testing"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/assert"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
func TestValidateDetails(t *testing.T) {
t.Run("snapshot is valid", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("snap shot"),
bundle.RelationKeyType.String(): pbtypes.String(bundle.TypeKeyTask.URL()),
bundle.RelationKeyAssignee.String(): pbtypes.String("kirill"),
bundle.RelationKeyTasks.String(): pbtypes.StringList([]string{"task1", "task2"}),
bundle.RelationKeyFeaturedRelations.String(): pbtypes.StringList([]string{
bundle.RelationKeyType.URL(), "rel-customTag",
}),
}},
}}}
info := &useCaseInfo{
objects: map[string]objectInfo{
bundle.TypeKeyTask.URL(): {},
"kirill": {},
"task1": {},
"task2": {},
},
customTypesAndRelations: map[string]customInfo{
"rel-customTag": {},
},
}
// when
err := validateDetails(s, info)
// then
assert.NoError(t, err)
})
t.Run("some object is missing", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyAssignee.String(): pbtypes.String("kirill"),
}},
}}}
info := &useCaseInfo{}
// when
err := validateDetails(s, info)
// then
assert.Error(t, err)
})
t.Run("broken template", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyTargetObjectType.String(): pbtypes.String(addr.MissingObject),
}},
}}}
info := &useCaseInfo{}
// when
err := validateDetails(s, info)
// then
assert.Error(t, err)
assert.ErrorIs(t, errSkipObject, err)
})
t.Run("exclude missing recommendedRelations", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyRecommendedRelations.String(): pbtypes.StringList([]string{
bundle.RelationKeyCreator.BundledURL(),
bundle.RelationKeyCreatedDate.BundledURL(),
}),
bundle.RelationKeyRecommendedFeaturedRelations.String(): pbtypes.StringList([]string{
bundle.RelationKeyType.BundledURL(),
bundle.RelationKeyTag.BundledURL(),
}),
}},
}}}
info := &useCaseInfo{
objects: map[string]objectInfo{
bundle.RelationKeyCreator.BundledURL(): {},
bundle.RelationKeyTag.BundledURL(): {},
},
}
// when
err := validateDetails(s, info)
// then
assert.NoError(t, err)
assert.Equal(t, []string{bundle.RelationKeyCreator.BundledURL()}, pbtypes.GetStringList(s.Snapshot.Data.Details, bundle.RelationKeyRecommendedRelations.String()))
assert.Equal(t, []string{bundle.RelationKeyTag.BundledURL()}, pbtypes.GetStringList(s.Snapshot.Data.Details, bundle.RelationKeyRecommendedFeaturedRelations.String()))
})
}
func TestRemoveWidgetBlock(t *testing.T) {
rootId := "root"
t.Run("blocks were removed", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Blocks: []*model.Block{
{Id: rootId, ChildrenIds: []string{"w1", "w2", "w3"}},
{Id: "w1", ChildrenIds: []string{"l1"}},
{Id: "w2", ChildrenIds: []string{"l2"}},
{Id: "w3", ChildrenIds: []string{"l3"}},
{Id: "l1"}, {Id: "l2"}, {Id: "l3"},
},
}}}
// when
err := removeWidgetBlocks(s, rootId, []string{"l2", "l3"})
// then
assert.NoError(t, err)
assert.Len(t, s.Snapshot.Data.Blocks, 3)
assert.Equal(t, []string{"w1"}, s.Snapshot.Data.Blocks[0].ChildrenIds)
})
t.Run("no root found", func(t *testing.T) {
// given
s := &pb.SnapshotWithType{Snapshot: &pb.ChangeSnapshot{Data: &model.SmartBlockSnapshotBase{
Blocks: []*model.Block{
{Id: "wrong root id", ChildrenIds: []string{"w1"}},
{Id: "w1", ChildrenIds: []string{"l1"}}, {Id: "l1"},
},
}}}
// when
err := removeWidgetBlocks(s, rootId, []string{"l1"})
// then
assert.Error(t, err)
})
}

View file

@ -150,6 +150,7 @@ func (s *service) GetInfo(ctx context.Context) (*model.AccountInfo, error) {
AnalyticsId: analyticsId,
NetworkId: s.getNetworkId(),
TechSpaceId: s.spaceService.TechSpaceId(),
EthereumAddress: s.wallet.GetAccountEthAddress().Hex(),
}, nil
}

View file

@ -222,6 +222,9 @@ func Bootstrap(a *app.App, components ...app.Component) {
Register(backlinks.New()).
Register(filestore.New()).
// Services
Register(collection.New()).
Register(subscription.New()).
Register(crossspacesub.New()).
Register(nodeconfsource.New()).
Register(nodeconfstore.New()).
Register(nodeconf.New()).
@ -294,9 +297,6 @@ func Bootstrap(a *app.App, components ...app.Component) {
Register(unsplash.New()).
Register(restriction.New()).
Register(debug.New()).
Register(collection.New()).
Register(subscription.New()).
Register(crossspacesub.New()).
Register(syncsubscriptions.New()).
Register(builtinobjects.New()).
Register(bookmark.New()).

View file

@ -2,17 +2,32 @@ package chats
import (
"context"
"errors"
"fmt"
"sync"
"github.com/anyproto/any-sync/app"
"github.com/cheggaaa/mb/v3"
"go.uber.org/zap"
"github.com/anyproto/anytype-heart/core/block/cache"
"github.com/anyproto/anytype-heart/core/block/editor/chatobject"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/session"
subscriptionservice "github.com/anyproto/anytype-heart/core/subscription"
"github.com/anyproto/anytype-heart/core/subscription/crossspacesub"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const CName = "core.block.chats"
var log = logging.Logger(CName).Desugar()
type Service interface {
AddMessage(ctx context.Context, sessionCtx session.Context, chatObjectId string, message *model.ChatMessage) (string, error)
EditMessage(ctx context.Context, chatObjectId string, messageId string, newMessage *model.ChatMessage) error
@ -20,16 +35,27 @@ type Service interface {
DeleteMessage(ctx context.Context, chatObjectId string, messageId string) error
GetMessages(ctx context.Context, chatObjectId string, req chatobject.GetMessagesRequest) ([]*model.ChatMessage, error)
GetMessagesByIds(ctx context.Context, chatObjectId string, messageIds []string) ([]*model.ChatMessage, error)
SubscribeLastMessages(ctx context.Context, chatObjectId string, limit int) ([]*model.ChatMessage, int, error)
Unsubscribe(chatObjectId string) error
SubscribeLastMessages(ctx context.Context, chatObjectId string, limit int, subId string) ([]*model.ChatMessage, int, error)
Unsubscribe(chatObjectId string, subId string) error
app.Component
SubscribeToMessagePreviews(ctx context.Context) (string, error)
app.ComponentRunnable
}
var _ Service = (*service)(nil)
type service struct {
objectGetter cache.ObjectGetter
objectGetter cache.ObjectGetter
crossSpaceSubService crossspacesub.Service
componentCtx context.Context
componentCtxCancel context.CancelFunc
eventSender event.Sender
lock sync.Mutex
chatObjectsSubQueue *mb.MB[*pb.EventMessage]
}
func New() Service {
@ -42,10 +68,113 @@ func (s *service) Name() string {
func (s *service) Init(a *app.App) error {
s.objectGetter = app.MustComponent[cache.ObjectGetter](a)
s.crossSpaceSubService = app.MustComponent[crossspacesub.Service](a)
s.eventSender = app.MustComponent[event.Sender](a)
s.componentCtx, s.componentCtxCancel = context.WithCancel(context.Background())
return nil
}
const (
allChatsSubscriptionId = "allChatObjects"
)
func (s *service) SubscribeToMessagePreviews(ctx context.Context) (string, error) {
s.lock.Lock()
if s.chatObjectsSubQueue != nil {
s.lock.Unlock()
return chatobject.LastMessageSubscriptionId, nil
}
s.chatObjectsSubQueue = mb.New[*pb.EventMessage](0)
s.lock.Unlock()
resp, err := s.crossSpaceSubService.Subscribe(subscriptionservice.SubscribeRequest{
SubId: allChatsSubscriptionId,
InternalQueue: s.chatObjectsSubQueue,
Keys: []string{bundle.RelationKeyId.String()},
NoDepSubscription: true,
Filters: []database.FilterRequest{
{
RelationKey: bundle.RelationKeyLayout,
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.Int64(model.ObjectType_chatDerived),
},
},
})
if err != nil {
return "", fmt.Errorf("cross-space sub: %w", err)
}
for _, rec := range resp.Records {
err := s.onChatAdded(rec.GetString(bundle.RelationKeyId))
if err != nil {
log.Error("init lastMessage subscription", zap.Error(err))
}
}
go s.monitorChats()
return chatobject.LastMessageSubscriptionId, nil
}
func (s *service) Run(ctx context.Context) error {
return nil
}
func (s *service) monitorChats() {
matcher := subscriptionservice.EventMatcher{
OnAdd: func(add *pb.EventObjectSubscriptionAdd) {
err := s.onChatAdded(add.Id)
if err != nil {
log.Error("init last message subscription", zap.Error(err))
}
},
OnRemove: func(remove *pb.EventObjectSubscriptionRemove) {
err := s.Unsubscribe(remove.Id, chatobject.LastMessageSubscriptionId)
if err != nil && !errors.Is(err, domain.ErrObjectNotFound) {
log.Error("unsubscribe from the last message", zap.Error(err))
}
},
}
for {
msg, err := s.chatObjectsSubQueue.WaitOne(s.componentCtx)
if errors.Is(err, mb.ErrClosed) {
return
}
if err != nil {
log.Error("wait message", zap.Error(err))
return
}
matcher.Match(msg)
}
}
func (s *service) onChatAdded(chatObjectId string) error {
return cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
var err error
_, _, err = sb.SubscribeLastMessages(s.componentCtx, chatobject.LastMessageSubscriptionId, 1, true)
if err != nil {
return err
}
return nil
})
}
func (s *service) Close(ctx context.Context) error {
var err error
s.lock.Lock()
defer s.lock.Unlock()
if s.chatObjectsSubQueue != nil {
err = s.chatObjectsSubQueue.Close()
}
s.componentCtxCancel()
err = errors.Join(err,
s.crossSpaceSubService.Unsubscribe(allChatsSubscriptionId),
)
return err
}
func (s *service) AddMessage(ctx context.Context, sessionCtx session.Context, chatObjectId string, message *model.ChatMessage) (string, error) {
var messageId string
err := cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
@ -100,14 +229,14 @@ func (s *service) GetMessagesByIds(ctx context.Context, chatObjectId string, mes
return res, err
}
func (s *service) SubscribeLastMessages(ctx context.Context, chatObjectId string, limit int) ([]*model.ChatMessage, int, error) {
func (s *service) SubscribeLastMessages(ctx context.Context, chatObjectId string, limit int, subId string) ([]*model.ChatMessage, int, error) {
var (
msgs []*model.ChatMessage
numBefore int
)
err := cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
var err error
msgs, numBefore, err = sb.SubscribeLastMessages(ctx, limit)
msgs, numBefore, err = sb.SubscribeLastMessages(ctx, subId, limit, false)
if err != nil {
return err
}
@ -116,8 +245,8 @@ func (s *service) SubscribeLastMessages(ctx context.Context, chatObjectId string
return msgs, numBefore, err
}
func (s *service) Unsubscribe(chatObjectId string) error {
func (s *service) Unsubscribe(chatObjectId string, subId string) error {
return cache.Do(s.objectGetter, chatObjectId, func(sb chatobject.StoreObject) error {
return sb.Unsubscribe()
return sb.Unsubscribe(subId)
})
}

View file

@ -43,7 +43,7 @@ func (d ChatHandler) BeforeCreate(ctx context.Context, ch storestate.ChangeOp) (
model := msg.toModel()
model.OrderId = ch.Change.Order
d.subscription.add(model)
d.subscription.add(ch.Change.PrevOrderId, model)
return
}

View file

@ -19,6 +19,8 @@ import (
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
@ -28,6 +30,8 @@ const (
ascOrder = "_o.id"
)
var log = logging.Logger("core.block.editor.chatobject").Desugar()
type StoreObject interface {
smartblock.SmartBlock
anystoredebug.AnystoreDebug
@ -38,9 +42,9 @@ type StoreObject interface {
EditMessage(ctx context.Context, messageId string, newMessage *model.ChatMessage) error
ToggleMessageReaction(ctx context.Context, messageId string, emoji string) error
DeleteMessage(ctx context.Context, messageId string) error
SubscribeLastMessages(ctx context.Context, limit int) ([]*model.ChatMessage, int, error)
SubscribeLastMessages(ctx context.Context, subId string, limit int, asyncInit bool) ([]*model.ChatMessage, int, error)
MarkSeenHeads(heads []string)
Unsubscribe() error
Unsubscribe(subId string) error
}
type GetMessagesRequest struct {
@ -64,11 +68,12 @@ type storeObject struct {
eventSender event.Sender
subscription *subscription
crdtDb anystore.DB
spaceIndex spaceindex.Store
arenaPool *anyenc.ArenaPool
}
func New(sb smartblock.SmartBlock, accountService AccountService, eventSender event.Sender, crdtDb anystore.DB) StoreObject {
func New(sb smartblock.SmartBlock, accountService AccountService, eventSender event.Sender, crdtDb anystore.DB, spaceIndex spaceindex.Store) StoreObject {
return &storeObject{
SmartBlock: sb,
locker: sb.(smartblock.Locker),
@ -76,6 +81,7 @@ func New(sb smartblock.SmartBlock, accountService AccountService, eventSender ev
arenaPool: &anyenc.ArenaPool{},
eventSender: eventSender,
crdtDb: crdtDb,
spaceIndex: spaceIndex,
}
}
@ -84,7 +90,7 @@ func (s *storeObject) Init(ctx *smartblock.InitContext) error {
if err != nil {
return err
}
s.subscription = newSubscription(s.SpaceID(), s.Id(), s.eventSender)
s.subscription = newSubscription(s.SpaceID(), s.Id(), s.eventSender, s.spaceIndex)
stateStore, err := storestate.New(ctx.Ctx, s.Id(), s.crdtDb, ChatHandler{
subscription: s.subscription,
@ -312,13 +318,20 @@ func (s *storeObject) hasMyReaction(ctx context.Context, arena *anyenc.Arena, me
return false, nil
}
func (s *storeObject) SubscribeLastMessages(ctx context.Context, limit int) ([]*model.ChatMessage, int, error) {
func (s *storeObject) SubscribeLastMessages(ctx context.Context, subId string, limit int, asyncInit bool) ([]*model.ChatMessage, int, error) {
coll, err := s.store.Collection(ctx, collectionName)
if err != nil {
return nil, 0, fmt.Errorf("get collection: %w", err)
}
txn, err := s.store.NewTx(ctx)
if err != nil {
return nil, 0, fmt.Errorf("init read transaction: %w", err)
}
defer txn.Commit()
query := coll.Find(nil).Sort(descOrder).Limit(uint(limit))
messages, err := s.queryMessages(ctx, query)
messages, err := s.queryMessages(txn.Context(), query)
if err != nil {
return nil, 0, fmt.Errorf("query messages: %w", err)
}
@ -327,13 +340,29 @@ func (s *storeObject) SubscribeLastMessages(ctx context.Context, limit int) ([]*
return messages[i].OrderId < messages[j].OrderId
})
s.subscription.enable()
s.subscription.subscribe(subId)
if asyncInit {
var previousOrderId string
if len(messages) > 0 {
previousOrderId, err = txn.GetPrevOrderId(messages[0].OrderId)
if err != nil {
return nil, 0, fmt.Errorf("get previous order id: %w", err)
}
}
return messages, 0, nil
for _, message := range messages {
s.subscription.add(previousOrderId, message)
previousOrderId = message.OrderId
}
s.subscription.flush()
return nil, 0, nil
} else {
return messages, 0, nil
}
}
func (s *storeObject) Unsubscribe() error {
s.subscription.close()
func (s *storeObject) Unsubscribe(subId string) error {
s.subscription.unsubscribe(subId)
return nil
}
@ -341,7 +370,7 @@ func (s *storeObject) TryClose(objectTTL time.Duration) (res bool, err error) {
if !s.locker.TryLock() {
return false, nil
}
isActive := s.subscription.enabled
isActive := s.subscription.isActive()
s.Unlock()
if isActive {

View file

@ -21,6 +21,7 @@ import (
"github.com/anyproto/anytype-heart/core/event/mock_event"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
@ -58,7 +59,9 @@ func newFixture(t *testing.T) *fixture {
sb := smarttest.New("chatId1")
object := New(sb, accountService, eventSender, db)
spaceIndex := spaceindex.NewStoreFixture(t)
object := New(sb, accountService, eventSender, db, spaceIndex)
fx := &fixture{
storeObject: object.(*storeObject),

View file

@ -2,13 +2,22 @@ package chatobject
import (
"slices"
"time"
"github.com/hashicorp/golang-lru/v2/expirable"
"go.uber.org/zap"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/slice"
)
const LastMessageSubscriptionId = "lastMessage"
type subscription struct {
spaceId string
chatId string
@ -18,23 +27,39 @@ type subscription struct {
eventsBuffer []*pb.EventMessage
enabled bool
spaceIndex spaceindex.Store
identityCache *expirable.LRU[string, *domain.Details]
ids []string
}
func newSubscription(spaceId string, chatId string, eventSender event.Sender) *subscription {
func newSubscription(spaceId string, chatId string, eventSender event.Sender, spaceIndex spaceindex.Store) *subscription {
return &subscription{
spaceId: spaceId,
chatId: chatId,
eventSender: eventSender,
spaceId: spaceId,
chatId: chatId,
eventSender: eventSender,
spaceIndex: spaceIndex,
identityCache: expirable.NewLRU[string, *domain.Details](50, nil, time.Minute),
}
}
func (s *subscription) enable() {
s.enabled = true
func (s *subscription) subscribe(subId string) {
if !slices.Contains(s.ids, subId) {
s.ids = append(s.ids, subId)
}
}
func (s *subscription) close() {
s.enabled = false
func (s *subscription) unsubscribe(subId string) {
s.ids = slice.Remove(s.ids, subId)
}
func (s *subscription) isActive() bool {
return len(s.ids) > 0
}
func (s *subscription) withDeps() bool {
return slices.Equal(s.ids, []string{LastMessageSubscriptionId})
}
// setSessionContext sets the session context for the current operation
@ -59,20 +84,54 @@ func (s *subscription) flush() {
s.sessionContext.SetMessages(s.chatId, slices.Clone(s.eventsBuffer))
s.eventSender.BroadcastToOtherSessions(s.sessionContext.ID(), ev)
s.sessionContext = nil
} else if s.enabled {
} else if s.isActive() {
s.eventSender.Broadcast(ev)
}
}
func (s *subscription) add(message *model.ChatMessage) {
func (s *subscription) getIdentityDetails(identity string) (*domain.Details, error) {
cached, ok := s.identityCache.Get(identity)
if ok {
return cached, nil
}
details, err := s.spaceIndex.GetDetails(domain.NewParticipantId(s.spaceId, identity))
if err != nil {
return nil, err
}
s.identityCache.Add(identity, details)
return details, nil
}
func (s *subscription) add(prevOrderId string, message *model.ChatMessage) {
if !s.canSend() {
return
}
ev := &pb.EventChatAdd{
Id: message.Id,
Message: message,
OrderId: message.OrderId,
Id: message.Id,
Message: message,
OrderId: message.OrderId,
AfterOrderId: prevOrderId,
SubIds: slices.Clone(s.ids),
}
if s.withDeps() {
identityDetails, err := s.getIdentityDetails(message.Creator)
if err != nil {
log.Error("get identity details", zap.Error(err))
} else {
ev.Dependencies = append(ev.Dependencies, identityDetails.ToProto())
}
for _, attachment := range message.Attachments {
attachmentDetails, err := s.spaceIndex.GetDetails(attachment.Target)
if err != nil {
log.Error("get attachment details", zap.Error(err))
} else {
ev.Dependencies = append(ev.Dependencies, attachmentDetails.ToProto())
}
}
}
s.eventsBuffer = append(s.eventsBuffer, event.NewMessage(s.spaceId, &pb.EventMessageValueOfChatAdd{
ChatAdd: ev,
}))
@ -80,7 +139,8 @@ func (s *subscription) add(message *model.ChatMessage) {
func (s *subscription) delete(messageId string) {
ev := &pb.EventChatDelete{
Id: messageId,
Id: messageId,
SubIds: slices.Clone(s.ids),
}
s.eventsBuffer = append(s.eventsBuffer, event.NewMessage(s.spaceId, &pb.EventMessageValueOfChatDelete{
ChatDelete: ev,
@ -94,6 +154,7 @@ func (s *subscription) updateFull(message *model.ChatMessage) {
ev := &pb.EventChatUpdate{
Id: message.Id,
Message: message,
SubIds: slices.Clone(s.ids),
}
s.eventsBuffer = append(s.eventsBuffer, event.NewMessage(s.spaceId, &pb.EventMessageValueOfChatUpdate{
ChatUpdate: ev,
@ -107,6 +168,7 @@ func (s *subscription) updateReactions(message *model.ChatMessage) {
ev := &pb.EventChatUpdateReactions{
Id: message.Id,
Reactions: message.Reactions,
SubIds: slices.Clone(s.ids),
}
s.eventsBuffer = append(s.eventsBuffer, event.NewMessage(s.spaceId, &pb.EventMessageValueOfChatUpdateReactions{
ChatUpdateReactions: ev,
@ -117,7 +179,7 @@ func (s *subscription) canSend() bool {
if s.sessionContext != nil {
return true
}
if !s.enabled {
if !s.isActive() {
return false
}
return true

View file

@ -21,7 +21,7 @@ func TestSubscription(t *testing.T) {
assert.NotEmpty(t, messageId)
}
messages, _, err := fx.SubscribeLastMessages(ctx, 5)
messages, _, err := fx.SubscribeLastMessages(ctx, "subId", 5, false)
require.NoError(t, err)
wantTexts := []string{"text 6", "text 7", "text 8", "text 9", "text 10"}
for i, msg := range messages {

View file

@ -170,7 +170,7 @@ func (f *ObjectFactory) produceSmartblock(space smartblock.Space) (smartblock.Sm
}
func (f *ObjectFactory) New(space smartblock.Space, sbType coresb.SmartBlockType) (smartblock.SmartBlock, error) {
sb, store := f.produceSmartblock(space)
sb, spaceIndex := f.produceSmartblock(space)
switch sbType {
case coresb.SmartBlockTypePage,
coresb.SmartBlockTypeDate,
@ -183,9 +183,9 @@ func (f *ObjectFactory) New(space smartblock.Space, sbType coresb.SmartBlockType
case coresb.SmartBlockTypeObjectType:
return f.newObjectType(space.Id(), sb), nil
case coresb.SmartBlockTypeArchive:
return NewArchive(sb, store), nil
return NewArchive(sb, spaceIndex), nil
case coresb.SmartBlockTypeHome:
return NewDashboard(sb, store, f.layoutConverter), nil
return NewDashboard(sb, spaceIndex, f.layoutConverter), nil
case coresb.SmartBlockTypeProfilePage,
coresb.SmartBlockTypeAnytypeProfile:
return f.newProfile(space.Id(), sb), nil
@ -195,25 +195,25 @@ func (f *ObjectFactory) New(space smartblock.Space, sbType coresb.SmartBlockType
coresb.SmartBlockTypeBundledTemplate:
return f.newTemplate(space.Id(), sb), nil
case coresb.SmartBlockTypeWorkspace:
return f.newWorkspace(sb, store), nil
return f.newWorkspace(sb, spaceIndex), nil
case coresb.SmartBlockTypeSpaceView:
return f.newSpaceView(sb), nil
case coresb.SmartBlockTypeMissingObject:
return NewMissingObject(sb), nil
case coresb.SmartBlockTypeWidget:
return NewWidgetObject(sb, store, f.layoutConverter), nil
return NewWidgetObject(sb, spaceIndex, f.layoutConverter), nil
case coresb.SmartBlockTypeNotificationObject:
return NewNotificationObject(sb), nil
case coresb.SmartBlockTypeSubObject:
return nil, fmt.Errorf("subobject not supported via factory")
case coresb.SmartBlockTypeParticipant:
return f.newParticipant(space.Id(), sb, store), nil
return f.newParticipant(space.Id(), sb, spaceIndex), nil
case coresb.SmartBlockTypeDevicesObject:
return NewDevicesObject(sb, f.deviceService), nil
case coresb.SmartBlockTypeChatDerivedObject:
return chatobject.New(sb, f.accountService, f.eventSender, f.objectStore.GetCrdtDb(space.Id())), nil
return chatobject.New(sb, f.accountService, f.eventSender, f.objectStore.GetCrdtDb(space.Id()), spaceIndex), nil
case coresb.SmartBlockTypeAccountObject:
return accountobject.New(sb, f.accountService.Keys(), store, f.layoutConverter, f.fileObjectService, f.objectStore.GetCrdtDb(space.Id()), f.config), nil
return accountobject.New(sb, f.accountService.Keys(), spaceIndex, f.layoutConverter, f.fileObjectService, f.objectStore.GetCrdtDb(space.Id()), f.config), nil
default:
return nil, fmt.Errorf("unexpected smartblock type: %v", sbType)
}

View file

@ -105,6 +105,7 @@ func (ot *ObjectType) CreationStateMigration(ctx *smartblock.InitContext) migrat
template.WithTitle,
template.WithLayout(model.ObjectType_objectType),
}
templates = append(templates, ot.dataviewTemplates()...)
template.InitTemplate(s, templates...)
},
@ -121,6 +122,12 @@ func (ot *ObjectType) StateMigrations() migration.Migrations {
Version: 3,
Proc: ot.featuredRelationsMigration,
},
{
Version: 4,
Proc: func(s *state.State) {
template.InitTemplate(s, ot.dataviewTemplates()...)
},
},
})
}
@ -133,7 +140,12 @@ func (ot *ObjectType) featuredRelationsMigration(s *state.State) {
return
}
featuredRelationKeys := relationutils.DefaultFeaturedRelationKeys()
var typeKey domain.TypeKey
if uk, err := domain.UnmarshalUniqueKey(s.Details().GetString(bundle.RelationKeyUniqueKey)); err == nil {
typeKey = domain.TypeKey(uk.InternalKey())
}
featuredRelationKeys := relationutils.DefaultFeaturedRelationKeys(typeKey)
featuredRelationIds := make([]string, 0, len(featuredRelationKeys))
for _, key := range featuredRelationKeys {
id, err := ot.Space().DeriveObjectID(context.Background(), domain.MustUniqueKey(coresb.SmartBlockTypeRelation, key.String()))
@ -384,6 +396,30 @@ func (ot *ObjectType) queryObjectsAndTemplates() ([]database.Record, error) {
return append(records, templates...), nil
}
func (ot *ObjectType) dataviewTemplates() []template.StateTransformer {
details := ot.Details()
name := details.GetString(bundle.RelationKeyName)
key := details.GetString(bundle.RelationKeyUniqueKey)
dvContent := template.MakeDataviewContent(false, &model.ObjectType{
Url: ot.Id(),
Name: name,
// todo: add RelationLinks, because they are not indexed at this moment :(
Key: key,
}, []*model.RelationLink{
{
Key: bundle.RelationKeyName.String(),
Format: model.RelationFormat_longtext,
},
}, objectTypeAllViewId)
dvContent.Dataview.TargetObjectId = ot.Id()
return []template.StateTransformer{
template.WithDataviewID(state.DataviewBlockID, dvContent, false),
template.WithForcedDetail(bundle.RelationKeySetOf, domain.StringList([]string{ot.Id()})),
}
}
type layoutRelationsChanges struct {
relationsToRemove []domain.RelationKey
isLayoutFound bool

View file

@ -231,12 +231,6 @@ func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Mig
template.WithTitle,
template.WithLayout(layout),
)
case model.ObjectType_objectType:
templates = append(templates,
template.WithTitle,
template.WithLayout(layout),
)
templates = append(templates, p.getObjectTypeTemplates()...)
case model.ObjectType_chat:
templates = append(templates,
template.WithTitle,
@ -268,48 +262,5 @@ func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Mig
}
func (p *Page) StateMigrations() migration.Migrations {
migrations := []migration.Migration{
{
Version: 2,
Proc: func(s *state.State) {},
},
}
// migration 3 is skipped
// migration 4 is applied only for ObjectType
if p.ObjectTypeKey() == bundle.TypeKeyObjectType {
migrations = append(migrations,
migration.Migration{
Version: 4,
Proc: func(s *state.State) {
template.InitTemplate(s, p.getObjectTypeTemplates()...)
},
})
}
return migration.MakeMigrations(migrations)
}
func (p *Page) getObjectTypeTemplates() []template.StateTransformer {
details := p.Details()
name := details.GetString(bundle.RelationKeyName)
key := details.GetString(bundle.RelationKeyUniqueKey)
dvContent := template.MakeDataviewContent(false, &model.ObjectType{
Url: p.Id(),
Name: name,
// todo: add RelationLinks, because they are not indexed at this moment :(
Key: key,
}, []*model.RelationLink{
{
Key: bundle.RelationKeyName.String(),
Format: model.RelationFormat_longtext,
},
}, objectTypeAllViewId)
dvContent.Dataview.TargetObjectId = p.Id()
return []template.StateTransformer{
template.WithDataviewID(state.DataviewBlockID, dvContent, false),
template.WithForcedDetail(bundle.RelationKeySetOf, domain.StringList([]string{p.Id()})),
}
return migration.Migrations{Migrations: []migration.Migration{}}
}

View file

@ -76,19 +76,21 @@ func New(ctx context.Context, id string, db anystore.DB, handlers ...Handler) (s
}
type ChangeSet struct {
Id string
Order string
Creator string
Changes []*pb.StoreChangeContent
Timestamp int64
Id string
PrevOrderId string
Order string
Creator string
Changes []*pb.StoreChangeContent
Timestamp int64
}
type Change struct {
Id string
Order string
Creator string
Change *pb.StoreChangeContent
Timestamp int64
Id string
PrevOrderId string
Order string
Creator string
Change *pb.StoreChangeContent
Timestamp int64
}
type StoreState struct {
@ -134,11 +136,12 @@ func (ss *StoreState) Collection(ctx context.Context, name string) (anystore.Col
func (ss *StoreState) applyChangeSet(ctx context.Context, set ChangeSet) (err error) {
for _, ch := range set.Changes {
applyErr := ss.applyChange(ctx, Change{
Id: set.Id,
Order: set.Order,
Change: ch,
Creator: set.Creator,
Timestamp: set.Timestamp,
Id: set.Id,
PrevOrderId: set.PrevOrderId,
Order: set.Order,
Change: ch,
Creator: set.Creator,
Timestamp: set.Timestamp,
})
if applyErr == nil || errors.Is(applyErr, ErrIgnore) {
continue

View file

@ -49,6 +49,28 @@ func TestStoreStateTx_GetOrder(t *testing.T) {
})
}
func TestStoreStateTx_GetPrevOrderId(t *testing.T) {
fx := newFixture(t, "test", DefaultHandler{Name: "tcoll"})
tx, err := fx.NewTx(ctx)
require.NoError(t, err)
err = tx.SetOrder("ch1", "1")
require.NoError(t, err)
err = tx.SetOrder("ch2", "2")
require.NoError(t, err)
prev, err := tx.GetPrevOrderId("1")
require.NoError(t, err)
assert.Equal(t, "", prev)
prev, err = tx.GetPrevOrderId("2")
require.NoError(t, err)
assert.Equal(t, "1", prev)
err = tx.Commit()
require.NoError(t, err)
}
func TestStoreStateTx_ApplyChangeSet(t *testing.T) {
t.Run("create", func(t *testing.T) {
fx := newFixture(t, "objId", DefaultHandler{Name: "testColl"})

View file

@ -3,10 +3,12 @@ package storestate
import (
"context"
"errors"
"fmt"
"time"
anystore "github.com/anyproto/any-store"
"github.com/anyproto/any-store/anyenc"
"github.com/anyproto/any-store/query"
)
const maxOrderId = "_max"
@ -20,6 +22,10 @@ type StoreStateTx struct {
maxOrderChanged bool
}
func (stx *StoreStateTx) Context() context.Context {
return stx.ctx
}
func (stx *StoreStateTx) init() (err error) {
stx.maxOrder, err = stx.GetOrder(maxOrderId)
if err != nil && !errors.Is(err, ErrOrderNotFound) {
@ -28,6 +34,26 @@ func (stx *StoreStateTx) init() (err error) {
return nil
}
func (stx *StoreStateTx) GetPrevOrderId(orderId string) (string, error) {
iter, err := stx.state.collChangeOrders.Find(query.Key{
Path: []string{"o"},
Filter: query.NewComp(query.CompOpLt, orderId),
}).Sort("-o").Limit(1).Iter(stx.ctx)
if err != nil {
return "", fmt.Errorf("open iterator: %w", err)
}
defer iter.Close()
if !iter.Next() {
return "", iter.Err()
}
doc, err := iter.Doc()
if err != nil {
return "", fmt.Errorf("get prev order id: %w", err)
}
return string(doc.Value().GetStringBytes("o")), nil
}
func (stx *StoreStateTx) GetOrder(changeId string) (orderId string, err error) {
doc, err := stx.state.collChangeOrders.FindId(stx.ctx, changeId)
if err != nil {

View file

@ -92,7 +92,7 @@ func TestMakeDataviewContent(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
block := MakeDataviewContent(tc.isCollection, tc.ot, tc.relLinks)
block := MakeDataviewContent(tc.isCollection, tc.ot, tc.relLinks, "")
assertDataviewBlock(t, block, tc.isCollection, tc.expectedRelations, tc.isVisible)
})
}

View file

@ -1,6 +1,10 @@
package editor
import (
"context"
"golang.org/x/exp/slices"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/converter"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -8,6 +12,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/template"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
"github.com/anyproto/anytype-heart/core/block/migration"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
@ -49,7 +54,7 @@ func (w *WidgetObject) Init(ctx *smartblock.InitContext) (err error) {
func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migration.Migration {
return migration.Migration{
Version: 1,
Version: 2,
Proc: func(st *state.State) {
template.InitTemplate(st,
template.WithEmpty,
@ -61,8 +66,54 @@ func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migra
}
}
func replaceWidgetTarget(st *state.State, targetFrom string, targetTo string, viewId string, layout model.BlockContentWidgetLayout) {
st.Iterate(func(b simple.Block) (isContinue bool) {
if wc, ok := b.Model().Content.(*model.BlockContentOfWidget); ok {
// get child
if len(b.Model().GetChildrenIds()) > 0 {
child := st.Get(b.Model().GetChildrenIds()[0])
childBlock := st.Get(child.Model().Id)
if linkBlock, ok := childBlock.Model().Content.(*model.BlockContentOfLink); ok {
if linkBlock.Link.TargetBlockId == targetFrom {
targets := st.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
if slices.Contains(targets, targetTo) {
return false
}
targets = append(targets, targetTo)
st.SetDetail(bundle.RelationKeyAutoWidgetTargets, domain.StringList(targets))
linkBlock.Link.TargetBlockId = targetTo
wc.Widget.ViewId = viewId
wc.Widget.Layout = layout
return false
}
}
}
}
return true
})
}
func (w *WidgetObject) StateMigrations() migration.Migrations {
return migration.MakeMigrations(nil)
return migration.MakeMigrations([]migration.Migration{
{
Version: 2,
Proc: func(s *state.State) {
spc := w.Space()
setTypeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeySet)
if err != nil {
return
}
collectionTypeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeyCollection)
if err != nil {
return
}
replaceWidgetTarget(s, "collection", collectionTypeId, "all", model.BlockContentWidget_View)
replaceWidgetTarget(s, "set", setTypeId, "all", model.BlockContentWidget_View)
},
},
},
)
}
func (w *WidgetObject) Unlink(ctx session.Context, ids ...string) (err error) {

View file

@ -734,7 +734,7 @@ func (e *exportContext) getRelationsFromStore(relations []string) ([]database.Re
func (e *exportContext) addRelation(relation database.Record) {
relationKey := domain.RelationKey(relation.Details.GetString(bundle.RelationKeyRelationKey))
if relationKey != "" && !bundle.HasRelation(relationKey) {
if relationKey != "" {
id := relation.Details.GetString(bundle.RelationKeyId)
e.docs[id] = &Doc{Details: relation.Details, isLink: e.isLinkProcess}
}

View file

@ -1690,7 +1690,7 @@ func Test_docsForExport(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, 3, len(expCtx.docs))
assert.Equal(t, 4, len(expCtx.docs))
})
t.Run("objects without file", func(t *testing.T) {
// given

View file

@ -146,7 +146,7 @@ func TestIconSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_Image, nil, nil)
syncer := NewIconSyncer(fileUploader, service)
@ -191,7 +191,7 @@ func TestIconSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("", nil, fmt.Errorf("failed to upload"))
}).Return("", model.BlockContentFile_Image, nil, fmt.Errorf("failed to upload"))
syncer := NewIconSyncer(fileUploader, service)

View file

@ -11,6 +11,8 @@ import (
mock "github.com/stretchr/testify/mock"
model "github.com/anyproto/anytype-heart/pkg/lib/pb/model"
session "github.com/anyproto/anytype-heart/core/session"
smartblock "github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -207,7 +209,7 @@ func (_c *MockBlockService_UploadBlockFile_Call) RunAndReturn(run func(session.C
}
// UploadFile provides a mock function with given fields: ctx, spaceId, req
func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (string, *domain.Details, error) {
func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error) {
ret := _m.Called(ctx, spaceId, req)
if len(ret) == 0 {
@ -215,9 +217,10 @@ func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req
}
var r0 string
var r1 *domain.Details
var r2 error
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) (string, *domain.Details, error)); ok {
var r1 model.BlockContentFileType
var r2 *domain.Details
var r3 error
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error)); ok {
return rf(ctx, spaceId, req)
}
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) string); ok {
@ -226,21 +229,27 @@ func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(context.Context, string, block.FileUploadRequest) *domain.Details); ok {
if rf, ok := ret.Get(1).(func(context.Context, string, block.FileUploadRequest) model.BlockContentFileType); ok {
r1 = rf(ctx, spaceId, req)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*domain.Details)
r1 = ret.Get(1).(model.BlockContentFileType)
}
if rf, ok := ret.Get(2).(func(context.Context, string, block.FileUploadRequest) *domain.Details); ok {
r2 = rf(ctx, spaceId, req)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*domain.Details)
}
}
if rf, ok := ret.Get(2).(func(context.Context, string, block.FileUploadRequest) error); ok {
r2 = rf(ctx, spaceId, req)
if rf, ok := ret.Get(3).(func(context.Context, string, block.FileUploadRequest) error); ok {
r3 = rf(ctx, spaceId, req)
} else {
r2 = ret.Error(2)
r3 = ret.Error(3)
}
return r0, r1, r2
return r0, r1, r2, r3
}
// MockBlockService_UploadFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadFile'
@ -263,12 +272,12 @@ func (_c *MockBlockService_UploadFile_Call) Run(run func(ctx context.Context, sp
return _c
}
func (_c *MockBlockService_UploadFile_Call) Return(objectId string, details *domain.Details, err error) *MockBlockService_UploadFile_Call {
_c.Call.Return(objectId, details, err)
func (_c *MockBlockService_UploadFile_Call) Return(objectId string, fileType model.BlockContentFileType, details *domain.Details, err error) *MockBlockService_UploadFile_Call {
_c.Call.Return(objectId, fileType, details, err)
return _c
}
func (_c *MockBlockService_UploadFile_Call) RunAndReturn(run func(context.Context, string, block.FileUploadRequest) (string, *domain.Details, error)) *MockBlockService_UploadFile_Call {
func (_c *MockBlockService_UploadFile_Call) RunAndReturn(run func(context.Context, string, block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error)) *MockBlockService_UploadFile_Call {
_c.Call.Return(run)
return _c
}

View file

@ -66,7 +66,7 @@ func TestFileRelationSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_File, nil, nil)
syncer := NewFileRelationSyncer(fileUploader, nil)
// when
@ -84,7 +84,7 @@ func TestFileRelationSyncer_Sync(t *testing.T) {
LocalPath: "local path",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_File, nil, nil)
syncer := NewFileRelationSyncer(fileUploader, nil)
// when

View file

@ -268,7 +268,7 @@ func (s *service) prepareDetailsForInstallingObject(
switch uk.SmartblockType() {
case coresb.SmartBlockTypeBundledObjectType, coresb.SmartBlockTypeObjectType:
relationKeys, isAlreadyFilled, err := relationutils.FillRecommendedRelations(ctx, spc, details)
relationKeys, isAlreadyFilled, err := relationutils.FillRecommendedRelations(ctx, spc, details, domain.TypeKey(uk.InternalKey()))
if err != nil {
return nil, fmt.Errorf("fill recommended relations: %w", err)
}

View file

@ -30,7 +30,7 @@ func (s *service) createObjectType(ctx context.Context, space clientspace.Space,
object.SetInt64(bundle.RelationKeyRecommendedLayout, int64(model.ObjectType_basic))
}
keys, isAlreadyFilled, err := relationutils.FillRecommendedRelations(ctx, space, object)
keys, isAlreadyFilled, err := relationutils.FillRecommendedRelations(ctx, space, object, domain.TypeKey(uniqueKey.InternalKey()))
if err != nil {
return "", nil, fmt.Errorf("fill recommended relations: %w", err)
}

View file

@ -153,6 +153,7 @@ func (s *store) PushStoreChange(ctx context.Context, params PushStoreChangeParam
if err != nil {
return "", fmt.Errorf("marshal change: %w", err)
}
addResult, err := s.ObjectTree.AddContentWithValidator(ctx, objecttree.SignableChangeContent{
Data: data,
Key: s.accountKeysService.Account().SignKey,
@ -160,13 +161,17 @@ func (s *store) PushStoreChange(ctx context.Context, params PushStoreChangeParam
DataType: dataType,
Timestamp: params.Time.Unix(),
}, func(change objecttree.StorageChange) error {
// TODO: get order here
prevOrder, err := tx.GetPrevOrderId(change.OrderId)
if err != nil {
return fmt.Errorf("get prev order id: %w", err)
}
err = tx.ApplyChangeSet(storestate.ChangeSet{
Id: change.Id,
Order: change.OrderId,
Changes: params.Changes,
Creator: s.accountService.AccountID(),
Timestamp: params.Time.Unix(),
Id: change.Id,
PrevOrderId: prevOrder,
Order: change.OrderId,
Changes: params.Changes,
Creator: s.accountService.AccountID(),
Timestamp: params.Time.Unix(),
})
if err != nil {
return fmt.Errorf("apply change set: %w", err)
@ -202,8 +207,9 @@ func (s *store) update(ctx context.Context, tree objecttree.ObjectTree) error {
return err
}
applier := &storeApply{
tx: tx,
ot: tree,
tx: tx,
ot: tree,
needFetchPrevOrderId: true,
}
if err = applier.Apply(); err != nil {
return errors.Join(tx.Rollback(), err)

View file

@ -16,31 +16,39 @@ type storeApply struct {
ot objecttree.ObjectTree
allIsNew bool
prevOrder string
prevChange *objecttree.Change
nextCachedOrder string
nextCacheChange map[string]struct{}
needFetchPrevOrderId bool
}
func (a *storeApply) Apply() (err error) {
iterErr := a.ot.IterateRoot(UnmarshalStoreChange, func(change *objecttree.Change) bool {
func (a *storeApply) Apply() error {
var lastErr error
err := a.ot.IterateRoot(UnmarshalStoreChange, func(change *objecttree.Change) bool {
// not a new change - remember and continue
if !a.allIsNew && !change.IsNew {
return true
}
if err = a.applyChange(change); err != nil {
var prevOrderId string
if a.needFetchPrevOrderId {
prevOrderId, lastErr = a.tx.GetPrevOrderId(change.OrderId)
if lastErr != nil {
log.With("error", lastErr).Error("get prev order")
return false
}
}
lastErr = a.applyChange(prevOrderId, change)
if lastErr != nil {
return false
}
return true
})
if err == nil && iterErr != nil {
return iterErr
}
return
return errors.Join(err, lastErr)
}
func (a *storeApply) applyChange(change *objecttree.Change) (err error) {
func (a *storeApply) applyChange(prevOrderId string, change *objecttree.Change) (err error) {
storeChange, ok := change.Model.(*pb.StoreChange)
if !ok {
// if it is root
@ -50,11 +58,12 @@ func (a *storeApply) applyChange(change *objecttree.Change) (err error) {
return fmt.Errorf("unexpected change content type: %T", change.Model)
}
set := storestate.ChangeSet{
Id: change.Id,
Order: change.OrderId,
Changes: storeChange.ChangeSet,
Creator: change.Identity.Account(),
Timestamp: change.Timestamp,
Id: change.Id,
PrevOrderId: prevOrderId,
Order: change.OrderId,
Changes: storeChange.ChangeSet,
Creator: change.Identity.Account(),
Timestamp: change.Timestamp,
}
err = a.tx.ApplyChangeSet(set)
// Skip invalid changes

View file

@ -98,7 +98,7 @@ func (mw *Middleware) ChatGetMessagesByIds(cctx context.Context, req *pb.RpcChat
func (mw *Middleware) ChatSubscribeLastMessages(cctx context.Context, req *pb.RpcChatSubscribeLastMessagesRequest) *pb.RpcChatSubscribeLastMessagesResponse {
chatService := mustService[chats.Service](mw)
messages, numBefore, err := chatService.SubscribeLastMessages(cctx, req.ChatObjectId, int(req.Limit))
messages, numBefore, err := chatService.SubscribeLastMessages(cctx, req.ChatObjectId, int(req.Limit), req.SubId)
code := mapErrorCode[pb.RpcChatSubscribeLastMessagesResponseErrorCode](err)
return &pb.RpcChatSubscribeLastMessagesResponse{
Messages: messages,
@ -113,7 +113,7 @@ func (mw *Middleware) ChatSubscribeLastMessages(cctx context.Context, req *pb.Rp
func (mw *Middleware) ChatUnsubscribe(cctx context.Context, req *pb.RpcChatUnsubscribeRequest) *pb.RpcChatUnsubscribeResponse {
chatService := mustService[chats.Service](mw)
err := chatService.Unsubscribe(req.ChatObjectId)
err := chatService.Unsubscribe(req.ChatObjectId, req.SubId)
code := mapErrorCode[pb.RpcChatUnsubscribeResponseErrorCode](err)
return &pb.RpcChatUnsubscribeResponse{
Error: &pb.RpcChatUnsubscribeResponseError{
@ -122,3 +122,17 @@ func (mw *Middleware) ChatUnsubscribe(cctx context.Context, req *pb.RpcChatUnsub
},
}
}
func (mw *Middleware) ChatSubscribeToMessagePreviews(cctx context.Context, req *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse {
chatService := mustService[chats.Service](mw)
subId, err := chatService.SubscribeToMessagePreviews(cctx)
code := mapErrorCode[pb.RpcChatSubscribeToMessagePreviewsResponseErrorCode](err)
return &pb.RpcChatSubscribeToMessagePreviewsResponse{
SubId: subId,
Error: &pb.RpcChatSubscribeToMessagePreviewsResponseError{
Code: code,
Description: getErrorDescription(err),
},
}
}

View file

@ -65,7 +65,7 @@ func ExportTree(ctx context.Context, params ExportParams) error {
converter = params.Converter
changes []*treechangeproto.RawTreeChangeWithId
)
err = writeTree.IterateRoot(
err = params.Readable.IterateRoot(
func(change *objecttree.Change, decrypted []byte) (any, error) {
return converter.Unmarshall(change.DataType, decrypted)
},

View file

@ -19,6 +19,7 @@ type ImportResult struct {
List list.AclList
Storage objecttree.Storage
FolderPath string
Store anystore.DB
}
func (i ImportResult) CreateReadableTree(fullTree bool, beforeId string) (objecttree.ReadableObjectTree, error) {
@ -48,11 +49,10 @@ func ImportStorage(ctx context.Context, path string) (res ImportResult, err erro
if err = ziputil.UnzipFolder(path, targetDir); err != nil {
return
}
anyStore, err := anystore.Open(ctx, targetDir, nil)
anyStore, err := anystore.Open(ctx, filepath.Join(targetDir, "db"), nil)
if err != nil {
return
}
defer anyStore.Close()
var (
aclId string
treeId string
@ -92,5 +92,6 @@ func ImportStorage(ctx context.Context, path string) (res ImportResult, err erro
List: acl,
Storage: treeStorage,
FolderPath: targetDir,
Store: anyStore,
}, nil
}

View file

@ -43,10 +43,6 @@ func (e *treeExporter) Export(ctx context.Context, path string, tree objecttree.
defer func() {
_ = os.RemoveAll(exportDirPath)
}()
err = os.Mkdir(dbPath, 0755)
if err != nil {
return
}
anyStore, err := anystore.Open(ctx, dbPath, nil)
if err != nil {
return
@ -76,8 +72,7 @@ func (e *treeExporter) Export(ctx context.Context, path string, tree objecttree.
e.log.Printf("can't fetch localstore info: %v", err)
} else {
if len(data) > 0 {
// TODO: [storage] fix details, take from main
// data[0].Details = transform(data[0].Details, e.anonymized, anonymize.Struct)
data[0].Details = transform(data[0].Details, e.anonymized, anonymize.Details)
data[0].Snippet = transform(data[0].Snippet, e.anonymized, anonymize.Text)
for i, r := range data[0].Relations {
data[0].Relations[i] = transform(r, e.anonymized, anonymize.Relation)

View file

@ -11,7 +11,6 @@ import (
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
@ -546,8 +545,7 @@ func (u *uploader) detectType(buf *fileReader) model.BlockContentFileType {
log.With("error", err).Error("detect MIME")
return model.BlockContentFile_File
}
mediaType, _ := path.Split(mime.String())
return file.DetectTypeByMIME(u.name, mediaType)
return file.DetectTypeByMIME(u.name, mime.String())
}
type FileComponent interface {

View file

@ -438,9 +438,10 @@ func (i *indexer) reindexIDs(ctx context.Context, space smartblock.Space, reinde
func (i *indexer) reindexOutdatedObjects(ctx context.Context, space clientspace.Space) (toReindex, success int, err error) {
store := i.store.SpaceIndex(space.Id())
var entries []headstorage.HeadsEntry
err = space.Storage().HeadStorage().IterateEntries(ctx, headstorage.IterOpts{}, func(entry headstorage.HeadsEntry) (bool, error) {
// skipping Acl
if entry.CommonSnapshot != "" {
if entry.CommonSnapshot != "" && entry.Id != space.Storage().StateStorage().SettingsId() {
entries = append(entries, entry)
}
return true, nil

View file

@ -13,6 +13,7 @@ var allObjectsRelationsWhiteList = []string{
bundle.RelationKeySpaceId.String(),
bundle.RelationKeyId.String(),
bundle.RelationKeyLayout.String(),
bundle.RelationKeyResolvedLayout.String(),
bundle.RelationKeyIsArchived.String(),
bundle.RelationKeyIsDeleted.String(),
bundle.RelationKeyName.String(),
@ -39,6 +40,15 @@ var derivedObjectsWhiteList = append(slices.Clone(allObjectsRelationsWhiteList),
var relationsWhiteList = append(slices.Clone(derivedObjectsWhiteList), bundle.RelationKeyRelationFormat.String())
var relationOptionWhiteList = append(slices.Clone(derivedObjectsWhiteList), bundle.RelationKeyRelationOptionColor.String())
var objectWhiteList = append(slices.Clone(derivedObjectsWhiteList),
bundle.RelationKeyRecommendedRelations.String(),
bundle.RelationKeyRecommendedFeaturedRelations.String(),
bundle.RelationKeyRecommendedLayout.String(),
bundle.RelationKeyLayoutWidth.String(),
bundle.RelationKeyLayoutAlign.String(),
bundle.RelationKeyIconName.String(),
bundle.RelationKeyIconOption.String(),
)
var fileRelationsWhiteList = append(
slices.Clone(documentRelationsWhiteList),
@ -60,7 +70,7 @@ var publishingRelationsWhiteList = map[model.ObjectTypeLayout][]string{
model.ObjectType_todo: todoRelationsWhiteList,
model.ObjectType_set: documentRelationsWhiteList,
model.ObjectType_collection: documentRelationsWhiteList,
model.ObjectType_objectType: derivedObjectsWhiteList,
model.ObjectType_objectType: objectWhiteList,
model.ObjectType_relation: relationsWhiteList,
model.ObjectType_file: fileRelationsWhiteList,
model.ObjectType_pdf: fileRelationsWhiteList,

View file

@ -25,6 +25,13 @@ var (
bundle.RelationKeyBacklinks,
}
defaultSetFeaturedRelationKeys = []domain.RelationKey{
bundle.RelationKeyType,
bundle.RelationKeySetOf,
bundle.RelationKeyTag,
bundle.RelationKeyBacklinks,
}
defaultRecommendedRelationKeys = []domain.RelationKey{
bundle.RelationKeyCreatedDate,
bundle.RelationKeyCreator,
@ -36,10 +43,6 @@ var (
bundle.RelationKeyLastModifiedBy,
bundle.RelationKeyLastOpenedDate,
bundle.RelationKeyAddedDate,
bundle.RelationKeySource,
bundle.RelationKeySourceObject,
bundle.RelationKeyImportType,
bundle.RelationKeyOrigin,
}
fileSpecificRelationKeysMap = map[domain.RelationKey]struct{}{
@ -64,14 +67,19 @@ var (
errRecommendedRelationsAlreadyFilled = fmt.Errorf("recommended featured relations are already filled")
)
func DefaultFeaturedRelationKeys() []domain.RelationKey {
func DefaultFeaturedRelationKeys(typeKey domain.TypeKey) []domain.RelationKey {
if typeKey == bundle.TypeKeySet {
return defaultSetFeaturedRelationKeys
}
return defaultFeaturedRelationKeys
}
// FillRecommendedRelations fills recommendedRelations, recommendedFeaturedRelations, recommendedFileRelations
// and recommendedHiddenRelations based on object's details.
// If these relations are already filled with correct ids, isAlreadyFilled = true is returned
func FillRecommendedRelations(ctx context.Context, deriver ObjectIDDeriver, details *domain.Details) (keys []domain.RelationKey, isAlreadyFilled bool, err error) {
func FillRecommendedRelations(
ctx context.Context, deriver ObjectIDDeriver, details *domain.Details, typeKey domain.TypeKey,
) (keys []domain.RelationKey, isAlreadyFilled bool, err error) {
keys, err = getRelationKeysFromDetails(details)
if err != nil {
if errors.Is(err, errRecommendedRelationsAlreadyFilled) {
@ -99,11 +107,13 @@ func FillRecommendedRelations(ctx context.Context, deriver ObjectIDDeriver, deta
keys = other
}
featuredRelationKeys := DefaultFeaturedRelationKeys(typeKey)
// we should include default system recommended relations and
// exclude default recommended featured relations and default hidden relations
keys = lo.Uniq(append(keys, defaultRecommendedRelationKeys...))
keys = slices.DeleteFunc(keys, func(key domain.RelationKey) bool {
return slices.Contains(append(defaultHiddenRelationKeys, defaultFeaturedRelationKeys...), key)
return slices.Contains(append(defaultHiddenRelationKeys, featuredRelationKeys...), key)
})
relationIds, err := prepareRelationIds(ctx, deriver, keys)
@ -112,7 +122,7 @@ func FillRecommendedRelations(ctx context.Context, deriver ObjectIDDeriver, deta
}
details.SetStringList(bundle.RelationKeyRecommendedRelations, relationIds)
featuredRelationIds, err := prepareRelationIds(ctx, deriver, defaultFeaturedRelationKeys)
featuredRelationIds, err := prepareRelationIds(ctx, deriver, featuredRelationKeys)
if err != nil {
return nil, false, fmt.Errorf("prepare recommended featured relation ids: %w", err)
}
@ -124,7 +134,7 @@ func FillRecommendedRelations(ctx context.Context, deriver ObjectIDDeriver, deta
}
details.SetStringList(bundle.RelationKeyRecommendedHiddenRelations, hiddenRelationIds)
return slices.Concat(keys, fileRecommendedRelationKeys, defaultHiddenRelationKeys, defaultFeaturedRelationKeys), false, nil
return slices.Concat(keys, fileRecommendedRelationKeys, defaultHiddenRelationKeys, featuredRelationKeys), false, nil
}
func getRelationKeysFromDetails(details *domain.Details) ([]domain.RelationKey, error) {

View file

@ -70,7 +70,7 @@ func TestFillRecommendedRelations(t *testing.T) {
})
// when
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details)
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details, bundle.TypeKeyNote)
// then
assert.NoError(t, err)
@ -78,7 +78,7 @@ func TestFillRecommendedRelations(t *testing.T) {
assert.Equal(t, tc.expected, details.GetStringList(bundle.RelationKeyRecommendedRelations))
assert.Equal(t, defaultRecFeatRelIds, details.GetStringList(bundle.RelationKeyRecommendedFeaturedRelations))
assert.Equal(t, defaultRecHiddenRelIds, details.GetStringList(bundle.RelationKeyRecommendedHiddenRelations))
assert.Len(t, keys, len(tc.expected)+3+8) // 3 featured and 8 hidden
assert.Len(t, keys, len(tc.expected)+3+4) // 3 featured and 4 hidden
})
}
@ -91,7 +91,7 @@ func TestFillRecommendedRelations(t *testing.T) {
})
// when
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details)
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details, bundle.TypeKeyPage)
// then
assert.NoError(t, err)
@ -130,7 +130,7 @@ func TestFillRecommendedRelations(t *testing.T) {
})
// when
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details)
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details, bundle.TypeKeyTask)
// then
assert.NoError(t, err)
@ -138,7 +138,7 @@ func TestFillRecommendedRelations(t *testing.T) {
assert.Equal(t, tc.expected, details.GetStringList(bundle.RelationKeyRecommendedRelations))
assert.Equal(t, defaultRecFeatRelIds, details.GetStringList(bundle.RelationKeyRecommendedFeaturedRelations))
assert.Equal(t, defaultRecHiddenRelIds, details.GetStringList(bundle.RelationKeyRecommendedHiddenRelations))
assert.Len(t, keys, len(tc.expected)+3+8) // 3 featured and 8 hidden
assert.Len(t, keys, len(tc.expected)+3+4) // 3 featured and 4 hidden
})
}
@ -146,7 +146,6 @@ func TestFillRecommendedRelations(t *testing.T) {
// given
details := domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{
bundle.RelationKeyOrigin.BundledURL(),
bundle.RelationKeyFileExt.BundledURL(),
bundle.RelationKeyAddedDate.BundledURL(),
bundle.RelationKeyCameraIso.BundledURL(),
@ -156,7 +155,7 @@ func TestFillRecommendedRelations(t *testing.T) {
})
// when
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details)
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details, bundle.TypeKeyProject)
// then
assert.NoError(t, err)
@ -169,7 +168,30 @@ func TestFillRecommendedRelations(t *testing.T) {
bundle.RelationKeyCameraIso.URL(),
bundle.RelationKeyAperture.URL(),
}, details.GetStringList(bundle.RelationKeyRecommendedFileRelations))
assert.Len(t, keys, 17)
assert.Len(t, keys, 13)
})
t.Run("recommendedRelations relations of Set", func(t *testing.T) {
// given
details := domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{
bundle.RelationKeySetOf.BundledURL(),
bundle.RelationKeyType.BundledURL(),
bundle.RelationKeyTag.BundledURL(),
bundle.RelationKeyCreatedDate.BundledURL(),
}),
})
// when
keys, isAlreadyFilled, err := FillRecommendedRelations(nil, &mockDeriver{}, details, bundle.TypeKeySet)
// then
assert.NoError(t, err)
assert.False(t, isAlreadyFilled)
assert.Equal(t, buildRelationIds(defaultRecommendedRelationKeys), details.GetStringList(bundle.RelationKeyRecommendedRelations))
assert.Equal(t, buildRelationIds(defaultSetFeaturedRelationKeys), details.GetStringList(bundle.RelationKeyRecommendedFeaturedRelations))
assert.Equal(t, defaultRecHiddenRelIds, details.GetStringList(bundle.RelationKeyRecommendedHiddenRelations))
assert.Len(t, keys, 4+3+4) // 4 featured + 3 sidebar + 4 hidden
})
}

View file

@ -381,6 +381,9 @@ type EventMatcher struct {
}
func (m EventMatcher) Match(msg *pb.EventMessage) {
if msg == nil || msg.Value == nil {
return
}
switch v := msg.Value.(type) {
case *pb.EventMessageValueOfSubscriptionAdd:
if m.OnAdd != nil {

View file

@ -64,7 +64,7 @@ func newCrossSpaceSubscription(subId string, request subscriptionservice.Subscri
return s, aggregatedResp, nil
}
func (s *crossSpaceSubscription) run() {
func (s *crossSpaceSubscription) run(internalQueue *mb.MB[*pb.EventMessage]) {
for {
msgs, err := s.queue.Wait(s.ctx)
if errors.Is(err, context.Canceled) {
@ -75,11 +75,19 @@ func (s *crossSpaceSubscription) run() {
}
for _, msg := range msgs {
s.patchEvent(msg)
if internalQueue != nil {
err = internalQueue.Add(s.ctx, msg)
if err != nil {
log.Error("add to internal queue", zap.Error(err), zap.String("subId", s.subId))
}
}
}
s.eventSender.Broadcast(&pb.Event{
Messages: msgs,
})
if internalQueue == nil {
s.eventSender.Broadcast(&pb.Event{
Messages: msgs,
})
}
}
}

View file

@ -89,6 +89,9 @@ func (s *service) Subscribe(req subscriptionservice.SubscribeRequest) (*subscrip
if len(req.Sorts) > 0 {
return nil, fmt.Errorf("sorting is not supported")
}
if req.AsyncInit {
return nil, fmt.Errorf("async init is not supported")
}
s.lock.Lock()
defer s.lock.Unlock()
@ -97,7 +100,7 @@ func (s *service) Subscribe(req subscriptionservice.SubscribeRequest) (*subscrip
return nil, fmt.Errorf("new cross space subscription: %w", err)
}
s.subscriptions[req.SubId] = spaceSub
go spaceSub.run()
go spaceSub.run(req.InternalQueue)
return resp, nil
}

View file

@ -172,6 +172,9 @@ func (s *service) Close(ctx context.Context) error {
}
func (s *service) Search(req SubscribeRequest) (resp *SubscribeResponse, err error) {
if req.SpaceId == "" {
return nil, fmt.Errorf("spaceId should not be empty")
}
// todo: removed temp fix after we will have session-scoped subscriptions
// this is to prevent multiple subscriptions with the same id in different spaces
err = s.Unsubscribe(req.SubId)
@ -186,6 +189,9 @@ func (s *service) Search(req SubscribeRequest) (resp *SubscribeResponse, err err
}
func (s *service) SubscribeIdsReq(req pb.RpcObjectSubscribeIdsRequest) (resp *pb.RpcObjectSubscribeIdsResponse, err error) {
if req.SpaceId == "" {
return nil, fmt.Errorf("spaceId should not be empty")
}
// todo: removed temp fix after we will have session-scoped subscriptions
// this is to prevent multiple subscriptions with the same id in different spaces
err = s.Unsubscribe(req.SubId)

View file

@ -62,6 +62,7 @@ func TestService_Search(t *testing.T) {
}
t.Run("dependencies", func(t *testing.T) {
fx := newFixture(t)
defer fx.a.Close(context.Background())
defer fx.ctrl.Finish()
@ -83,7 +84,7 @@ func TestService_Search(t *testing.T) {
require.NoError(t, err)
// Wait enough time to flush pending updates to subscriptions handler
time.Sleep(batchTime + 3*time.Millisecond)
time.Sleep(batchTime + 4*time.Millisecond)
spaceSub.onChange([]*entry{
newEntry("1", domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{

View file

@ -564,6 +564,10 @@
- [Rpc.Chat.SubscribeLastMessages.Request](#anytype-Rpc-Chat-SubscribeLastMessages-Request)
- [Rpc.Chat.SubscribeLastMessages.Response](#anytype-Rpc-Chat-SubscribeLastMessages-Response)
- [Rpc.Chat.SubscribeLastMessages.Response.Error](#anytype-Rpc-Chat-SubscribeLastMessages-Response-Error)
- [Rpc.Chat.SubscribeToMessagePreviews](#anytype-Rpc-Chat-SubscribeToMessagePreviews)
- [Rpc.Chat.SubscribeToMessagePreviews.Request](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Request)
- [Rpc.Chat.SubscribeToMessagePreviews.Response](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response)
- [Rpc.Chat.SubscribeToMessagePreviews.Response.Error](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error)
- [Rpc.Chat.ToggleMessageReaction](#anytype-Rpc-Chat-ToggleMessageReaction)
- [Rpc.Chat.ToggleMessageReaction.Request](#anytype-Rpc-Chat-ToggleMessageReaction-Request)
- [Rpc.Chat.ToggleMessageReaction.Response](#anytype-Rpc-Chat-ToggleMessageReaction-Response)
@ -1421,6 +1425,7 @@
- [Rpc.Chat.GetMessages.Response.Error.Code](#anytype-Rpc-Chat-GetMessages-Response-Error-Code)
- [Rpc.Chat.GetMessagesByIds.Response.Error.Code](#anytype-Rpc-Chat-GetMessagesByIds-Response-Error-Code)
- [Rpc.Chat.SubscribeLastMessages.Response.Error.Code](#anytype-Rpc-Chat-SubscribeLastMessages-Response-Error-Code)
- [Rpc.Chat.SubscribeToMessagePreviews.Response.Error.Code](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error-Code)
- [Rpc.Chat.ToggleMessageReaction.Response.Error.Code](#anytype-Rpc-Chat-ToggleMessageReaction-Response-Error-Code)
- [Rpc.Chat.Unsubscribe.Response.Error.Code](#anytype-Rpc-Chat-Unsubscribe-Response-Error-Code)
- [Rpc.Debug.AccountSelectTrace.Response.Error.Code](#anytype-Rpc-Debug-AccountSelectTrace-Response-Error-Code)
@ -2303,6 +2308,7 @@
| ChatGetMessagesByIds | [Rpc.Chat.GetMessagesByIds.Request](#anytype-Rpc-Chat-GetMessagesByIds-Request) | [Rpc.Chat.GetMessagesByIds.Response](#anytype-Rpc-Chat-GetMessagesByIds-Response) | |
| ChatSubscribeLastMessages | [Rpc.Chat.SubscribeLastMessages.Request](#anytype-Rpc-Chat-SubscribeLastMessages-Request) | [Rpc.Chat.SubscribeLastMessages.Response](#anytype-Rpc-Chat-SubscribeLastMessages-Response) | |
| ChatUnsubscribe | [Rpc.Chat.Unsubscribe.Request](#anytype-Rpc-Chat-Unsubscribe-Request) | [Rpc.Chat.Unsubscribe.Response](#anytype-Rpc-Chat-Unsubscribe-Response) | |
| ChatSubscribeToMessagePreviews | [Rpc.Chat.SubscribeToMessagePreviews.Request](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Request) | [Rpc.Chat.SubscribeToMessagePreviews.Response](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response) | |
| ObjectChatAdd | [Rpc.Object.ChatAdd.Request](#anytype-Rpc-Object-ChatAdd-Request) | [Rpc.Object.ChatAdd.Response](#anytype-Rpc-Object-ChatAdd-Response) | |
@ -10442,6 +10448,7 @@ Get marks list in the selected range in text block.
| ----- | ---- | ----- | ----------- |
| chatObjectId | [string](#string) | | Identifier for the chat |
| limit | [int32](#int32) | | Number of max last messages to return and subscribe |
| subId | [string](#string) | | |
@ -10481,6 +10488,58 @@ Get marks list in the selected range in text block.
<a name="anytype-Rpc-Chat-SubscribeToMessagePreviews"></a>
### Rpc.Chat.SubscribeToMessagePreviews
<a name="anytype-Rpc-Chat-SubscribeToMessagePreviews-Request"></a>
### Rpc.Chat.SubscribeToMessagePreviews.Request
<a name="anytype-Rpc-Chat-SubscribeToMessagePreviews-Response"></a>
### Rpc.Chat.SubscribeToMessagePreviews.Response
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error | [Rpc.Chat.SubscribeToMessagePreviews.Response.Error](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error) | | |
| subId | [string](#string) | | |
<a name="anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error"></a>
### Rpc.Chat.SubscribeToMessagePreviews.Response.Error
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| code | [Rpc.Chat.SubscribeToMessagePreviews.Response.Error.Code](#anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error-Code) | | |
| description | [string](#string) | | |
<a name="anytype-Rpc-Chat-ToggleMessageReaction"></a>
### Rpc.Chat.ToggleMessageReaction
@ -10558,6 +10617,7 @@ Get marks list in the selected range in text block.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| chatObjectId | [string](#string) | | Identifier for the chat |
| subId | [string](#string) | | |
@ -22758,6 +22818,19 @@ Middleware-to-front-end response, that can contain a NULL error or a non-NULL er
<a name="anytype-Rpc-Chat-SubscribeToMessagePreviews-Response-Error-Code"></a>
### Rpc.Chat.SubscribeToMessagePreviews.Response.Error.Code
| Name | Number | Description |
| ---- | ------ | ----------- |
| NULL | 0 | |
| UNKNOWN_ERROR | 1 | |
| BAD_INPUT | 2 | ... |
<a name="anytype-Rpc-Chat-ToggleMessageReaction-Response-Error-Code"></a>
### Rpc.Chat.ToggleMessageReaction.Response.Error.Code
@ -27527,7 +27600,10 @@ Precondition: user A opened a block
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | |
| orderId | [string](#string) | | |
| afterOrderId | [string](#string) | | |
| message | [model.ChatMessage](#anytype-model-ChatMessage) | | |
| subIds | [string](#string) | repeated | |
| dependencies | [google.protobuf.Struct](#google-protobuf-Struct) | repeated | |
@ -27543,6 +27619,7 @@ Precondition: user A opened a block
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | |
| subIds | [string](#string) | repeated | |
@ -27559,6 +27636,7 @@ Precondition: user A opened a block
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | |
| message | [model.ChatMessage](#anytype-model-ChatMessage) | | |
| subIds | [string](#string) | repeated | |
@ -27575,6 +27653,7 @@ Precondition: user A opened a block
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | |
| reactions | [model.ChatMessage.Reactions](#anytype-model-ChatMessage-Reactions) | | |
| subIds | [string](#string) | repeated | |
@ -29012,6 +29091,7 @@ Contains basic information about a user account
| timeZone | [string](#string) | | time zone from config |
| analyticsId | [string](#string) | | |
| networkId | [string](#string) | | network id to which anytype is connected |
| ethereumAddress | [string](#string) | | we have Any PK AND Ethereum PK derived from one seed phrase |

46
go.mod
View file

@ -51,11 +51,11 @@ require (
github.com/hbagdi/go-unsplash v0.0.0-20230414214043-474fc02c9119
github.com/huandu/skiplist v1.2.1
github.com/improbable-eng/grpc-web v0.15.0
github.com/ipfs/boxo v0.28.0
github.com/ipfs/boxo v0.29.0
github.com/ipfs/go-block-format v0.2.0
github.com/ipfs/go-cid v0.5.0
github.com/ipfs/go-datastore v0.8.1
github.com/ipfs/go-ds-flatfs v0.5.4
github.com/ipfs/go-datastore v0.8.2
github.com/ipfs/go-ds-flatfs v0.5.5
github.com/ipfs/go-ipld-format v0.6.0
github.com/ipfs/go-log v1.0.5
github.com/joho/godotenv v1.5.1
@ -81,7 +81,7 @@ require (
github.com/otiai10/copy v1.14.1
github.com/otiai10/opengraph/v2 v2.1.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.21.1
github.com/pseudomuto/protoc-gen-doc v1.5.1
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/samber/lo v1.49.1
@ -104,13 +104,13 @@ require (
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
golang.org/x/image v0.24.0
golang.org/x/image v0.25.0
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.27.0
golang.org/x/sys v0.30.0
golang.org/x/text v0.22.0
google.golang.org/grpc v1.70.0
golang.org/x/net v0.37.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
google.golang.org/grpc v1.71.0
gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20180125164251-1832d8546a9f
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
@ -148,7 +148,7 @@ require (
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/disintegration/imaging v1.6.2 // indirect
@ -157,7 +157,7 @@ require (
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/flopp/go-findfont v0.1.0 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
@ -205,17 +205,17 @@ require (
github.com/ipfs/go-ipfs-util v0.0.3 // indirect
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
github.com/ipfs/go-metrics-interface v0.3.0 // indirect
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jinzhu/copier v0.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-libp2p v0.40.0 // indirect
github.com/libp2p/go-libp2p v0.41.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@ -229,7 +229,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr v0.14.0 // indirect
github.com/multiformats/go-multiaddr v0.15.0 // indirect
github.com/multiformats/go-multicodec v0.9.0 // indirect
github.com/multiformats/go-multistream v0.6.0 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
@ -279,18 +279,18 @@ require (
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
lukechampine.com/blake3 v1.4.0 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
@ -312,7 +312,7 @@ replace gopkg.in/Graylog2/go-gelf.v2 => github.com/anyproto/go-gelf v0.0.0-20210
replace github.com/araddon/dateparse => github.com/mehanizm/dateparse v0.0.0-20210806203422-f82c8742c9f8 // use a fork to support dd.mm.yyyy date format
replace github.com/multiformats/go-multiaddr => github.com/anyproto/go-multiaddr v0.8.1-0.20221213144344-0b6b93adaec4
replace github.com/multiformats/go-multiaddr => github.com/anyproto/go-multiaddr v0.8.1-0.20250307125826-51ba58e2ebc7
replace github.com/gogo/protobuf => github.com/anyproto/protobuf v1.3.3-0.20240201225420-6e325cf0ac38

113
go.sum
View file

@ -84,6 +84,8 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/anyproto/any-store v0.1.8 h1:/bxUVq6sBTwYkmPL2g1xUAWNb3axF+zPhP2dvdEBH68=
github.com/anyproto/any-store v0.1.8/go.mod h1:GpnVhcGm5aUQtOwCnKeTt4jsWgVXZ773WbQVLFdeCFo=
github.com/anyproto/any-store v0.1.7 h1:E3DntI+JXo3h7v0WTUJWH+nm7G4MV0PNOXZ6SFzQ2OU=
github.com/anyproto/any-store v0.1.7/go.mod h1:nbyRoJYOlvSWU1xDOrmgPP96UeoTf4eYZ9k+qqLK9k8=
github.com/anyproto/any-sync v0.6.1 h1:Dasbp7qGQme8diGGpzaDQfSDs5o7PAK3E5rxHHrB/+4=
github.com/anyproto/any-sync v0.6.1/go.mod h1:5js8TNBdqe75zwlr9XEQSVDtwhsvEU2qLeC2wTnT/Fo=
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a h1:ZZM+0OUCQMWSLSflpkf0ZMVo3V76qEDDIXPpQOClNs0=
@ -96,8 +98,8 @@ github.com/anyproto/go-gelf v0.0.0-20210418191311-774bd5b016e7 h1:SyEu5uxZ5nKHEJ
github.com/anyproto/go-gelf v0.0.0-20210418191311-774bd5b016e7/go.mod h1:N7kTiXo+LLnV94mHMr7Veos3I1SZ/ELcbkG8sZvcAPI=
github.com/anyproto/go-log/v2 v2.1.2-0.20220721095711-bcf09ff293b2 h1:X8xiwPlNiSQs1HKguhZyHYs4XFQLWsj566bFsRjN7hM=
github.com/anyproto/go-log/v2 v2.1.2-0.20220721095711-bcf09ff293b2/go.mod h1:TMD+iYDL/QBjspKUN0Ypxpr2IMAz3uGUAsbCeClDQ+4=
github.com/anyproto/go-multiaddr v0.8.1-0.20221213144344-0b6b93adaec4 h1:ZUvfKh0DCmmY1wjMtBPS3ggvCvQA0+spKFsAuSSeCsU=
github.com/anyproto/go-multiaddr v0.8.1-0.20221213144344-0b6b93adaec4/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
github.com/anyproto/go-multiaddr v0.8.1-0.20250307125826-51ba58e2ebc7 h1:SD0mX7Ds438ZP6J1g7qpXN4naRi/Naa0umTvlTba76c=
github.com/anyproto/go-multiaddr v0.8.1-0.20250307125826-51ba58e2ebc7/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/anyproto/go-naturaldate/v2 v2.0.2-0.20230524105841-9829cfd13438 h1:flfZXdcXB2iVHrTZDwSMlJ7RCRS/ydiPw63xWr+waSU=
github.com/anyproto/go-naturaldate/v2 v2.0.2-0.20230524105841-9829cfd13438/go.mod h1:cmdcU4pcVTftMlM89z+d8dLBJc02HtY5tI12yQucUxM=
github.com/anyproto/go-slip10 v1.0.0 h1:uAEtSuudR3jJBOfkOXf3bErxVoxbuKwdoJN55M1i6IA=
@ -229,11 +231,11 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh
github.com/dchesterton/go-jpeg-image-structure/v2 v2.0.0-20240318203529-c3eea088bd38 h1:GDvo0S+xL3iMJYofhBVQVM6EuAcTCoEV1096iN1pedI=
github.com/dchesterton/go-jpeg-image-structure/v2 v2.0.0-20240318203529-c3eea088bd38/go.mod h1:WaARaUjQuSuDCDFAiU/GwzfxMTJBulfEhqEA2Tx6B4Y=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
@ -285,8 +287,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo=
github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -543,6 +545,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -581,20 +584,20 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
github.com/ipfs/boxo v0.28.0 h1:io6nXqN8XMOstB7dQGG5GWnMk4WssoMvva9OADErZdI=
github.com/ipfs/boxo v0.28.0/go.mod h1:eY9w3iTpmZGKzDfEYjm3oK8f+xjv8KJhhNXJwicmd3I=
github.com/ipfs/boxo v0.29.0 h1:clzd7PglUcE+Ufq1KucS3aKID7pzGVaSgcdRsW395t4=
github.com/ipfs/boxo v0.29.0/go.mod h1:c3R52nMlgMsN1tADffYcogKoVRsX1RJE1TMYSpJ4uVs=
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
github.com/ipfs/go-datastore v0.8.1 h1:p4+tWJuopShJgB3kIK0MMvnH6CFPvi7g/S44f2/EHQA=
github.com/ipfs/go-datastore v0.8.1/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0=
github.com/ipfs/go-datastore v0.8.2 h1:Jy3wjqQR6sg/LhyY0NIePZC3Vux19nLtg7dx0TVqr6U=
github.com/ipfs/go-datastore v0.8.2/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0=
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ds-flatfs v0.5.4 h1:rQjd6tiQZgMU8yNmS2iai4FI+JP7EoYLhsjxxAkA/sE=
github.com/ipfs/go-ds-flatfs v0.5.4/go.mod h1:sskCDxKNQT3GMVz7QpBc2DK4qalYHZ12y2iCGp4ZZbU=
github.com/ipfs/go-ds-flatfs v0.5.5 h1:lkx5C99pFBMI7T1sYF7y3v7xIYekNVNMp/95Gm9Y3tY=
github.com/ipfs/go-ds-flatfs v0.5.5/go.mod h1:bM7+m7KFUyv5dp3RBKTr3+OHgZ6h8ydCQkO7tjeO9Z4=
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=
@ -607,12 +610,12 @@ github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99
github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU=
github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY=
github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU=
github.com/ipfs/go-peertaskqueue v0.8.2/go.mod h1:L6QPvou0346c2qPJNiJa6BvOibxDfaiPlqHInmzg0FA=
github.com/ipfs/go-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew=
github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI=
github.com/ipfs/go-test v0.2.1 h1:/D/a8xZ2JzkYqcVcV/7HYlCnc7bv/pKHQiX5TdClkPE=
github.com/ipfs/go-test v0.2.1/go.mod h1:dzu+KB9cmWjuJnXFDYJwC25T3j1GcN57byN+ixmK39M=
github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
@ -662,8 +665,8 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -689,8 +692,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw=
github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc=
github.com/libp2p/go-libp2p v0.40.0 h1:1LOMO3gigxeXFs50HGEc1U79OINewUQB7o4gTKGPC3U=
github.com/libp2p/go-libp2p v0.40.0/go.mod h1:hOzj2EAIYsXpVpBnyA1pRHzpUJGF9nbWiDLjgasnbF0=
github.com/libp2p/go-libp2p v0.41.0 h1:JRaD39dqf/tBBGapJ0T38N73vOaDCsWgcx3mE6HgXWk=
github.com/libp2p/go-libp2p v0.41.0/go.mod h1:Be8QYqC4JW6Xq8buukNeoZJjyT1XUDcGoIooCHm1ye4=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg=
@ -699,8 +702,6 @@ github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUI
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8=
github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE=
github.com/libp2p/go-yamux/v5 v5.0.0 h1:2djUh96d3Jiac/JpGkKs4TO49YhsfLopAoryfPmf+Po=
@ -883,8 +884,8 @@ github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk=
github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
github.com/pion/sctp v1.8.35 h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA=
github.com/pion/sctp v1.8.35/go.mod h1:EcXP8zCYVTRy3W9xtOF7wJm1L1aXfKRQzaM33SjQlzg=
github.com/pion/sctp v1.8.36 h1:owNudmnz1xmhfYje5L/FCav3V9wpPRePHle3Zi+P+M0=
github.com/pion/sctp v1.8.36/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA=
github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
@ -899,8 +900,8 @@ github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
github.com/pion/webrtc/v4 v4.0.9 h1:PyOYMRKJgfy0dzPcYtFD/4oW9zaw3Ze3oZzzbj2LV9E=
github.com/pion/webrtc/v4 v4.0.9/go.mod h1:ViHLVaNpiuvaH8pdiuQxuA9awuE6KVzAXx3vVWilOck=
github.com/pion/webrtc/v4 v4.0.10 h1:Hq/JLjhqLxi+NmCtE8lnRPDr8H4LcNvwg8OxVcdv56Q=
github.com/pion/webrtc/v4 v4.0.10/go.mod h1:ViHLVaNpiuvaH8pdiuQxuA9awuE6KVzAXx3vVWilOck=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -919,8 +920,8 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -1137,10 +1138,10 @@ go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -1190,8 +1191,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1212,8 +1213,8 @@ golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86h
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -1307,8 +1308,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1318,8 +1319,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1337,8 +1338,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1424,8 +1425,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1436,8 +1437,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1453,8 +1454,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1601,8 +1602,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E=
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@ -1628,8 +1629,8 @@ google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1683,8 +1684,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -7952,6 +7952,7 @@ message Rpc {
message Request {
string chatObjectId = 1; // Identifier for the chat
int32 limit = 2; // Number of max last messages to return and subscribe
string subId = 3;
}
message Response {
@ -7976,6 +7977,7 @@ message Rpc {
message Unsubscribe {
message Request {
string chatObjectId = 1; // Identifier for the chat
string subId = 2;
}
message Response {
Error error = 1;
@ -7993,6 +7995,27 @@ message Rpc {
}
}
}
message SubscribeToMessagePreviews {
message Request {}
message Response {
Error error = 1;
string subId = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
}
}

View file

@ -124,18 +124,24 @@ message Event {
message Add {
string id = 1;
string orderId = 2;
string afterOrderId = 6;
model.ChatMessage message = 3;
repeated string subIds = 4;
repeated google.protobuf.Struct dependencies = 5;
}
message Delete {
string id = 1;
repeated string subIds = 2;
}
message Update {
string id = 1;
model.ChatMessage message = 2;
repeated string subIds = 3;
}
message UpdateReactions {
string id = 1;
model.ChatMessage.Reactions reactions = 2;
repeated string subIds = 3;
}
}

View file

@ -394,5 +394,6 @@ service ClientCommands {
rpc ChatGetMessagesByIds (anytype.Rpc.Chat.GetMessagesByIds.Request) returns (anytype.Rpc.Chat.GetMessagesByIds.Response);
rpc ChatSubscribeLastMessages (anytype.Rpc.Chat.SubscribeLastMessages.Request) returns (anytype.Rpc.Chat.SubscribeLastMessages.Response);
rpc ChatUnsubscribe (anytype.Rpc.Chat.Unsubscribe.Request) returns (anytype.Rpc.Chat.Unsubscribe.Response);
rpc ChatSubscribeToMessagePreviews (anytype.Rpc.Chat.SubscribeToMessagePreviews.Request) returns (anytype.Rpc.Chat.SubscribeToMessagePreviews.Response);
rpc ObjectChatAdd (anytype.Rpc.Object.ChatAdd.Request) returns (anytype.Rpc.Object.ChatAdd.Response);
}

View file

@ -5659,6 +5659,55 @@ func (_c *MockClientCommandsServer_ChatSubscribeLastMessages_Call) RunAndReturn(
return _c
}
// ChatSubscribeToMessagePreviews provides a mock function with given fields: _a0, _a1
func (_m *MockClientCommandsServer) ChatSubscribeToMessagePreviews(_a0 context.Context, _a1 *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for ChatSubscribeToMessagePreviews")
}
var r0 *pb.RpcChatSubscribeToMessagePreviewsResponse
if rf, ok := ret.Get(0).(func(context.Context, *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*pb.RpcChatSubscribeToMessagePreviewsResponse)
}
}
return r0
}
// MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChatSubscribeToMessagePreviews'
type MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call struct {
*mock.Call
}
// ChatSubscribeToMessagePreviews is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *pb.RpcChatSubscribeToMessagePreviewsRequest
func (_e *MockClientCommandsServer_Expecter) ChatSubscribeToMessagePreviews(_a0 interface{}, _a1 interface{}) *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call {
return &MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call{Call: _e.mock.On("ChatSubscribeToMessagePreviews", _a0, _a1)}
}
func (_c *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call) Run(run func(_a0 context.Context, _a1 *pb.RpcChatSubscribeToMessagePreviewsRequest)) *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*pb.RpcChatSubscribeToMessagePreviewsRequest))
})
return _c
}
func (_c *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call) Return(_a0 *pb.RpcChatSubscribeToMessagePreviewsResponse) *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call) RunAndReturn(run func(context.Context, *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse) *MockClientCommandsServer_ChatSubscribeToMessagePreviews_Call {
_c.Call.Return(run)
return _c
}
// ChatToggleMessageReaction provides a mock function with given fields: _a0, _a1
func (_m *MockClientCommandsServer) ChatToggleMessageReaction(_a0 context.Context, _a1 *pb.RpcChatToggleMessageReactionRequest) *pb.RpcChatToggleMessageReactionResponse {
ret := _m.Called(_a0, _a1)

View file

@ -26,18 +26,18 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func init() { proto.RegisterFile("pb/protos/service/service.proto", fileDescriptor_93a29dc403579097) }
var fileDescriptor_93a29dc403579097 = []byte{
// 5514 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x5f, 0x6f, 0x24, 0x49,
0x52, 0xc0, 0xb7, 0x5f, 0x58, 0xa8, 0xe3, 0x16, 0xe8, 0x85, 0x65, 0x6f, 0xb9, 0x9b, 0x99, 0x9d,
0x9d, 0xb1, 0x3d, 0x63, 0xbb, 0x3d, 0x3b, 0xb3, 0xff, 0xb8, 0x43, 0x82, 0x1e, 0x7b, 0xec, 0xf5,
0x9d, 0xed, 0x35, 0xee, 0xf6, 0x8c, 0xb4, 0x12, 0x12, 0xe5, 0xaa, 0x74, 0xbb, 0x70, 0x75, 0x65,
0x5d, 0x55, 0x76, 0x7b, 0xfa, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x9c, 0xf8, 0x27, 0x78, 0x42,
0x42, 0x7c, 0x00, 0x3e, 0x06, 0x8f, 0xf7, 0xc8, 0x23, 0xda, 0xfd, 0x0a, 0x7c, 0x00, 0x54, 0xf9,
0x3f, 0xa3, 0x32, 0xb2, 0xca, 0xcb, 0xd3, 0x8c, 0x1c, 0xbf, 0x88, 0xc8, 0xac, 0x8c, 0xcc, 0x8c,
0xcc, 0xca, 0xca, 0x8e, 0xee, 0x96, 0x17, 0x3b, 0x65, 0x45, 0x19, 0xad, 0x77, 0x6a, 0x52, 0x2d,
// 5536 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x4f, 0x6f, 0x24, 0x49,
0x56, 0xc0, 0xa7, 0x2e, 0x0c, 0xe4, 0xb2, 0x03, 0xd4, 0xc0, 0x30, 0x3b, 0xec, 0x76, 0xf7, 0xf4,
0x74, 0xdb, 0xee, 0xb6, 0x5d, 0xee, 0xe9, 0x9e, 0x7f, 0xec, 0x22, 0x41, 0xb5, 0xdd, 0xf6, 0x78,
0xd7, 0xf6, 0x18, 0x57, 0xb9, 0x5b, 0x1a, 0x09, 0x89, 0x74, 0x66, 0xb8, 0x9c, 0x38, 0x2b, 0x23,
0x37, 0x33, 0xaa, 0xdc, 0xb5, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x56, 0xfc, 0x13, 0x9c, 0x90,
0xf8, 0x04, 0x1c, 0xf8, 0x10, 0x1c, 0xf7, 0xc8, 0x11, 0xcd, 0x7c, 0x06, 0xee, 0x28, 0xe3, 0x7f,
0xbc, 0x8c, 0x17, 0x99, 0x1e, 0x4e, 0xdd, 0xf2, 0xfb, 0xbd, 0xf7, 0x22, 0x32, 0x5e, 0x44, 0xbc,
0x88, 0x8c, 0x8c, 0x8a, 0xee, 0x96, 0x17, 0x3b, 0x65, 0x45, 0x19, 0xad, 0x77, 0x6a, 0x52, 0x2d,
0xb3, 0x84, 0xa8, 0x7f, 0x47, 0xfc, 0xcf, 0xc3, 0x37, 0xe3, 0x62, 0xc5, 0x56, 0x25, 0x79, 0xef,
0x5d, 0x43, 0x26, 0x74, 0x3e, 0x8f, 0x8b, 0xb4, 0x16, 0xc8, 0x7b, 0xef, 0x18, 0x09, 0x59, 0x92,
0x82, 0xc9, 0xbf, 0x3f, 0xfd, 0x8f, 0xff, 0x1d, 0x44, 0x6f, 0xed, 0xe6, 0x19, 0x29, 0xd8, 0xae,
0x82, 0xc9, 0xbf, 0x3f, 0xfd, 0xcf, 0xff, 0x1d, 0x44, 0x6f, 0xed, 0xe6, 0x19, 0x29, 0xd8, 0xae,
0xd4, 0x18, 0x7e, 0x19, 0x7d, 0x7b, 0x5c, 0x96, 0x07, 0x84, 0xbd, 0x24, 0x55, 0x9d, 0xd1, 0x62,
0xf8, 0xc1, 0x48, 0x3a, 0x18, 0x9d, 0x95, 0xc9, 0x68, 0x5c, 0x96, 0x23, 0x23, 0x1c, 0x9d, 0x91,
0x1f, 0x2f, 0x48, 0xcd, 0xde, 0x7b, 0x10, 0x86, 0xea, 0x92, 0x16, 0x35, 0x19, 0x5e, 0x46, 0xbf,
@ -180,7 +180,7 @@ var fileDescriptor_93a29dc403579097 = []byte{
0xb4, 0xf4, 0xfa, 0x47, 0xd1, 0xbb, 0x6d, 0xaf, 0x72, 0x22, 0xda, 0xe9, 0x34, 0x05, 0xe6, 0xa2,
0x27, 0xfd, 0x15, 0xa4, 0xfb, 0x7f, 0xd1, 0xfb, 0xd2, 0xc2, 0x7f, 0x42, 0xe7, 0x73, 0x52, 0xa4,
0x24, 0x55, 0x1a, 0x75, 0xb3, 0x7e, 0xfa, 0x0c, 0xb7, 0xab, 0x15, 0x46, 0xb6, 0x86, 0x2e, 0xd1,
0x6f, 0x7e, 0x03, 0x4d, 0x59, 0xb4, 0xff, 0x1c, 0x44, 0x8f, 0xbc, 0x45, 0x53, 0x81, 0xeb, 0x14,
0x6f, 0x7e, 0x03, 0x4d, 0x59, 0xb4, 0xff, 0x18, 0x44, 0x8f, 0xbc, 0x45, 0x53, 0x81, 0xeb, 0x14,
0xf1, 0x77, 0xfa, 0x38, 0xf2, 0x69, 0xea, 0xa2, 0x8e, 0xff, 0x1f, 0x16, 0x64, 0x91, 0xff, 0x75,
0x10, 0xdd, 0x37, 0x8a, 0x4d, 0x78, 0xef, 0xd2, 0xe2, 0x32, 0xcf, 0x12, 0xc6, 0xdf, 0xc8, 0x4a,
0x15, 0xfc, 0x71, 0x62, 0x1a, 0xdd, 0x8f, 0x33, 0xa0, 0x69, 0x16, 0xaf, 0x9f, 0x67, 0x35, 0xa3,
@ -366,12 +366,13 @@ var fileDescriptor_93a29dc403579097 = []byte{
0xf5, 0x24, 0xe0, 0x65, 0x53, 0x5c, 0xd9, 0x12, 0x23, 0xdf, 0xbd, 0x79, 0x30, 0xb3, 0x4e, 0x00,
0x1e, 0x9e, 0xaf, 0x0e, 0x53, 0xb8, 0x4e, 0x80, 0xfa, 0x9c, 0x41, 0xd6, 0x09, 0x18, 0xeb, 0x36,
0x9d, 0xde, 0xf7, 0x3a, 0x8a, 0x6b, 0x53, 0x39, 0x4f, 0xd3, 0x79, 0xc1, 0x50, 0xd3, 0x61, 0x0a,
0xee, 0x23, 0xb5, 0xb7, 0xd6, 0x3c, 0x8f, 0xd4, 0xb7, 0xaf, 0xb6, 0xd6, 0x85, 0x99, 0x71, 0x49,
0xaf, 0x27, 0xf9, 0x91, 0x25, 0xff, 0x6f, 0x35, 0x08, 0x21, 0x32, 0x2e, 0xb5, 0x20, 0x61, 0xfb,
0xf9, 0xfb, 0xff, 0xf5, 0xd5, 0x9d, 0xc1, 0xcf, 0xbe, 0xba, 0x33, 0xf8, 0x9f, 0xaf, 0xee, 0x0c,
0x7e, 0xfa, 0xf5, 0x9d, 0x37, 0x7e, 0xf6, 0xf5, 0x9d, 0x37, 0xfe, 0xfb, 0xeb, 0x3b, 0x6f, 0x7c,
0xf9, 0xa6, 0xfc, 0x2d, 0xea, 0x8b, 0x9f, 0xe3, 0xbf, 0x28, 0xfd, 0xec, 0xff, 0x02, 0x00, 0x00,
0xff, 0xff, 0xc9, 0x60, 0xbd, 0x6d, 0xaf, 0x7a, 0x00, 0x00,
0xee, 0x23, 0xb5, 0xb7, 0xd6, 0x3c, 0x8f, 0xd4, 0xb7, 0xaf, 0xb6, 0xd6, 0x85, 0x59, 0x89, 0x8f,
0x53, 0xc5, 0x29, 0x95, 0xc5, 0x90, 0xdf, 0x35, 0xd6, 0x20, 0xf1, 0x71, 0x8b, 0xdd, 0xa2, 0x91,
0xc4, 0xa7, 0x5b, 0xcb, 0x8c, 0x93, 0x7a, 0x7d, 0xcb, 0x8f, 0x50, 0xf9, 0x7f, 0x3b, 0x42, 0x08,
0x91, 0x71, 0xb2, 0x05, 0x09, 0xdb, 0xcf, 0xdf, 0xff, 0xaf, 0xaf, 0xee, 0x0c, 0x7e, 0xf6, 0xd5,
0x9d, 0xc1, 0xff, 0x7c, 0x75, 0x67, 0xf0, 0xd3, 0xaf, 0xef, 0xbc, 0xf1, 0xb3, 0xaf, 0xef, 0xbc,
0xf1, 0xdf, 0x5f, 0xdf, 0x79, 0xe3, 0xcb, 0x37, 0xe5, 0x6f, 0x63, 0x5f, 0xfc, 0x1c, 0xff, 0x85,
0xeb, 0x67, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x94, 0x61, 0xee, 0x3f, 0x7b, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -729,6 +730,7 @@ type ClientCommandsClient interface {
ChatGetMessagesByIds(ctx context.Context, in *pb.RpcChatGetMessagesByIdsRequest, opts ...grpc.CallOption) (*pb.RpcChatGetMessagesByIdsResponse, error)
ChatSubscribeLastMessages(ctx context.Context, in *pb.RpcChatSubscribeLastMessagesRequest, opts ...grpc.CallOption) (*pb.RpcChatSubscribeLastMessagesResponse, error)
ChatUnsubscribe(ctx context.Context, in *pb.RpcChatUnsubscribeRequest, opts ...grpc.CallOption) (*pb.RpcChatUnsubscribeResponse, error)
ChatSubscribeToMessagePreviews(ctx context.Context, in *pb.RpcChatSubscribeToMessagePreviewsRequest, opts ...grpc.CallOption) (*pb.RpcChatSubscribeToMessagePreviewsResponse, error)
ObjectChatAdd(ctx context.Context, in *pb.RpcObjectChatAddRequest, opts ...grpc.CallOption) (*pb.RpcObjectChatAddResponse, error)
}
@ -3283,6 +3285,15 @@ func (c *clientCommandsClient) ChatUnsubscribe(ctx context.Context, in *pb.RpcCh
return out, nil
}
func (c *clientCommandsClient) ChatSubscribeToMessagePreviews(ctx context.Context, in *pb.RpcChatSubscribeToMessagePreviewsRequest, opts ...grpc.CallOption) (*pb.RpcChatSubscribeToMessagePreviewsResponse, error) {
out := new(pb.RpcChatSubscribeToMessagePreviewsResponse)
err := c.cc.Invoke(ctx, "/anytype.ClientCommands/ChatSubscribeToMessagePreviews", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *clientCommandsClient) ObjectChatAdd(ctx context.Context, in *pb.RpcObjectChatAddRequest, opts ...grpc.CallOption) (*pb.RpcObjectChatAddResponse, error) {
out := new(pb.RpcObjectChatAddResponse)
err := c.cc.Invoke(ctx, "/anytype.ClientCommands/ObjectChatAdd", in, out, opts...)
@ -3637,6 +3648,7 @@ type ClientCommandsServer interface {
ChatGetMessagesByIds(context.Context, *pb.RpcChatGetMessagesByIdsRequest) *pb.RpcChatGetMessagesByIdsResponse
ChatSubscribeLastMessages(context.Context, *pb.RpcChatSubscribeLastMessagesRequest) *pb.RpcChatSubscribeLastMessagesResponse
ChatUnsubscribe(context.Context, *pb.RpcChatUnsubscribeRequest) *pb.RpcChatUnsubscribeResponse
ChatSubscribeToMessagePreviews(context.Context, *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse
ObjectChatAdd(context.Context, *pb.RpcObjectChatAddRequest) *pb.RpcObjectChatAddResponse
}
@ -4484,6 +4496,9 @@ func (*UnimplementedClientCommandsServer) ChatSubscribeLastMessages(ctx context.
func (*UnimplementedClientCommandsServer) ChatUnsubscribe(ctx context.Context, req *pb.RpcChatUnsubscribeRequest) *pb.RpcChatUnsubscribeResponse {
return nil
}
func (*UnimplementedClientCommandsServer) ChatSubscribeToMessagePreviews(ctx context.Context, req *pb.RpcChatSubscribeToMessagePreviewsRequest) *pb.RpcChatSubscribeToMessagePreviewsResponse {
return nil
}
func (*UnimplementedClientCommandsServer) ObjectChatAdd(ctx context.Context, req *pb.RpcObjectChatAddRequest) *pb.RpcObjectChatAddResponse {
return nil
}
@ -9536,6 +9551,24 @@ func _ClientCommands_ChatUnsubscribe_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler)
}
func _ClientCommands_ChatSubscribeToMessagePreviews_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(pb.RpcChatSubscribeToMessagePreviewsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ClientCommandsServer).ChatSubscribeToMessagePreviews(ctx, in), nil
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/anytype.ClientCommands/ChatSubscribeToMessagePreviews",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ClientCommandsServer).ChatSubscribeToMessagePreviews(ctx, req.(*pb.RpcChatSubscribeToMessagePreviewsRequest)), nil
}
return interceptor(ctx, in, info, handler)
}
func _ClientCommands_ObjectChatAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(pb.RpcObjectChatAddRequest)
if err := dec(in); err != nil {
@ -10674,6 +10707,10 @@ var _ClientCommands_serviceDesc = grpc.ServiceDesc{
MethodName: "ChatUnsubscribe",
Handler: _ClientCommands_ChatUnsubscribe_Handler,
},
{
MethodName: "ChatSubscribeToMessagePreviews",
Handler: _ClientCommands_ChatSubscribeToMessagePreviews_Handler,
},
{
MethodName: "ObjectChatAdd",
Handler: _ClientCommands_ObjectChatAdd_Handler,

View file

@ -9,7 +9,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const TypeChecksum = "c4f1cd92b533a4b5f533c01185049b8c7c170401c2f0bf09d6d888cad6a61669"
const TypeChecksum = "0d2b4f839dd21d6e7abce40021cda9dbc2ed6caef0cff7bbff21c7c5b815b716"
const (
TypePrefix = "_ot"
)
@ -356,10 +356,10 @@ var (
IconColor: 6,
IconName: "search",
Layout: model.ObjectType_set,
Name: "Set",
Name: "Query",
Readonly: true,
RelationLinks: []*model.RelationLink{MustGetRelationLink(RelationKeyTag), MustGetRelationLink(RelationKeySetOf)},
Revision: 1,
Revision: 2,
Types: []model.SmartBlockType{model.SmartBlockType_Page},
Url: TypePrefix + "set",
},

View file

@ -304,7 +304,7 @@
},
{
"id": "set",
"name": "Set",
"name": "Query",
"types": [
"Page"
],
@ -317,7 +317,7 @@
"setOf"
],
"description": "Query all objects in your space based on types and relations",
"revision": 1
"revision": 2
},
{
"id": "collection",

View file

@ -50,6 +50,10 @@ func OpenDatabaseWithLockCheck(ctx context.Context, path string, config *anystor
return nil, lockCloseNoop, err
}
store, err = anystore.Open(ctx, path, config)
if err != nil {
l.Errorf("failed to open anystore again, %s", err)
return nil, lockCloseNoop, err
}
} else {
l.Errorf("failed to open anystore, non-recoverable error")
// some other error

File diff suppressed because it is too large Load diff

View file

@ -672,6 +672,7 @@ message Account {
string timeZone = 104; // time zone from config
string analyticsId = 105;
string networkId = 106; // network id to which anytype is connected
string ethereumAddress = 107; // we have Any PK AND Ethereum PK derived from one seed phrase
}
message Auth {

View file

@ -98,7 +98,7 @@ func reviseObject(ctx context.Context, log logger.CtxLogger, space dependencies.
details := buildDiffDetails(bundleObject, localObject, isSystem)
if isSystem {
recRelsDetails, err := checkRecommendedRelations(ctx, space, bundleObject, localObject)
recRelsDetails, err := checkRecommendedRelations(ctx, space, bundleObject, localObject, uk)
if err != nil {
log.Error("failed to check recommended relations", zap.Error(err))
}
@ -225,7 +225,7 @@ func checkRelationFormatObjectTypes(
}
func checkRecommendedRelations(
ctx context.Context, space dependencies.SpaceWithCtx, origin, current *domain.Details,
ctx context.Context, space dependencies.SpaceWithCtx, origin, current *domain.Details, uk domain.UniqueKey,
) (newValues []*domain.Detail, err error) {
details := origin.CopyOnlyKeys(
bundle.RelationKeyRecommendedRelations,
@ -233,7 +233,7 @@ func checkRecommendedRelations(
bundle.RelationKeyUniqueKey,
)
_, filled, err := relationutils.FillRecommendedRelations(ctx, space, details)
_, filled, err := relationutils.FillRecommendedRelations(ctx, space, details, domain.TypeKey(uk.InternalKey()))
if filled {
return nil, nil
}

View file

@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"slices"
"strconv"
"sync"
"time"
@ -32,6 +33,7 @@ import (
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
@ -77,19 +79,6 @@ var (
pb.RpcObjectImportUseCaseRequest_GET_STARTED: getStartedZip,
pb.RpcObjectImportUseCaseRequest_EMPTY: emptyZip,
}
// TODO: GO-2009 Now we need to create widgets by hands, widget import is not implemented yet
widgetParams = map[pb.RpcObjectImportUseCaseRequestUseCase][]widgetParameters{
pb.RpcObjectImportUseCaseRequest_EMPTY: {
{model.BlockContentWidget_Link, "bafyreic75ulgm2yz426hjwdjkzqw3kafniknki7qkhufqgrspmxzdppixa", "", true},
},
pb.RpcObjectImportUseCaseRequest_GET_STARTED: {
{model.BlockContentWidget_Link, "bafyreiccjf5vbijsmr55ypsnnzltmcvl4n63g73twwxqnfkn5usoq2iqyi", "", true},
{model.BlockContentWidget_View, "bafyreigigitlypzxjf2trrguj7ict7y6xu2r3qi6hc5eycp47pz66ghlpy", "all", true},
{model.BlockContentWidget_View, "bafyreihruv47l2cm3fh5rl7gou2s3re2dtd6iofjahogp7nockgsaomg5m", "all", true},
{model.BlockContentWidget_CompactList, widget.DefaultWidgetRecentOpen, "", false},
},
}
)
type BuiltinObjects interface {
@ -374,39 +363,43 @@ func (b *builtinObjects) createWidgets(ctx session.Context, spaceId string, useC
}
widgetObjectID := spc.DerivedIDs().Widgets
typeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeyPage)
if err != nil {
log.Errorf("failed to get type id: %w", err)
return
}
// todo: rewrite to use CreateTypeWidgetIfMissing in block.Service
if err = cache.DoStateCtx(b.objectGetter, ctx, widgetObjectID, func(s *state.State, w widget.Widget) error {
for _, param := range widgetParams[useCase] {
objectID := param.objectID
if param.isObjectIDChanged {
objectID, err = b.getNewObjectID(spc.Id(), objectID)
if err != nil {
log.Errorf("Skipping creation of widget block as failed to get new object id using old one '%s': %v", objectID, err)
continue
}
}
request := &pb.RpcBlockCreateWidgetRequest{
ContextId: widgetObjectID,
Position: model.Block_Bottom,
WidgetLayout: param.layout,
Block: &model.Block{
Content: &model.BlockContentOfLink{
Link: &model.BlockContentLink{
TargetBlockId: objectID,
Style: model.BlockContentLink_Page,
IconSize: model.BlockContentLink_SizeNone,
CardStyle: model.BlockContentLink_Inline,
Description: model.BlockContentLink_None,
},
targets := s.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
if slices.Contains(targets, typeId) {
return nil
}
targets = append(targets, typeId)
s.Details().Set(bundle.RelationKeyAutoWidgetTargets, domain.StringList(targets))
objectID, e := spc.DeriveObjectID(nil, domain.MustUniqueKey(coresb.SmartBlockTypeObjectType, bundle.TypeKeyPage.String()))
if e != nil {
return fmt.Errorf("failed to derive page type object id: %w", err)
}
request := &pb.RpcBlockCreateWidgetRequest{
ContextId: widgetObjectID,
Position: model.Block_Bottom,
WidgetLayout: model.BlockContentWidget_View,
Block: &model.Block{
Content: &model.BlockContentOfLink{
Link: &model.BlockContentLink{
TargetBlockId: objectID,
Style: model.BlockContentLink_Page,
IconSize: model.BlockContentLink_SizeNone,
CardStyle: model.BlockContentLink_Inline,
Description: model.BlockContentLink_None,
},
},
}
if param.viewID != "" {
request.ViewId = param.viewID
}
if _, err = w.CreateBlock(s, request); err != nil {
log.Errorf("Failed to make Widget blocks: %v", err)
}
},
}
if _, e = w.CreateBlock(s, request); err != nil {
return fmt.Errorf("failed to make Widget block: %v", e)
}
return nil
}); err != nil {