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

GO-4459 Merge branch 'main' into GO-4459-rest-api-docs

This commit is contained in:
Jannis Metrikat 2025-03-08 09:51:27 +01:00
commit 2cf446429c
No known key found for this signature in database
GPG key ID: B223CAC5AAF85615
126 changed files with 4494 additions and 3196 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

@ -12,7 +12,7 @@ on:
run-on-runner:
description: 'Specify the runner to use'
required: true
default: 'arm64'
default: 'mac-mini-org-heart'
permissions:
@ -22,18 +22,22 @@ permissions:
jobs:
wait_for_perftest:
uses: ./.github/workflows/reusable_wait_for_perftest.yml
build:
runs-on: ${{ github.event_name == 'push' && 'arm64' || (github.event.inputs.run-on-runner || 'arm64') }}
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
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.run-on-runner }}" != "arm64" ]]; then
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.run-on-runner }}" != "mac-mini-org-heart" ]]; then
echo "Invalid runner"
exit 1
fi
- name: Install Go
if: runner.name != 'mac-mini-org-heart'
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
uses: actions/setup-go@v4
with:
go-version: 1.23.2
@ -50,9 +54,14 @@ jobs:
uses: actions/checkout@v3
- name: Install brew and node deps
if: runner.name != 'mac-mini-org-heart'
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
run: make install-brew-and-node-deps
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: '16.2'
- name: Set env vars
env:
UNSPLASH_KEY: ${{ secrets.UNSPLASH_KEY }}

View file

@ -14,12 +14,14 @@ on:
options:
- alpha
- beta
# schedule:
# - cron: '0 0 * * *' # every day at midnight
# filters:
# branches:
# include:
# - 'nightly-ci-test'
run-on-runner:
description: 'Specify the runner to use'
required: true
default: 'mac-mini-org-heart'
type: choice
options:
- mac-mini-org-heart
- macos-14
permissions:
actions: 'write'
@ -27,10 +29,15 @@ permissions:
contents: 'write'
jobs:
wait_for_perftest:
uses: ./.github/workflows/reusable_wait_for_perftest.yml
build:
runs-on: 'macos-14'
needs: wait_for_perftest
runs-on: ${{ github.event.inputs.run-on-runner }}
steps:
- name: Install Go
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
uses: actions/setup-go@v4
with:
go-version: 1.23.2
@ -47,8 +54,14 @@ jobs:
uses: actions/checkout@v3
- name: Install brew and node deps
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
run: make install-brew-and-node-deps
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: '16.2'
- name: Nightly mode env settings
shell: bash
run: |

View file

@ -6,7 +6,7 @@ on:
run-on-runner:
description: 'Specify the runner to use'
required: true
default: 'arm64'
default: 'mac-mini-org-heart'
perf-test:
description: 'Run perf test times'
required: true
@ -26,11 +26,15 @@ permissions:
jobs:
wait_for_self_hosted_mac_mini:
uses: ./.github/workflows/reusable_wait_for_self_hosted_mac_mini.yml
perftests-grafana:
runs-on: ${{ github.event.inputs.run-on-runner || 'arm64' }}
needs: wait_for_self_hosted_mac_mini
runs-on: ${{ github.event.inputs.run-on-runner || 'mac-mini-org-heart' }}
steps:
- name: Install Go
if: runner.name != 'mac-mini-org-heart'
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
uses: actions/setup-go@v4
with:
go-version: 1.23.2
@ -47,7 +51,7 @@ jobs:
uses: actions/checkout@v3
- name: Install brew and node deps
if: runner.name != 'mac-mini-org-heart'
if: ${{ !startsWith(runner.name, 'mac-mini-runner-') }}
run: make install-brew-and-node-deps
- name: Set env vars

View file

@ -8,7 +8,7 @@ on:
run-on-runner-mac:
description: 'Specify the runner to use on MacOS'
required: true
default: 'ARM64'
default: 'mac-mini-org-heart'
run-on-runner-win:
description: 'Specify the runner to use on Windows'
required: true
@ -31,9 +31,13 @@ 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: 'ARM64'
runs-on: 'mac-mini-org-heart'
steps:
- name: Setup GO
run: |

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

1
.gitignore vendored
View file

@ -20,3 +20,4 @@ build
/core/anytype/config/nodes/custom.yml
/.direnv/
/.envrc
*.swp

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

@ -306,7 +306,7 @@ func getTableSizes(mw *core.Middleware) (tables map[string]uint64) {
tables = make(map[string]uint64)
cfg := mw.GetApp().MustComponent(config.CName).(*config.Config)
db, err := sql.Open("sqlite3", cfg.GetSqliteStorePath())
db, err := sql.Open("sqlite3", cfg.GetNewSpaceStorePath())
if err != nil {
fmt.Println("Error opening database:", err)
return

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

@ -136,8 +136,8 @@ func BootstrapConfig(newAccount bool, isStaging bool) *config.Config {
)
}
func BootstrapWallet(rootPath string, derivationResult crypto.DerivationResult) wallet.Wallet {
return wallet.NewWithAccountRepo(rootPath, derivationResult)
func BootstrapWallet(rootPath string, derivationResult crypto.DerivationResult, lang string) wallet.Wallet {
return wallet.NewWithAccountRepo(rootPath, derivationResult, lang)
}
func StartNewApp(ctx context.Context, clientWithVersion string, components ...app.Component) (a *app.App, err error) {
@ -157,14 +157,8 @@ func StartNewApp(ctx context.Context, clientWithVersion string, components ...ap
totalSpent := time.Since(startTime)
l := log.With(zap.Int64("total", totalSpent.Milliseconds()))
stat := a.StartStat()
event := &metrics.AppStart{
TotalMs: stat.SpentMsTotal,
PerCompMs: stat.SpentMsPerComp,
Extra: map[string]interface{}{},
}
if v, ok := ctx.Value(metrics.CtxKeyRPC).(string); ok {
event.Request = v
l = l.With(zap.String("rpc", v))
}
@ -181,19 +175,10 @@ func StartNewApp(ctx context.Context, clientWithVersion string, components ...ap
for _, field := range c.GetLogFields() {
field.Key = comp.Name() + "_" + field.Key
l = l.With(field)
if field.String != "" {
event.Extra[field.Key] = field.String
} else {
event.Extra[field.Key] = field.Integer
}
}
}
})
if metrics.Enabled {
metrics.Service.Send(event)
}
if totalSpent > WarningAfter {
l.Warn("app started")
} else {

View file

@ -194,7 +194,7 @@ func (c *Config) initFromFileAndEnv(repoPath string) error {
if len(split) == 1 {
return fmt.Errorf("failed to split repo path: %s", repoPath)
}
c.SqliteTempPath = filepath.Join(split[0], "cache")
c.SqliteTempPath = filepath.Join(split[0], "files")
c.AnyStoreConfig.SQLiteConnectionOptions = make(map[string]string)
c.AnyStoreConfig.SQLiteConnectionOptions["temp_store_directory"] = "'" + c.SqliteTempPath + "'"
}

View file

@ -62,7 +62,7 @@ func (s *Service) AccountCreate(ctx context.Context, req *pb.RpcAccountCreateReq
}
comps := []app.Component{
cfg,
anytype.BootstrapWallet(s.rootPath, derivationResult),
anytype.BootstrapWallet(s.rootPath, derivationResult, s.fulltextPrimaryLanguage),
s.eventSender,
}

View file

@ -87,6 +87,7 @@ func (s *Service) RecoverFromLegacy(req *pb.RpcAccountRecoverFromLegacyExportReq
return RecoverFromLegacyResponse{}, ErrAccountMismatch
}
s.rootPath = req.RootPath
s.fulltextPrimaryLanguage = req.FulltextPrimaryLanguage
err = os.MkdirAll(s.rootPath, 0700)
if err != nil {
return RecoverFromLegacyResponse{}, anyerror.CleanupError(err)
@ -134,7 +135,7 @@ func (s *Service) RecoverFromLegacy(req *pb.RpcAccountRecoverFromLegacyExportReq
func (s *Service) startApp(cfg *config.Config, derivationResult crypto.DerivationResult) error {
comps := []app.Component{
cfg,
anytype.BootstrapWallet(s.rootPath, derivationResult),
anytype.BootstrapWallet(s.rootPath, derivationResult, s.fulltextPrimaryLanguage),
s.eventSender,
}

View file

@ -41,7 +41,7 @@ func (s *Service) AccountSelect(ctx context.Context, req *pb.RpcAccountSelectReq
if req.Id == "" {
return nil, ErrEmptyAccountID
}
curMigration := s.migrationManager.getOrCreateMigration(req.RootPath, req.Id)
curMigration := s.migrationManager.getOrCreateMigration(req.RootPath, req.Id, req.FulltextPrimaryLanguage)
if !curMigration.successful() {
return nil, ErrAccountStoreIsNotMigrated
}
@ -82,16 +82,30 @@ func (s *Service) AccountSelect(ctx context.Context, req *pb.RpcAccountSelectReq
}
metrics.Service.SetWorkingDir(req.RootPath, req.Id)
return s.start(ctx, req.Id, req.RootPath, req.DisableLocalNetworkSync, req.JsonApiListenAddr, req.PreferYamuxTransport, req.NetworkMode, req.NetworkCustomConfigFilePath)
return s.start(ctx, req.Id, req.RootPath, req.DisableLocalNetworkSync, req.JsonApiListenAddr,
req.PreferYamuxTransport, req.NetworkMode, req.NetworkCustomConfigFilePath, req.FulltextPrimaryLanguage)
}
func (s *Service) start(ctx context.Context, id string, rootPath string, disableLocalNetworkSync bool, jsonApiListenAddr string, preferYamux bool, networkMode pb.RpcAccountNetworkMode, networkConfigFilePath string) (*model.Account, error) {
func (s *Service) start(
ctx context.Context,
id string,
rootPath string,
disableLocalNetworkSync bool,
jsonApiListenAddr string,
preferYamux bool,
networkMode pb.RpcAccountNetworkMode,
networkConfigFilePath string,
lang string,
) (*model.Account, error) {
ctx, task := trace2.NewTask(ctx, "application.start")
defer task.End()
if rootPath != "" {
s.rootPath = rootPath
}
if lang != "" {
s.fulltextPrimaryLanguage = lang
}
if s.mnemonic == "" {
return nil, ErrNoMnemonicProvided
}
@ -129,7 +143,7 @@ func (s *Service) start(ctx context.Context, id string, rootPath string, disable
}
comps := []app.Component{
cfg,
anytype.BootstrapWallet(s.rootPath, res),
anytype.BootstrapWallet(s.rootPath, res, s.fulltextPrimaryLanguage),
s.eventSender,
}

View file

@ -61,6 +61,7 @@ func (s *Service) AccountChangeNetworkConfigAndRestart(ctx context.Context, req
}
rootPath := s.app.MustComponent(walletComp.CName).(walletComp.Wallet).RootPath()
lang := s.app.MustComponent(walletComp.CName).(walletComp.Wallet).FtsPrimaryLang()
accountId := s.app.MustComponent(walletComp.CName).(walletComp.Wallet).GetAccountPrivkey().GetPublic().Account()
conf := s.app.MustComponent(config.CName).(*config.Config)
@ -89,7 +90,8 @@ func (s *Service) AccountChangeNetworkConfigAndRestart(ctx context.Context, req
return ErrFailedToStopApplication
}
_, err = s.start(ctx, accountId, rootPath, conf.DontStartLocalNetworkSyncAutomatically, conf.JsonApiListenAddr, conf.PeferYamuxTransport, req.NetworkMode, req.NetworkCustomConfigFilePath)
_, err = s.start(ctx, accountId, rootPath, conf.DontStartLocalNetworkSyncAutomatically, conf.JsonApiListenAddr,
conf.PeferYamuxTransport, req.NetworkMode, req.NetworkCustomConfigFilePath, lang)
return err
}

View file

@ -25,7 +25,7 @@ func (s *Service) AccountMigrate(ctx context.Context, req *pb.RpcAccountMigrateR
if s.rootPath == "" {
s.rootPath = req.RootPath
}
return s.migrationManager.getOrCreateMigration(req.RootPath, req.Id).wait()
return s.migrationManager.getOrCreateMigration(req.RootPath, req.Id, req.FulltextPrimaryLanguage).wait()
}
func (s *Service) AccountMigrateCancel(ctx context.Context, req *pb.RpcAccountMigrateCancelRequest) error {
@ -37,7 +37,7 @@ func (s *Service) AccountMigrateCancel(ctx context.Context, req *pb.RpcAccountMi
return nil
}
func (s *Service) migrate(ctx context.Context, id string) error {
func (s *Service) migrate(ctx context.Context, id, lang string) error {
res, err := core.WalletAccountAt(s.mnemonic, 0)
if err != nil {
return err
@ -53,7 +53,7 @@ func (s *Service) migrate(ctx context.Context, id string) error {
cfg.DisableNetworkIdCheck = true
comps := []app.Component{
cfg,
anytype.BootstrapWallet(s.rootPath, res),
anytype.BootstrapWallet(s.rootPath, res, lang),
s.eventSender,
}
a := &app.App{}
@ -75,21 +75,23 @@ type migration struct {
err error
id string
done chan struct{}
lang string
}
func newMigration(m *migrationManager, id string) *migration {
func newMigration(m *migrationManager, id, lang string) *migration {
ctx, cancel := context.WithCancel(context.Background())
return &migration{
ctx: ctx,
cancel: cancel,
done: make(chan struct{}),
id: id,
lang: lang,
manager: m,
}
}
func newSuccessfulMigration(manager *migrationManager, id string) *migration {
m := newMigration(manager, id)
func newSuccessfulMigration(manager *migrationManager, id, lang string) *migration {
m := newMigration(manager, id, lang)
m.setFinished(nil, false)
return m
}
@ -127,7 +129,7 @@ func (m *migration) wait() error {
return m.err
}
m.mx.Unlock()
err := m.manager.service.migrate(m.ctx, m.id)
err := m.manager.service.migrate(m.ctx, m.id, m.lang)
if err != nil {
m.setFinished(err, true)
return err
@ -184,7 +186,7 @@ func (m *migrationManager) isRunning() bool {
return m.runningMigration != ""
}
func (m *migrationManager) getOrCreateMigration(rootPath, id string) *migration {
func (m *migrationManager) getOrCreateMigration(rootPath, id, lang string) *migration {
m.Lock()
defer m.Unlock()
if m.migrations == nil {
@ -194,14 +196,14 @@ func (m *migrationManager) getOrCreateMigration(rootPath, id string) *migration
sqlitePath := filepath.Join(rootPath, id, config.SpaceStoreSqlitePath)
baderPath := filepath.Join(rootPath, id, config.SpaceStoreBadgerPath)
if anyPathExists([]string{sqlitePath, baderPath}) {
m.migrations[id] = newMigration(m, id)
m.migrations[id] = newMigration(m, id, lang)
} else {
m.migrations[id] = newSuccessfulMigration(m, id)
m.migrations[id] = newSuccessfulMigration(m, id, lang)
}
}
if m.migrations[id].finished() && !m.migrations[id].successful() {
// resetting migration
m.migrations[id] = newMigration(m, id)
m.migrations[id] = newMigration(m, id, lang)
}
return m.migrations[id]
}

View file

@ -25,12 +25,13 @@ type Service struct {
// memoized private key derived from mnemonic, used for signing session tokens
sessionSigningKey []byte
rootPath string
clientWithVersion string
eventSender event.Sender
sessions session.Service
traceRecorder *traceRecorder
migrationManager *migrationManager
rootPath string
fulltextPrimaryLanguage string
clientWithVersion string
eventSender event.Sender
sessions session.Service
traceRecorder *traceRecorder
migrationManager *migrationManager
appAccountStartInProcessCancel context.CancelFunc
appAccountStartInProcessCancelMutex sync.Mutex

View file

@ -17,6 +17,7 @@ func (s *Service) WalletCreate(req *pb.RpcWalletCreateRequest) (string, error) {
defer s.lock.Unlock()
s.rootPath = req.RootPath
s.fulltextPrimaryLanguage = req.FulltextPrimaryLanguage
err := os.MkdirAll(s.rootPath, 0700)
if err != nil {

View file

@ -32,5 +32,6 @@ func (s *Service) WalletRecover(req *pb.RpcWalletRecoverRequest) error {
return err
}
s.rootPath = req.RootPath
s.fulltextPrimaryLanguage = req.FulltextPrimaryLanguage
return nil
}

View file

@ -208,7 +208,6 @@ func (a *accountObject) initState(st *state.State) error {
template.InitTemplate(st,
template.WithTitle,
template.WithForcedObjectTypes([]domain.TypeKey{bundle.TypeKeyProfile}),
template.WithResolvedLayout(model.ObjectType_profile),
template.WithLayout(model.ObjectType_profile),
template.WithDetail(bundle.RelationKeyLayoutAlign, domain.Int64(model.Block_AlignCenter)),
)

View file

@ -260,8 +260,7 @@ func (bs *basic) setDetailSpecialCases(st *state.State, detail domain.Detail) er
return fmt.Errorf("can't change object type directly: %w", domain.ErrValidationFailed)
}
if detail.Key == bundle.RelationKeyResolvedLayout {
// special case when client sets the layout detail directly instead of using SetLayoutInState command
return bs.SetLayoutInState(st, model.ObjectTypeLayout(detail.Value.Int64()), false)
return fmt.Errorf("can't change object layout directly: %w", domain.ErrValidationFailed)
}
if detail.Key == bundle.RelationKeyRecommendedLayout {
// nolint:gosec
@ -380,7 +379,7 @@ func (bs *basic) SetLayoutInState(s *state.State, toLayout model.ObjectTypeLayou
return fmt.Errorf("layout change is restricted for object '%s': %w", bs.Id(), err)
}
}
s.SetDetail(bundle.RelationKeyResolvedLayout, domain.Int64(toLayout))
if err = bs.layoutConverter.Convert(s, fromLayout, toLayout, ignoreRestriction); err != nil {
return fmt.Errorf("convert layout: %w", err)
}

View file

@ -1109,7 +1109,7 @@ func addDescription(st *smarttest.SmartTest, description string) {
func addRelations(st *smarttest.SmartTest) {
newState := st.Doc.NewState()
template.InitTemplate(newState, template.RequireHeader)
template.InitTemplate(newState, template.WithFeaturedRelations)
template.InitTemplate(newState, template.WithFeaturedRelationsBlock)
template.InitTemplate(newState, template.WithForcedDescription)
state.ApplyState("", newState, false)
}

View file

@ -82,6 +82,7 @@ func isPageLayout(layout model.ObjectTypeLayout) bool {
model.ObjectType_todo,
model.ObjectType_note,
model.ObjectType_profile,
model.ObjectType_bookmark,
}, layout)
}

View file

@ -218,3 +218,16 @@ func TestInsertGroupRelationKey(t *testing.T) {
})
}
}
func TestLayout_isConversionAllowed(t *testing.T) {
lc := layoutConverter{}
assert.True(t, lc.isConversionAllowed(model.ObjectType_basic, model.ObjectType_todo))
assert.True(t, lc.isConversionAllowed(model.ObjectType_profile, model.ObjectType_note))
assert.True(t, lc.isConversionAllowed(model.ObjectType_basic, model.ObjectType_bookmark))
assert.True(t, lc.isConversionAllowed(model.ObjectType_bookmark, model.ObjectType_note))
assert.True(t, lc.isConversionAllowed(model.ObjectType_set, model.ObjectType_collection))
assert.False(t, lc.isConversionAllowed(model.ObjectType_collection, model.ObjectType_set))
assert.False(t, lc.isConversionAllowed(model.ObjectType_set, model.ObjectType_basic))
assert.False(t, lc.isConversionAllowed(model.ObjectType_todo, model.ObjectType_collection))
assert.False(t, lc.isConversionAllowed(model.ObjectType_basic, model.ObjectType_relation))
}

View file

@ -58,7 +58,6 @@ func (p *Dashboard) CreationStateMigration(ctx *smartblock.InitContext) migratio
template.InitTemplate(st,
template.WithObjectTypes([]domain.TypeKey{bundle.TypeKeyDashboard}),
template.WithLayout(model.ObjectType_dashboard),
template.WithResolvedLayout(model.ObjectType_dashboard),
template.WithEmpty,
template.WithDetailName("Home"),
template.WithDetailIconEmoji("🏠"),

View file

@ -176,11 +176,12 @@ func (f *ObjectFactory) New(space smartblock.Space, sbType coresb.SmartBlockType
coresb.SmartBlockTypeDate,
coresb.SmartBlockTypeBundledRelation,
coresb.SmartBlockTypeBundledObjectType,
coresb.SmartBlockTypeObjectType,
coresb.SmartBlockTypeRelation,
coresb.SmartBlockTypeRelationOption,
coresb.SmartBlockTypeChatObject:
return f.newPage(space.Id(), sb), nil
case coresb.SmartBlockTypeObjectType:
return f.newObjectType(space.Id(), sb), nil
case coresb.SmartBlockTypeArchive:
return NewArchive(sb, store), nil
case coresb.SmartBlockTypeHome:

View file

@ -0,0 +1,447 @@
package editor
import (
"context"
"errors"
"fmt"
"slices"
"github.com/anyproto/any-sync/app/ocache"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/clipboard"
"github.com/anyproto/anytype-heart/core/block/editor/dataview"
"github.com/anyproto/anytype-heart/core/block/editor/file"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/editor/stext"
"github.com/anyproto/anytype-heart/core/block/editor/template"
"github.com/anyproto/anytype-heart/core/block/migration"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/block/source"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/relationutils"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
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/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
var typeRequiredRelations = append(typeAndRelationRequiredRelations,
bundle.RelationKeyRecommendedRelations,
bundle.RelationKeyRecommendedFeaturedRelations,
bundle.RelationKeyRecommendedHiddenRelations,
bundle.RelationKeyRecommendedFileRelations,
bundle.RelationKeyRecommendedLayout,
bundle.RelationKeySmartblockTypes,
bundle.RelationKeyIconOption,
bundle.RelationKeyIconName,
)
type ObjectType struct {
smartblock.SmartBlock
basic.AllOperations
basic.IHistory
stext.Text
clipboard.Clipboard
source.ChangeReceiver
dataview.Dataview
spaceIndex spaceindex.Store
}
func (f *ObjectFactory) newObjectType(spaceId string, sb smartblock.SmartBlock) *ObjectType {
store := f.objectStore.SpaceIndex(spaceId)
fileComponent := file.NewFile(sb, f.fileBlockService, f.picker, f.processService, f.fileUploaderService)
return &ObjectType{
SmartBlock: sb,
ChangeReceiver: sb.(source.ChangeReceiver),
AllOperations: basic.NewBasic(sb, store, f.layoutConverter, f.fileObjectService),
IHistory: basic.NewHistory(sb),
Text: stext.NewText(
sb,
store,
f.eventSender,
),
Clipboard: clipboard.NewClipboard(
sb,
fileComponent,
f.tempDirProvider,
store,
f.fileService,
f.fileObjectService,
),
Dataview: dataview.NewDataview(sb, store),
spaceIndex: store,
}
}
func (ot *ObjectType) Init(ctx *smartblock.InitContext) (err error) {
ctx.RequiredInternalRelationKeys = append(ctx.RequiredInternalRelationKeys, typeRequiredRelations...)
if err = ot.SmartBlock.Init(ctx); err != nil {
return
}
ot.AddHook(ot.syncLayoutForObjectsAndTemplates, smartblock.HookAfterApply)
return nil
}
func (ot *ObjectType) CreationStateMigration(ctx *smartblock.InitContext) migration.Migration {
return migration.Migration{
Version: 2,
Proc: func(s *state.State) {
if len(ctx.ObjectTypeKeys) > 0 && len(ctx.State.ObjectTypeKeys()) == 0 {
ctx.State.SetObjectTypeKeys(ctx.ObjectTypeKeys)
}
templates := []template.StateTransformer{
template.WithEmpty,
template.WithObjectTypes(ctx.State.ObjectTypeKeys()),
template.WithTitle,
template.WithLayout(model.ObjectType_objectType),
}
template.InitTemplate(s, templates...)
},
}
}
func (ot *ObjectType) StateMigrations() migration.Migrations {
return migration.MakeMigrations([]migration.Migration{
{
Version: 2,
Proc: func(s *state.State) {},
},
{
Version: 3,
Proc: ot.featuredRelationsMigration,
},
})
}
func (ot *ObjectType) featuredRelationsMigration(s *state.State) {
if ot.Type() != coresb.SmartBlockTypeObjectType {
return
}
if s.HasRelation(bundle.RelationKeyRecommendedFeaturedRelations.String()) {
return
}
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()))
if err != nil {
log.Errorf("failed to derive object id: %v", err)
continue
}
featuredRelationIds = append(featuredRelationIds, id)
}
if len(featuredRelationIds) == 0 {
return
}
s.SetDetail(bundle.RelationKeyRecommendedFeaturedRelations, domain.StringList(featuredRelationIds))
recommendedRelations := s.Details().GetStringList(bundle.RelationKeyRecommendedRelations)
oldLen := len(recommendedRelations)
recommendedRelations = slices.DeleteFunc(recommendedRelations, func(s string) bool {
return slices.Contains(featuredRelationIds, s)
})
if oldLen == len(recommendedRelations) {
return
}
s.SetDetail(bundle.RelationKeyRecommendedRelations, domain.StringList(recommendedRelations))
}
type layoutState struct {
layout int64
layoutAlign int64
featuredRelations []string
isLayoutSet bool
isLayoutAlignSet bool
isFeaturedRelationsSet bool
}
func (ls layoutState) isAllSet() bool {
return ls.isLayoutSet && ls.isLayoutAlignSet && ls.isFeaturedRelationsSet
}
func (ls layoutState) isAnySet() bool {
return ls.isLayoutSet || ls.isLayoutAlignSet || ls.isFeaturedRelationsSet
}
type relationIdDeriver struct {
space smartblock.Space
cache map[domain.RelationKey]string
}
func (d *relationIdDeriver) deriveId(key domain.RelationKey) (string, error) {
if d.cache != nil {
if id, found := d.cache[key]; found {
return id, nil
}
}
id, err := d.space.DeriveObjectID(context.Background(), domain.MustUniqueKey(coresb.SmartBlockTypeRelation, key.String()))
if err != nil {
return "", fmt.Errorf("failed to derive relation id: %w", err)
}
if d.cache == nil {
d.cache = map[domain.RelationKey]string{}
}
d.cache[key] = id
return id, nil
}
func (ot *ObjectType) syncLayoutForObjectsAndTemplates(info smartblock.ApplyInfo) error {
newLayout := getLayoutStateFromMessages(info.Events)
if newLayout.isLayoutSet && !isLayoutChangeApplicable(newLayout.layout) {
// if layout change is not applicable, then it is init of some system type. Objects' layout should not be modified
newLayout.isLayoutSet = false
}
if !newLayout.isAnySet() {
// layout details were not changed
return nil
}
oldLayout := getLayoutStateFromParent(info.ParentState)
records, err := ot.queryObjectsAndTemplates()
if err != nil {
return err
}
var (
resultErr error
deriver = relationIdDeriver{space: ot.Space()}
)
for _, record := range records {
id := record.Details.GetString(bundle.RelationKeyId)
if id == "" {
continue
}
changes := collectRelationsChanges(record.Details, newLayout, oldLayout, deriver)
if len(changes.relationsToRemove) > 0 || changes.isFeaturedRelationsChanged {
// we should modify not local relations from object, that's why we apply changes even if object is not in cache
err = ot.Space().Do(id, func(b smartblock.SmartBlock) error {
st := b.NewState()
st.RemoveDetail(changes.relationsToRemove...)
if changes.isFeaturedRelationsChanged {
st.SetDetail(bundle.RelationKeyFeaturedRelations, domain.StringList(changes.newFeaturedRelations))
}
return b.Apply(st)
})
if err != nil {
resultErr = errors.Join(resultErr, err)
}
continue
}
if changes.isLayoutFound || !newLayout.isLayoutSet || record.Details.GetInt64(bundle.RelationKeyResolvedLayout) == newLayout.layout {
// layout detail remains in object or recommendedLayout was not changed or relevant layout is already set, skipping
continue
}
err = ot.Space().DoLockedIfNotExists(id, func() error {
return ot.spaceIndex.ModifyObjectDetails(id, func(details *domain.Details) (*domain.Details, bool, error) {
if details == nil {
return nil, false, nil
}
if details.GetInt64(bundle.RelationKeyResolvedLayout) == newLayout.layout {
return nil, false, nil
}
details.Set(bundle.RelationKeyResolvedLayout, domain.Int64(newLayout.layout))
return details, true, nil
})
})
if err == nil {
continue
}
if !errors.Is(err, ocache.ErrExists) {
resultErr = errors.Join(resultErr, err)
continue
}
if err = ot.Space().Do(id, func(b smartblock.SmartBlock) error {
if cr, ok := b.(source.ChangeReceiver); ok {
// we can do StateAppend here, so resolvedLayout will be injected automatically
return cr.StateAppend(func(d state.Doc) (s *state.State, changes []*pb.ChangeContent, err error) {
return d.NewState(), nil, nil
})
}
return nil
}); err != nil {
resultErr = errors.Join(resultErr, err)
}
}
if resultErr != nil {
return fmt.Errorf("failed to change layout details for objects: %w", resultErr)
}
return nil
}
func isLayoutChangeApplicable(layout int64) bool {
return slices.Contains([]model.ObjectTypeLayout{
model.ObjectType_basic,
model.ObjectType_todo,
model.ObjectType_profile,
model.ObjectType_note,
model.ObjectType_collection,
}, model.ObjectTypeLayout(layout)) // nolint:gosec
}
func getLayoutStateFromMessages(msgs []simple.EventMessage) layoutState {
ls := layoutState{}
for _, ev := range msgs {
if amend := ev.Msg.GetObjectDetailsAmend(); amend != nil {
for _, detail := range amend.Details {
switch detail.Key {
case bundle.RelationKeyRecommendedLayout.String():
ls.layout = int64(detail.Value.GetNumberValue())
ls.isLayoutSet = true
case bundle.RelationKeyRecommendedFeaturedRelations.String():
ls.featuredRelations = pbtypes.GetStringListValue(detail.Value)
ls.isFeaturedRelationsSet = true
case bundle.RelationKeyLayoutAlign.String():
ls.layoutAlign = int64(detail.Value.GetNumberValue())
ls.isLayoutAlignSet = true
}
}
if ls.isAllSet() {
return ls
}
}
}
return ls
}
func getLayoutStateFromParent(ps *state.State) layoutState {
ls := layoutState{}
if ps == nil {
return ls
}
if layout, ok := ps.Details().TryInt64(bundle.RelationKeyRecommendedLayout); ok {
ls.layout = layout
ls.isLayoutSet = true
}
if layoutAlign, ok := ps.Details().TryInt64(bundle.RelationKeyLayoutAlign); ok {
ls.layoutAlign = layoutAlign
ls.isLayoutAlignSet = true
}
featuredRelations, ok := ps.Details().TryStringList(bundle.RelationKeyRecommendedFeaturedRelations)
// featuredRelations can present in objects as empty slice or containing only description
if ok && len(featuredRelations) != 0 && !slices.Equal(featuredRelations, []string{bundle.RelationKeyDescription.String()}) {
ls.featuredRelations = featuredRelations
ls.isFeaturedRelationsSet = true
}
return ls
}
func (ot *ObjectType) queryObjectsAndTemplates() ([]database.Record, error) {
records, err := ot.spaceIndex.Query(database.Query{Filters: []database.FilterRequest{
{
RelationKey: bundle.RelationKeyType,
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.String(ot.Id()),
},
}})
if err != nil {
return nil, fmt.Errorf("failed to get objects of single type: %w", err)
}
templates, err := ot.spaceIndex.Query(database.Query{Filters: []database.FilterRequest{
{
RelationKey: bundle.RelationKeyTargetObjectType,
Condition: model.BlockContentDataviewFilter_Equal,
Value: domain.String(ot.Id()),
},
}})
if err != nil {
return nil, fmt.Errorf("failed to get templates with this target type: %w", err)
}
return append(records, templates...), nil
}
type layoutRelationsChanges struct {
relationsToRemove []domain.RelationKey
isLayoutFound bool
isFeaturedRelationsChanged bool
newFeaturedRelations []string
}
func collectRelationsChanges(details *domain.Details, newLayout, oldLayout layoutState, deriver relationIdDeriver) (changes layoutRelationsChanges) {
changes.relationsToRemove = make([]domain.RelationKey, 0, 2)
if newLayout.isLayoutSet {
layout, found := details.TryInt64(bundle.RelationKeyLayout)
if found {
changes.isLayoutFound = true
if layout == newLayout.layout || oldLayout.isLayoutSet && layout == oldLayout.layout {
changes.relationsToRemove = append(changes.relationsToRemove, bundle.RelationKeyLayout)
}
}
}
if newLayout.isLayoutAlignSet {
layoutAlign, found := details.TryInt64(bundle.RelationKeyLayoutAlign)
if found && (layoutAlign == newLayout.layoutAlign || oldLayout.isLayoutAlignSet && layoutAlign == oldLayout.layoutAlign) {
changes.relationsToRemove = append(changes.relationsToRemove, bundle.RelationKeyLayoutAlign)
}
}
if newLayout.isFeaturedRelationsSet {
featuredRelations, found := details.TryStringList(bundle.RelationKeyFeaturedRelations)
if found && isFeaturedRelationsCorrespondToType(featuredRelations, newLayout, oldLayout, deriver) {
changes.isFeaturedRelationsChanged = true
changes.newFeaturedRelations = []string{}
if slices.Contains(featuredRelations, bundle.RelationKeyDescription.String()) {
changes.newFeaturedRelations = append(changes.newFeaturedRelations, bundle.RelationKeyDescription.String())
}
}
}
return changes
}
func isFeaturedRelationsCorrespondToType(fr []string, newLayout, oldLayout layoutState, deriver relationIdDeriver) bool {
featuredRelationIds := make([]string, 0, len(fr))
for _, key := range fr {
id, err := deriver.deriveId(domain.RelationKey(key))
if err != nil {
log.Errorf("failed to derive relation key %s", key)
return false // let's fallback to true, so featuredRelations won't be changed
}
featuredRelationIds = append(featuredRelationIds, id)
}
if newLayout.isFeaturedRelationsSet && slices.Equal(featuredRelationIds, newLayout.featuredRelations) {
return true
}
return oldLayout.isFeaturedRelationsSet && slices.Equal(featuredRelationIds, oldLayout.featuredRelations)
}

View file

@ -0,0 +1,401 @@
package editor
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/anyproto/any-sync/app/ocache"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock/smarttest"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const (
spaceId = "spc"
)
type fixture struct {
sb *smarttest.SmartTest
store *objectstore.StoreFixture
space *smartblock.MockSpace
*ObjectType
}
func newFixture(t *testing.T, id string) *fixture {
sb := smarttest.New(id)
store := objectstore.NewStoreFixture(t)
spc := smartblock.NewMockSpace(t)
sb.SetSpace(spc)
page := &ObjectType{
SmartBlock: sb,
spaceIndex: store.SpaceIndex(spaceId),
}
return &fixture{
sb: sb,
store: store,
space: spc,
ObjectType: page,
}
}
func TestObjectType_syncLayoutForObjectsAndTemplates(t *testing.T) {
typeId := bundle.TypeKeyTask.URL()
t.Run("recommendedLayout is updated", func(t *testing.T) {
// given
fx := newFixture(t, typeId)
fx.sb.SetType(coresb.SmartBlockTypeObjectType)
fx.store.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("obj1"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_basic)),
// layout detail should be deleted from obj1, because its value equals old recommendedLayout value
},
{
bundle.RelationKeyId: domain.String("obj2"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_todo)),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_todo)),
// layout detail should be deleted from obj2, because its value equals new recommendedLayout value
},
{
bundle.RelationKeyId: domain.String("obj3"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_profile)),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_profile)),
// obj3 should not be modified, because old layout does not correspond to old and new recommendedLayout values
},
{
bundle.RelationKeyId: domain.String("obj4"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
// obj4 does not have layout detail set, so it has nothing to delete
// StateAppend must be called for this object because resolvedLayout must be reinjected
},
{
bundle.RelationKeyId: domain.String("obj5"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_note)),
// obj5 does not have layout detail set, so it has nothing to delete
// StateAppend must be called for this object because resolvedLayout must be reinjected
},
{
bundle.RelationKeyId: domain.String("obj6"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_todo)),
// obj6 does not have layout detail set, so it has nothing to delete
// obj6 will not be modified, because it already has correct resolvedLauout value
},
{
bundle.RelationKeyId: domain.String("tmpl"),
bundle.RelationKeyType: domain.String(bundle.TypeKeyTemplate.URL()),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyTargetObjectType: domain.String(typeId),
// layout detail should be deleted from template, because its value equals old recommendedLayout value
},
})
obj1 := smarttest.New("obj1")
require.NoError(t, obj1.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayout, Value: domain.Int64(int64(model.ObjectType_basic)),
}}, false))
obj2 := smarttest.New("obj2")
require.NoError(t, obj2.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayout, Value: domain.Int64(int64(model.ObjectType_todo)),
}}, false))
obj4 := smarttest.New("obj4")
tmpl := smarttest.New("tmpl")
require.NoError(t, tmpl.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayout, Value: domain.Int64(int64(model.ObjectType_basic)),
}}, false))
fx.space.EXPECT().DoLockedIfNotExists(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func() error) error {
switch id {
case "obj4":
return ocache.ErrExists
case "obj5":
return f()
default:
panic("DoLockedIfNotExists: invalid object id")
}
})
fx.space.EXPECT().Do(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func(smartblock.SmartBlock) error) error {
switch id {
case "obj1":
assert.NoError(t, f(obj1))
case "obj2":
assert.NoError(t, f(obj2))
case "obj4":
assert.NoError(t, f(obj4))
case "tmpl":
assert.NoError(t, f(tmpl))
default:
panic("Do: invalid object id")
}
return nil
})
// when
err := fx.syncLayoutForObjectsAndTemplates(makeApplyInfo(typeId,
// recommendedLayout is changed: basic -> todo
layoutState{isLayoutSet: true, layout: int64(model.ObjectType_basic)},
layoutState{isLayoutSet: true, layout: int64(model.ObjectType_todo)},
))
// then
assert.NoError(t, err)
assert.False(t, obj1.Details().Has(bundle.RelationKeyLayout))
assert.False(t, obj2.Details().Has(bundle.RelationKeyLayout))
assert.False(t, tmpl.Details().Has(bundle.RelationKeyLayout))
assert.True(t, obj4.Results.IsStateAppendCalled)
details, err := fx.spaceIndex.GetDetails("obj5")
require.NoError(t, err)
assert.Equal(t, int64(model.ObjectType_todo), details.GetInt64(bundle.RelationKeyResolvedLayout))
})
t.Run("layoutAlign is updated", func(t *testing.T) {
// given
fx := newFixture(t, typeId)
fx.sb.SetType(coresb.SmartBlockTypeObjectType)
fx.store.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("obj1"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyLayoutAlign: domain.Int64(int64(model.Block_AlignLeft)),
// layoutAlign detail should be deleted from obj1, because its value equals old type layoutAlign value
},
{
bundle.RelationKeyId: domain.String("obj2"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyLayoutAlign: domain.Int64(int64(model.Block_AlignRight)),
// layoutAlign detail should be deleted from obj2, because its value equals new type layoutAlign value
},
{
bundle.RelationKeyId: domain.String("obj3"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyLayoutAlign: domain.Int64(int64(model.Block_AlignCenter)),
// obj3 should not be modified, because layoutAlign does not correspond to old and new type layoutAlign values
},
{
bundle.RelationKeyId: domain.String("obj4"),
bundle.RelationKeyType: domain.String(typeId),
// obj4 does not have layoutAlign detail set, so it has nothing to delete
},
{
bundle.RelationKeyId: domain.String("tmpl"),
bundle.RelationKeyType: domain.String(bundle.TypeKeyTemplate.URL()),
bundle.RelationKeyLayoutAlign: domain.Int64(int64(model.Block_AlignRight)),
bundle.RelationKeyTargetObjectType: domain.String(typeId),
// layoutAlign detail should be deleted from template, because its value equals new type layoutAlign value
},
})
obj1 := smarttest.New("obj1")
require.NoError(t, obj1.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayoutAlign, Value: domain.Int64(int64(model.ObjectType_basic)),
}}, false))
obj2 := smarttest.New("obj2")
require.NoError(t, obj2.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayoutAlign, Value: domain.Int64(int64(model.ObjectType_todo)),
}}, false))
tmpl := smarttest.New("tmpl")
require.NoError(t, tmpl.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyLayoutAlign, Value: domain.Int64(int64(model.ObjectType_basic)),
}}, false))
fx.space.EXPECT().Do(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func(smartblock.SmartBlock) error) error {
switch id {
case "obj1":
assert.NoError(t, f(obj1))
case "obj2":
assert.NoError(t, f(obj2))
case "tmpl":
assert.NoError(t, f(tmpl))
default:
panic("Do: invalid object id")
}
return nil
})
// when
err := fx.syncLayoutForObjectsAndTemplates(makeApplyInfo(typeId,
// recommendedLayout is changed: basic -> todo
layoutState{isLayoutAlignSet: true, layoutAlign: int64(model.Block_AlignLeft)},
layoutState{isLayoutAlignSet: true, layoutAlign: int64(model.Block_AlignRight)},
))
// then
assert.NoError(t, err)
assert.False(t, obj1.Details().Has(bundle.RelationKeyLayoutAlign))
assert.False(t, obj2.Details().Has(bundle.RelationKeyLayoutAlign))
assert.False(t, tmpl.Details().Has(bundle.RelationKeyLayoutAlign))
})
t.Run("recommendedFeaturedRelations is updated", func(t *testing.T) {
// given
fx := newFixture(t, typeId)
fx.sb.SetType(coresb.SmartBlockTypeObjectType)
fx.store.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("obj1"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyFeaturedRelations: domain.StringList([]string{
bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(),
}),
// featuredRelations detail should be cleared in obj1, because its value corresponds to old recommendedFeaturedRelations value
},
{
bundle.RelationKeyId: domain.String("obj2"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyFeaturedRelations: domain.StringList([]string{
bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(), bundle.RelationKeyCreator.String(),
}),
// featuredRelations detail should be cleared in obj1, because its value corresponds to new recommendedFeaturedRelations value
},
{
bundle.RelationKeyId: domain.String("obj3"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyFeaturedRelations: domain.StringList([]string{
bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(), bundle.RelationKeyBacklinks.String(),
}),
// obj3 should not be modified, because featuredRelations does not correspond to old and new recommendedFeaturedRelations values
},
{
bundle.RelationKeyId: domain.String("obj4"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyFeaturedRelations: domain.StringList([]string{
bundle.RelationKeyDescription.String(),
}),
// featuredRelations of obj4 contains only description, so obj4 has nothing to delete
},
{
bundle.RelationKeyId: domain.String("tmpl"),
bundle.RelationKeyType: domain.String(bundle.TypeKeyTemplate.URL()),
bundle.RelationKeyFeaturedRelations: domain.StringList([]string{}),
bundle.RelationKeyTargetObjectType: domain.String(typeId),
// featuredRelations of template is empty, so it has nothing to delete
},
})
obj1 := smarttest.New("obj1")
require.NoError(t, obj1.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyFeaturedRelations, Value: domain.StringList([]string{
bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(),
}),
}}, false))
obj2 := smarttest.New("obj2")
require.NoError(t, obj2.SetDetails(nil, []domain.Detail{{
Key: bundle.RelationKeyFeaturedRelations, Value: domain.StringList([]string{
bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(), bundle.RelationKeyCreator.String(),
}),
}}, false))
fx.space.EXPECT().Do(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func(smartblock.SmartBlock) error) error {
switch id {
case "obj1":
assert.NoError(t, f(obj1))
case "obj2":
assert.NoError(t, f(obj2))
default:
panic("Do: invalid object id")
}
return nil
})
fx.space.EXPECT().DeriveObjectID(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, key domain.UniqueKey) (string, error) {
return key.Marshal(), nil
})
// when
err := fx.syncLayoutForObjectsAndTemplates(makeApplyInfo(typeId,
// recommendedLayout is changed: basic -> todo
layoutState{isFeaturedRelationsSet: true, featuredRelations: []string{
bundle.RelationKeyType.URL(), bundle.RelationKeyTag.URL(),
}},
layoutState{isFeaturedRelationsSet: true, featuredRelations: []string{
bundle.RelationKeyType.URL(), bundle.RelationKeyTag.URL(), bundle.RelationKeyCreator.URL(),
}},
))
// then
assert.NoError(t, err)
require.True(t, obj1.Details().Has(bundle.RelationKeyFeaturedRelations))
assert.Empty(t, obj1.Details().GetStringList(bundle.RelationKeyFeaturedRelations))
require.True(t, obj2.Details().Has(bundle.RelationKeyFeaturedRelations))
assert.Empty(t, obj2.Details().GetStringList(bundle.RelationKeyFeaturedRelations))
})
}
func makeApplyInfo(typeId string, oldLS, newLS layoutState) smartblock.ApplyInfo {
events := make([]simple.EventMessage, 0, 3)
if newLS.isLayoutSet {
events = append(events, makeObjectDetailsAmendMsg(domain.Detail{
Key: bundle.RelationKeyRecommendedLayout,
Value: domain.Int64(newLS.layout),
}))
}
if newLS.isLayoutAlignSet {
events = append(events, makeObjectDetailsAmendMsg(domain.Detail{
Key: bundle.RelationKeyLayoutAlign,
Value: domain.Int64(newLS.layoutAlign),
}))
}
if newLS.isFeaturedRelationsSet {
events = append(events, makeObjectDetailsAmendMsg(domain.Detail{
Key: bundle.RelationKeyRecommendedFeaturedRelations,
Value: domain.StringList(newLS.featuredRelations),
}))
}
ps := state.NewDoc(typeId, nil).NewState().SetDetails(domain.NewDetails())
if oldLS.isLayoutSet {
ps.SetDetail(bundle.RelationKeyRecommendedLayout, domain.Int64(oldLS.layout))
}
if oldLS.isLayoutAlignSet {
ps.SetDetail(bundle.RelationKeyLayoutAlign, domain.Int64(oldLS.layoutAlign))
}
if oldLS.isFeaturedRelationsSet {
ps.SetDetail(bundle.RelationKeyRecommendedFeaturedRelations, domain.StringList(oldLS.featuredRelations))
}
return smartblock.ApplyInfo{
Events: events,
ParentState: ps,
}
}
func makeObjectDetailsAmendMsg(detail domain.Detail) simple.EventMessage {
return simple.EventMessage{
Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfObjectDetailsAmend{ObjectDetailsAmend: &pb.EventObjectDetailsAmend{
Details: []*pb.EventObjectDetailsAmendKeyValue{{
Key: detail.Key.String(),
Value: detail.Value.ToProto(),
}},
}}},
}
}

View file

@ -33,7 +33,6 @@ var pageRequiredRelations = []domain.RelationKey{
bundle.RelationKeyLinks,
bundle.RelationKeyBacklinks,
bundle.RelationKeyMentions,
bundle.RelationKeyLayoutAlign,
}
var typeAndRelationRequiredRelations = []domain.RelationKey{
@ -45,17 +44,6 @@ var typeAndRelationRequiredRelations = []domain.RelationKey{
bundle.RelationKeyIsHidden,
}
var typeRequiredRelations = append(typeAndRelationRequiredRelations,
bundle.RelationKeyRecommendedRelations,
bundle.RelationKeyRecommendedFeaturedRelations,
bundle.RelationKeyRecommendedHiddenRelations,
bundle.RelationKeyRecommendedFileRelations,
bundle.RelationKeyRecommendedLayout,
bundle.RelationKeySmartblockTypes,
bundle.RelationKeyIconOption,
bundle.RelationKeyIconName,
)
var relationRequiredRelations = append(typeAndRelationRequiredRelations,
bundle.RelationKeyRelationFormat,
bundle.RelationKeyRelationFormatObjectTypes,
@ -214,11 +202,8 @@ func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Mig
templates := []template.StateTransformer{
template.WithEmpty,
template.WithObjectTypes(ctx.State.ObjectTypeKeys()),
template.WithResolvedLayout(layout),
template.WithDefaultFeaturedRelations,
template.WithFeaturedRelations,
template.WithFeaturedRelationsBlock,
template.WithLinkFieldsMigration,
template.WithCreatorRemovedFromFeaturedRelations,
}
switch layout {
@ -237,20 +222,11 @@ func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Mig
templates = append(templates,
template.WithTitle,
template.WithDescription,
template.WithAddedFeaturedRelation(bundle.RelationKeyType),
template.WithAddedFeaturedRelation(bundle.RelationKeyBacklinks),
template.WithBookmarkBlocks,
)
case model.ObjectType_relation:
templates = append(templates,
template.WithTitle,
template.WithAddedFeaturedRelation(bundle.RelationKeyType),
template.WithLayout(layout),
)
case model.ObjectType_objectType:
templates = append(templates,
template.WithTitle,
template.WithAddedFeaturedRelation(bundle.RelationKeyType),
template.WithLayout(layout),
)
case model.ObjectType_chat:
@ -287,7 +263,7 @@ func (p *Page) StateMigrations() migration.Migrations {
return migration.MakeMigrations([]migration.Migration{
{
Version: 2,
Proc: template.WithAddedFeaturedRelation(bundle.RelationKeyBacklinks),
Proc: func(s *state.State) {},
},
})
}

View file

@ -62,11 +62,8 @@ func (p *participant) Init(ctx *smartblock.InitContext) (err error) {
template.WithEmpty,
template.WithTitle,
template.WithDescription,
template.WithFeaturedRelations,
template.WithFeaturedRelationsBlock,
template.WithLayout(model.ObjectType_participant),
template.WithResolvedLayout(model.ObjectType_participant),
template.WithAddedFeaturedRelation(bundle.RelationKeyType),
template.WithAddedFeaturedRelation(bundle.RelationKeyBacklinks),
)
return nil
}

View file

@ -82,7 +82,6 @@ func (p *Profile) CreationStateMigration(ctx *smartblock.InitContext) migration.
template.InitTemplate(st,
template.WithObjectTypes([]domain.TypeKey{bundle.TypeKeyProfile}),
template.WithLayout(model.ObjectType_profile),
template.WithResolvedLayout(model.ObjectType_profile),
template.WithDetail(bundle.RelationKeyLayoutAlign, domain.Int64(model.Block_AlignCenter)),
migrationSetHidden,
)

View file

@ -236,6 +236,12 @@ func (sb *smartBlock) injectResolvedLayout(s *state.State) {
}
typeObjectId := s.LocalDetails().GetString(bundle.RelationKeyType)
if s.ObjectTypeKey() == bundle.TypeKeyTemplate {
// resolvedLayout for templates should be derived from target type
typeObjectId = s.Details().GetString(bundle.RelationKeyTargetObjectType)
}
if typeObjectId == "" {
if currentValue := s.LocalDetails().Get(bundle.RelationKeyResolvedLayout); currentValue.Ok() {
return
@ -245,16 +251,7 @@ func (sb *smartBlock) injectResolvedLayout(s *state.State) {
return
}
var parentLayoutValue domain.Value
parent := s.ParentState()
if parent != nil {
parentLayoutValue = parent.Details().Get(bundle.RelationKeyLayout)
}
if currentValue := s.LocalDetails().Get(bundle.RelationKeyResolvedLayout); currentValue.Ok() && !parentLayoutValue.Ok() {
// we can leave current value as is, if layout is not being unset right now
return
}
currentValue := s.LocalDetails().Get(bundle.RelationKeyResolvedLayout)
typeDetails, found := sb.lastDepDetails[typeObjectId]
if found {
@ -263,6 +260,9 @@ func (sb *smartBlock) injectResolvedLayout(s *state.State) {
records, err := sb.objectStore.SpaceIndex(sb.SpaceID()).QueryByIds([]string{typeObjectId})
if err != nil || len(records) != 1 {
log.Errorf("failed to query object %s: %v", typeObjectId, err)
if currentValue.Ok() {
return
}
s.SetDetailAndBundledRelation(bundle.RelationKeyResolvedLayout, domain.Int64(int64(model.ObjectType_basic)))
return
}
@ -270,6 +270,9 @@ func (sb *smartBlock) injectResolvedLayout(s *state.State) {
}
if !rawValue.Ok() {
if currentValue.Ok() {
return
}
log.Errorf("failed to get recommended layout from details of type. Fallback to basic layout")
s.SetDetailAndBundledRelation(bundle.RelationKeyResolvedLayout, domain.Int64(int64(model.ObjectType_basic)))
return

View file

@ -6,15 +6,12 @@ import (
"testing"
"time"
"github.com/anyproto/any-sync/app/ocache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
@ -303,7 +300,7 @@ func TestInjectResolvedLayout(t *testing.T) {
// then
assert.Equal(t, int64(model.ObjectType_todo), st.LocalDetails().GetInt64(bundle.RelationKeyResolvedLayout))
})
t.Run("resolved layout is already injected", func(t *testing.T) {
t.Run("failed to get type object id -> fallback to already sey resolvedLayout", func(t *testing.T) {
// given
fx := newFixture(id, t)
@ -316,7 +313,7 @@ func TestInjectResolvedLayout(t *testing.T) {
// then
assert.Equal(t, int64(model.ObjectType_set), st.LocalDetails().GetInt64(bundle.RelationKeyResolvedLayout))
})
t.Run("failed to get type object id -> fallback to basic", func(t *testing.T) {
t.Run("failed to get type object id and resolvedLayout is not set -> fallback to basic", func(t *testing.T) {
// given
fx := newFixture(id, t)
@ -334,6 +331,7 @@ func TestInjectResolvedLayout(t *testing.T) {
st := state.NewDoc("id", nil).NewState()
st.SetLocalDetail(bundle.RelationKeyType, domain.String(bundle.TypeKeyTask.URL()))
st.SetLocalDetail(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_basic))
fx.lastDepDetails = map[string]*domain.Details{
bundle.TypeKeyTask.URL(): domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
@ -353,6 +351,7 @@ func TestInjectResolvedLayout(t *testing.T) {
st := state.NewDoc("id", nil).NewState()
st.SetLocalDetail(bundle.RelationKeyType, domain.String(bundle.TypeKeyProfile.URL()))
st.SetLocalDetail(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_basic))
fx.objectStore.AddObjects(t, testSpaceId, []objectstore.TestObject{{
bundle.RelationKeyId: domain.String(bundle.TypeKeyProfile.URL()),
@ -378,135 +377,28 @@ func TestInjectResolvedLayout(t *testing.T) {
// then
assert.Equal(t, int64(model.ObjectType_basic), st.LocalDetails().GetInt64(bundle.RelationKeyResolvedLayout))
})
t.Run("layout is resolved from object store, because layout relation is deleted", func(t *testing.T) {
t.Run("layout for template is resolved from target type", func(t *testing.T) {
// given
fx := newFixture(id, t)
st := state.NewDoc("id", nil).NewState()
st.SetDetail(bundle.RelationKeyCoverId, domain.String("red"))
st.SetLocalDetail(bundle.RelationKeyType, domain.String(bundle.TypeKeyProfile.URL()))
st.SetLocalDetail(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_todo))
st.ParentState().SetDetail(bundle.RelationKeyLayout, domain.Int64(model.ObjectType_todo))
st.SetDetail(bundle.RelationKeyTargetObjectType, domain.String(bundle.TypeKeyTask.URL()))
st.SetLocalDetail(bundle.RelationKeyType, domain.String(bundle.TypeKeyTemplate.URL()))
st.SetLocalDetail(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_note))
st.SetObjectTypeKey(bundle.TypeKeyTemplate)
fx.objectStore.AddObjects(t, testSpaceId, []objectstore.TestObject{{
bundle.RelationKeyId: domain.String(bundle.TypeKeyProfile.URL()),
bundle.RelationKeyId: domain.String(bundle.TypeKeyTemplate.URL()),
bundle.RelationKeyRecommendedLayout: domain.Int64(model.ObjectType_profile),
}, {
bundle.RelationKeyId: domain.String(bundle.TypeKeyTask.URL()),
bundle.RelationKeyRecommendedLayout: domain.Int64(model.ObjectType_todo),
}})
// when
fx.injectResolvedLayout(st)
// then
assert.Equal(t, int64(model.ObjectType_profile), st.LocalDetails().GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_todo), st.LocalDetails().GetInt64(bundle.RelationKeyResolvedLayout))
})
}
func TestChangeResolvedLayoutForObjects(t *testing.T) {
typeId := "typeId"
t.Run("change resolvedLayout, do not delete layout", func(t *testing.T) {
// given
fx := newFixture(typeId, t)
fx.source.sbType = smartblock.SmartBlockTypeObjectType
fx.objectStore.AddObjects(t, testSpaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("obj1"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
},
{
bundle.RelationKeyId: domain.String("obj2"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_todo)),
},
{
bundle.RelationKeyId: domain.String("obj3"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_profile)),
},
{
bundle.RelationKeyId: domain.String("tmpl"),
bundle.RelationKeyType: domain.String(bundle.TypeKeyTemplate.URL()),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyTargetObjectType: domain.String(typeId),
},
})
fx.space.EXPECT().DoLockedIfNotExists(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func() error) error {
if id == "obj1" || id == "tmpl" {
return f()
}
return ocache.ErrExists
})
fx.space.EXPECT().Do(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func(SmartBlock) error) error {
assert.Equal(t, "obj3", id)
return nil
})
// when
err := fx.changeResolvedLayoutForObjects(makeLayoutChanges(int64(model.ObjectType_todo)), false)
// then
assert.NoError(t, err)
})
t.Run("change resolvedLayout, do not delete layout", func(t *testing.T) {
// given
fx := newFixture(typeId, t)
fx.source.sbType = smartblock.SmartBlockTypeObjectType
fx.objectStore.AddObjects(t, testSpaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("obj1"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_basic)),
},
{
bundle.RelationKeyId: domain.String("obj2"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_todo)),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_todo)),
},
{
bundle.RelationKeyId: domain.String("obj3"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_profile)),
},
{
bundle.RelationKeyId: domain.String("obj4"),
bundle.RelationKeyType: domain.String(typeId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_note)),
},
})
fx.space.EXPECT().DoLockedIfNotExists(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func() error) error {
assert.Equal(t, "obj4", id)
return f()
})
counter := 0
fx.space.EXPECT().Do(mock.Anything, mock.Anything).RunAndReturn(func(id string, f func(SmartBlock) error) error {
counter++
return nil
})
// when
err := fx.changeResolvedLayoutForObjects(makeLayoutChanges(int64(model.ObjectType_todo)), true)
// then
assert.NoError(t, err)
assert.Equal(t, 3, counter)
})
}
func makeLayoutChanges(layout int64) []simple.EventMessage {
return []simple.EventMessage{{
Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfObjectDetailsAmend{ObjectDetailsAmend: &pb.EventObjectDetailsAmend{
Details: []*pb.EventObjectDetailsAmendKeyValue{{
Key: bundle.RelationKeyRecommendedLayout.String(),
Value: domain.Int64(layout).ToProto(),
},
}}}},
}}
}

View file

@ -30,7 +30,6 @@ import (
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/relationutils"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
@ -70,9 +69,10 @@ const (
type Hook int
type ApplyInfo struct {
State *state.State
Events []simple.EventMessage
Changes []*pb.ChangeContent
State *state.State
ParentState *state.State
Events []simple.EventMessage
Changes []*pb.ChangeContent
}
type HookCallback func(info ApplyInfo) (err error)
@ -628,7 +628,6 @@ func (sb *smartBlock) EnabledRelationAsDependentObjects() {
}
func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
startTime := time.Now()
if sb.IsDeleted() {
return domain.ErrObjectIsDeleted
}
@ -700,10 +699,9 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
removeInternalFlags(s)
}
beforeApplyStateTime := time.Now()
migrationVersionUpdated := true
if parent := s.ParentState(); parent != nil {
parent := s.ParentState()
if parent != nil {
migrationVersionUpdated = s.MigrationVersion() != parent.MigrationVersion()
}
@ -712,15 +710,10 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
return
}
if err = sb.changeResolvedLayoutForObjects(msgs, true); err != nil {
return
}
// we may have layout changed, so we need to update restrictions
sb.updateRestrictions()
sb.setRestrictionsDetail(s)
afterApplyStateTime := time.Now()
st := sb.Doc.(*state.State)
changes := st.GetChanges()
@ -805,7 +798,6 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
sb.runIndexer(st)
}
afterPushChangeTime := time.Now()
if sendEvent {
events := msgsToEvents(msgs)
if ctx := s.Context(); ctx != nil {
@ -821,22 +813,16 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
if hasDepIds(sb.GetRelationLinks(), &act) {
sb.CheckSubscriptions()
}
afterReportChangeTime := time.Now()
if hooks {
if e := sb.execHooks(HookAfterApply, ApplyInfo{State: sb.Doc.(*state.State), Events: msgs, Changes: changes}); e != nil {
if e := sb.execHooks(HookAfterApply, ApplyInfo{
State: sb.Doc.(*state.State),
ParentState: parent,
Events: msgs,
Changes: changes,
}); e != nil {
log.With("objectID", sb.Id()).Warnf("after apply execHooks error: %v", e)
}
}
afterApplyHookTime := time.Now()
metrics.Service.Send(&metrics.StateApply{
BeforeApplyMs: beforeApplyStateTime.Sub(startTime).Milliseconds(),
StateApplyMs: afterApplyStateTime.Sub(beforeApplyStateTime).Milliseconds(),
PushChangeMs: afterPushChangeTime.Sub(afterApplyStateTime).Milliseconds(),
ReportChangeMs: afterReportChangeTime.Sub(afterPushChangeTime).Milliseconds(),
ApplyHookMs: afterApplyHookTime.Sub(afterReportChangeTime).Milliseconds(),
ObjectId: sb.Id(),
})
return
}
@ -962,10 +948,6 @@ func (sb *smartBlock) StateAppend(f func(d state.Doc) (s *state.State, changes [
}
log.Infof("changes: stateAppend: %d events", len(msgs))
if err = sb.changeResolvedLayoutForObjects(msgs, true); err != nil {
return err
}
if len(msgs) > 0 {
sb.sendEvent(&pb.Event{
Messages: msgsToEvents(msgs),
@ -977,7 +959,14 @@ func (sb *smartBlock) StateAppend(f func(d state.Doc) (s *state.State, changes [
sb.CheckSubscriptions()
}
sb.runIndexer(s)
sb.execHooks(HookAfterApply, ApplyInfo{State: s, Events: msgs, Changes: changes})
if err = sb.execHooks(HookAfterApply, ApplyInfo{
State: s,
ParentState: s.ParentState(),
Events: msgs,
Changes: changes,
}); err != nil {
log.Errorf("failed to execute smartblock hooks after apply on StateAppend: %v", err)
}
return nil
}

View file

@ -367,7 +367,8 @@ func (st *SmartTest) StateRebuild(d state.Doc) (err error) {
}
func (st *SmartTest) StateAppend(func(d state.Doc) (s *state.State, changes []*pb.ChangeContent, err error)) error {
panic("not implemented")
st.Results.IsStateAppendCalled = true
return nil
}
func (st *SmartTest) AddBlock(b simple.Block) *SmartTest {
@ -423,6 +424,7 @@ func (st *SmartTest) Update(ctx session.Context, apply func(b simple.Block) erro
}
type Results struct {
Events [][]simple.EventMessage
Applies [][]*model.Block
Events [][]simple.EventMessage
Applies [][]*model.Block
IsStateAppendCalled bool
}

View file

@ -116,7 +116,6 @@ func (s *SpaceView) initTemplate(st *state.State) {
template.InitTemplate(st,
template.WithObjectTypes([]domain.TypeKey{bundle.TypeKeySpaceView}),
template.WithLayout(model.ObjectType_spaceView),
template.WithResolvedLayout(model.ObjectType_spaceView),
)
}

View file

@ -7,6 +7,7 @@ import (
"github.com/gogo/protobuf/types"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/slice"
@ -20,6 +21,7 @@ var (
func (s *State) Normalize(withLayouts bool) (err error) {
s.removeDuplicates()
s.normalizeDetails()
return s.normalize(withLayouts)
}
@ -392,3 +394,32 @@ func CleanupLayouts(s *State) (removedCount int) {
cleanup(s.RootId())
return
}
func (s *State) normalizeDetails() {
if s.ObjectTypeKey() == bundle.TypeKeyObjectType {
s.normalizeRecommendedRelations()
}
}
// normalizeRecommendedRelations normalizes recommended relations of Type on state build level, because
// these lists mustn't contain similar values, but could be updated by multiple clients independently
func (s *State) normalizeRecommendedRelations() {
details := s.details
if details == nil && s.parent != nil {
details = s.parent.details
}
if details == nil {
return
}
recRelations := details.GetStringList(bundle.RelationKeyRecommendedRelations)
recFeatRelations := details.GetStringList(bundle.RelationKeyRecommendedFeaturedRelations)
recHiddenRelations := details.GetStringList(bundle.RelationKeyRecommendedHiddenRelations)
recHiddenRelations = slice.RemoveN(recHiddenRelations, recFeatRelations...)
recHiddenRelations = slice.RemoveN(recHiddenRelations, recRelations...)
recRelations = slice.RemoveN(recRelations, recFeatRelations...)
details.SetStringList(bundle.RelationKeyRecommendedRelations, recRelations)
details.SetStringList(bundle.RelationKeyRecommendedHiddenRelations, recHiddenRelations)
}

View file

@ -13,6 +13,7 @@ import (
"golang.org/x/exp/slices"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
@ -547,3 +548,26 @@ func countStringsLength(value *types.Value) (n int) {
}
return n
}
func TestNormalizeRecommendedRelations(t *testing.T) {
// given
s := NewDoc("root", nil).NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{"s1", "sh", "sf", "sfh"}), // s stands for sidebar
bundle.RelationKeyRecommendedFeaturedRelations: domain.StringList([]string{"f1", "f2", "sfh", "fh", "sf"}),
bundle.RelationKeyRecommendedHiddenRelations: domain.StringList([]string{"sfh", "sh", "fh", "h1", "h2", "h3"}),
}))
child := s.NewState()
// when
s.normalizeRecommendedRelations()
child.normalizeRecommendedRelations()
// then
assert.Equal(t, []string{"s1", "sh"}, s.Details().GetStringList(bundle.RelationKeyRecommendedRelations))
assert.Equal(t, []string{"f1", "f2", "sfh", "fh", "sf"}, s.Details().GetStringList(bundle.RelationKeyRecommendedFeaturedRelations))
assert.Equal(t, []string{"h1", "h2", "h3"}, s.Details().GetStringList(bundle.RelationKeyRecommendedHiddenRelations))
assert.Equal(t, []string{"s1", "sh"}, child.Details().GetStringList(bundle.RelationKeyRecommendedRelations))
assert.Equal(t, []string{"f1", "f2", "sfh", "fh", "sf"}, child.Details().GetStringList(bundle.RelationKeyRecommendedFeaturedRelations))
assert.Equal(t, []string{"h1", "h2", "h3"}, child.Details().GetStringList(bundle.RelationKeyRecommendedHiddenRelations))
}

View file

@ -15,7 +15,6 @@ import (
"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/metrics"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
@ -82,7 +81,6 @@ func (t *textImpl) UpdateTextBlocks(ctx session.Context, ids []string, showEvent
}
func (t *textImpl) Split(ctx session.Context, req pb.RpcBlockSplitRequest) (newId string, err error) {
startTime := time.Now()
s := t.NewStateCtx(ctx)
tb, err := getText(s, req.BlockId)
if err != nil {
@ -155,21 +153,13 @@ func (t *textImpl) Split(ctx session.Context, req pb.RpcBlockSplitRequest) (newI
return
}
}
algorithmMs := time.Now().Sub(startTime).Milliseconds()
if err = t.Apply(s); err != nil {
return
}
applyMs := time.Now().Sub(startTime).Milliseconds() - algorithmMs
metrics.Service.Send(&metrics.BlockSplit{
ObjectId: t.Id(),
AlgorithmMs: algorithmMs,
ApplyMs: applyMs,
})
return
}
func (t *textImpl) Merge(ctx session.Context, firstId, secondId string) (err error) {
startTime := time.Now()
s := t.NewStateCtx(ctx)
// Don't merge blocks inside header block
@ -196,16 +186,9 @@ func (t *textImpl) Merge(ctx session.Context, firstId, secondId string) (err err
}
s.Unlink(second.Model().Id)
first.Model().ChildrenIds = append(first.Model().ChildrenIds, second.Model().ChildrenIds...)
algorithmMs := time.Now().Sub(startTime).Milliseconds()
if err = t.Apply(s); err != nil {
return
}
applyMs := time.Now().Sub(startTime).Milliseconds() - algorithmMs
metrics.Service.Send(&metrics.BlockMerge{
ObjectId: t.Id(),
AlgorithmMs: algorithmMs,
ApplyMs: applyMs,
})
return
}

View file

@ -100,20 +100,8 @@ var WithObjectTypes = func(otypes []domain.TypeKey) StateTransformer {
}
}
var WithResolvedLayout = func(layout model.ObjectTypeLayout) StateTransformer {
return func(s *state.State) {
if !s.LocalDetails().Has(bundle.RelationKeyResolvedLayout) {
s.SetDetailAndBundledRelation(bundle.RelationKeyResolvedLayout, domain.Int64(layout))
}
}
}
var WithLayout = func(layout model.ObjectTypeLayout) StateTransformer {
return func(s *state.State) {
if !s.Details().Has(bundle.RelationKeyLayout) {
s.SetDetailAndBundledRelation(bundle.RelationKeyLayout, domain.Int64(layout))
}
}
return WithDetail(bundle.RelationKeyLayout, domain.Int64(layout))
}
var WithDetailName = func(name string) StateTransformer {
@ -228,38 +216,6 @@ var WithTitle = StateTransformer(func(s *state.State) {
}
})
// WithDefaultFeaturedRelations **MUST** be called before WithDescription
var WithDefaultFeaturedRelations = func(s *state.State) {
if !s.Details().Has(bundle.RelationKeyFeaturedRelations) {
var fr = []string{bundle.RelationKeyType.String()}
layout, _ := s.Layout()
switch layout {
case model.ObjectType_basic, model.ObjectType_note:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeyBacklinks.String()}
case model.ObjectType_set:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeySetOf.String(), bundle.RelationKeyBacklinks.String()}
case model.ObjectType_collection:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeyBacklinks.String()}
case model.ObjectType_file, model.ObjectType_image, model.ObjectType_audio, model.ObjectType_video, model.ObjectType_pdf:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(), bundle.RelationKeyBacklinks.String()}
// Tag is not added to details of object explicitly as it is not system relation
s.SetDetail(bundle.RelationKeyTag, domain.StringList([]string{}))
}
s.SetDetail(bundle.RelationKeyFeaturedRelations, domain.StringList(fr))
}
}
var WithAddedFeaturedRelation = func(key domain.RelationKey) StateTransformer {
return func(s *state.State) {
featRels := s.Details().GetStringList(bundle.RelationKeyFeaturedRelations)
if slice.FindPos(featRels, key.String()) > -1 {
return
} else {
s.SetDetail(bundle.RelationKeyFeaturedRelations, domain.StringList(append(featRels, key.String())))
}
}
}
var WithRemovedFeaturedRelation = func(key domain.RelationKey) StateTransformer {
return func(s *state.State) {
var featRels = s.Details().GetStringList(bundle.RelationKeyFeaturedRelations)
@ -270,18 +226,6 @@ var WithRemovedFeaturedRelation = func(key domain.RelationKey) StateTransformer
}
}
var WithCreatorRemovedFromFeaturedRelations = StateTransformer(func(s *state.State) {
fr := s.Details().GetStringList(bundle.RelationKeyFeaturedRelations)
if slice.FindPos(fr, bundle.RelationKeyCreator.String()) != -1 {
frc := make([]string, len(fr))
copy(frc, fr)
frc = slice.RemoveMut(frc, bundle.RelationKeyCreator.String())
s.SetDetail(bundle.RelationKeyFeaturedRelations, domain.StringList(frc))
}
})
var WithForcedDescription = func(s *state.State) {
RequireHeader(s)
@ -331,7 +275,10 @@ var WithForcedDescription = func(s *state.State) {
var WithDescription = func(s *state.State) {
RequireHeader(s)
WithAddedFeaturedRelation(bundle.RelationKeyDescription)(s)
featRels := s.Details().GetStringList(bundle.RelationKeyFeaturedRelations)
if slice.FindPos(featRels, bundle.RelationKeyDescription.String()) == -1 {
s.SetDetail(bundle.RelationKeyFeaturedRelations, domain.StringList(append(featRels, bundle.RelationKeyDescription.String())))
}
if !s.Exists(DescriptionBlockId) {
WithForcedDescription(s)
}
@ -392,7 +339,7 @@ var WithNameToFirstBlock = StateTransformer(func(s *state.State) {
}
})
var WithFeaturedRelations = StateTransformer(func(s *state.State) {
var WithFeaturedRelationsBlock = StateTransformer(func(s *state.State) {
RequireHeader(s)
var align model.BlockAlign

View file

@ -55,7 +55,6 @@ func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migra
template.WithEmpty,
template.WithObjectTypes([]domain.TypeKey{bundle.TypeKeyDashboard}),
template.WithLayout(model.ObjectType_dashboard),
template.WithResolvedLayout(model.ObjectType_dashboard),
template.WithDetail(bundle.RelationKeyIsHidden, domain.Bool(true)),
)
},

View file

@ -78,10 +78,9 @@ func (w *Workspaces) initTemplate(ctx *smartblock.InitContext) {
template.InitTemplate(ctx.State,
template.WithEmpty,
template.WithTitle,
template.WithFeaturedRelations,
template.WithFeaturedRelationsBlock,
template.WithDetail(bundle.RelationKeyIsHidden, domain.Bool(true)),
template.WithLayout(model.ObjectType_space),
template.WithResolvedLayout(model.ObjectType_space),
template.WithForcedObjectTypes([]domain.TypeKey{bundle.TypeKeySpace}),
template.WithForcedDetail(bundle.RelationKeyFeaturedRelations, domain.StringList([]string{bundle.RelationKeyType.String(), bundle.RelationKeyCreator.String()})),
)

View file

@ -164,6 +164,9 @@ type exportContext struct {
linkStateFilters *state.Filters
isLinkProcess bool
includeBackLinks bool
relations map[string]struct{}
setOfList map[string]struct{}
objectTypes map[string]struct{}
*export
}
@ -182,7 +185,11 @@ func newExportContext(e *export, req pb.RpcObjectListExportRequest) *exportConte
zip: req.Zip,
linkStateFilters: pbFiltersToState(req.LinksStateFilters),
includeBackLinks: req.IncludeBacklinks,
export: e,
setOfList: make(map[string]struct{}),
objectTypes: make(map[string]struct{}),
relations: make(map[string]struct{}),
export: e,
}
return ec
}
@ -201,6 +208,9 @@ func (e *exportContext) copy() *exportContext {
isLinkProcess: e.isLinkProcess,
linkStateFilters: e.linkStateFilters,
includeBackLinks: e.includeBackLinks,
relations: e.relations,
setOfList: e.setOfList,
objectTypes: e.objectTypes,
}
}
@ -524,70 +534,75 @@ func (e *exportContext) processFiles(ids []string) ([]string, error) {
func (e *exportContext) addDerivedObjects() error {
processedObjects := make(map[string]struct{}, 0)
allRelations, allTypes, allSetOfList, err := e.getRelationsAndTypes(e.docs, processedObjects)
err := e.getRelationsAndTypes(e.docs, processedObjects)
if err != nil {
return err
}
templateRelations, templateTypes, templateSetOfList, err := e.getTemplatesRelationsAndTypes(lo.Union(allTypes, allSetOfList), processedObjects)
err = e.getTemplatesRelationsAndTypes(processedObjects)
if err != nil {
return err
}
allRelations = lo.Union(allRelations, templateRelations)
allTypes = lo.Union(allTypes, templateTypes)
allSetOfList = lo.Union(allSetOfList, templateSetOfList)
err = e.addRelationsAndTypes(allTypes, allRelations, allSetOfList)
err = e.addRelationsAndTypes()
if err != nil {
return err
}
return nil
}
func (e *exportContext) getRelationsAndTypes(notProcessedObjects map[string]*Doc, processedObjects map[string]struct{}) ([]string, []string, []string, error) {
allRelations, allTypes, allSetOfList, err := e.collectDerivedObjects(notProcessedObjects)
func (e *exportContext) getRelationsAndTypes(notProcessedObjects map[string]*Doc, processedObjects map[string]struct{}) error {
err := e.collectDerivedObjects(notProcessedObjects)
if err != nil {
return nil, nil, nil, err
return err
}
// get derived objects only from types,
// because relations currently have only system relations and object type
if len(allTypes) > 0 || len(allSetOfList) > 0 {
relations, objectTypes, setOfList, err := e.getDerivedObjectsForTypes(lo.Union(allTypes, allSetOfList), processedObjects)
if len(e.objectTypes) > 0 || len(e.setOfList) > 0 {
err = e.getDerivedObjectsForTypes(processedObjects)
if err != nil {
return nil, nil, nil, err
return err
}
allRelations = lo.Union(allRelations, relations)
allTypes = lo.Union(allTypes, objectTypes)
allSetOfList = lo.Union(allSetOfList, setOfList)
}
return allRelations, allTypes, allSetOfList, nil
return nil
}
func (e *exportContext) collectDerivedObjects(objects map[string]*Doc) ([]string, []string, []string, error) {
var relations, objectsTypes, setOf []string
func (e *exportContext) collectDerivedObjects(objects map[string]*Doc) error {
for id := range objects {
err := cache.Do(e.picker, id, func(b sb.SmartBlock) error {
state := b.NewState().Copy().Filter(e.getStateFilters(id))
relations = lo.Union(relations, getObjectRelations(state))
objectRelations := getObjectRelations(state)
fillObjectsMap(e.relations, objectRelations)
details := state.CombinedDetails()
if isObjectWithDataview(details) {
dataviewRelations, err := getDataviewRelations(state)
if err != nil {
return err
}
relations = lo.Union(relations, dataviewRelations)
fillObjectsMap(e.relations, dataviewRelations)
}
var objectTypes []string
if details.Has(bundle.RelationKeyType) {
objectTypeId := details.GetString(bundle.RelationKeyType)
objectsTypes = lo.Union(objectsTypes, []string{objectTypeId})
objectTypes = append(objectTypes, details.GetString(bundle.RelationKeyType))
}
if details.Has(bundle.RelationKeyTargetObjectType) {
objectTypes = append(objectTypes, details.GetString(bundle.RelationKeyTargetObjectType))
}
fillObjectsMap(e.objectTypes, objectTypes)
setOfList := details.GetStringList(bundle.RelationKeySetOf)
setOf = lo.Union(setOf, setOfList)
fillObjectsMap(e.setOfList, setOfList)
return nil
})
if err != nil {
return nil, nil, nil, err
return err
}
}
return relations, objectsTypes, setOf, nil
return nil
}
func fillObjectsMap(dst map[string]struct{}, objectsToAdd []string) {
for _, objectId := range objectsToAdd {
dst[objectId] = struct{}{}
}
}
func getObjectRelations(state *state.State) []string {
@ -619,33 +634,40 @@ func getDataviewRelations(state *state.State) ([]string, error) {
return relations, err
}
func (e *exportContext) getDerivedObjectsForTypes(allTypes []string, processedObjects map[string]struct{}) ([]string, []string, []string, error) {
func (e *exportContext) getDerivedObjectsForTypes(processedObjects map[string]struct{}) error {
notProceedTypes := make(map[string]*Doc)
var relations, objectTypes []string
for _, object := range allTypes {
if _, ok := processedObjects[object]; ok {
continue
}
notProceedTypes[object] = nil
processedObjects[object] = struct{}{}
for object := range e.objectTypes {
e.fillNotProcessedTypes(processedObjects, object, notProceedTypes)
}
for object := range e.setOfList {
e.fillNotProcessedTypes(processedObjects, object, notProceedTypes)
}
if len(notProceedTypes) == 0 {
return relations, objectTypes, nil, nil
return nil
}
relations, objectTypes, setOfList, err := e.getRelationsAndTypes(notProceedTypes, processedObjects)
err := e.getRelationsAndTypes(notProceedTypes, processedObjects)
if err != nil {
return nil, nil, nil, err
return err
}
return relations, objectTypes, setOfList, nil
return nil
}
func (e *exportContext) getTemplatesRelationsAndTypes(allTypes []string, processedObjects map[string]struct{}) ([]string, []string, []string, error) {
func (e *exportContext) fillNotProcessedTypes(processedObjects map[string]struct{}, object string, notProceedTypes map[string]*Doc) {
if _, ok := processedObjects[object]; ok {
return
}
notProceedTypes[object] = nil
processedObjects[object] = struct{}{}
}
func (e *exportContext) getTemplatesRelationsAndTypes(processedObjects map[string]struct{}) error {
allTypes := lo.MapToSlice(e.objectTypes, func(key string, value struct{}) string { return key })
templates, err := e.queryAndFilterObjectsByRelation(e.spaceId, allTypes, bundle.RelationKeyTargetObjectType)
if err != nil {
return nil, nil, nil, err
return nil
}
if len(templates) == 0 {
return nil, nil, nil, nil
return nil
}
templatesToProcess := make(map[string]*Doc, len(templates))
for _, template := range templates {
@ -656,14 +678,18 @@ func (e *exportContext) getTemplatesRelationsAndTypes(allTypes []string, process
templatesToProcess[id] = templateDoc
}
}
templateRelations, templateType, templateSetOfList, err := e.getRelationsAndTypes(templatesToProcess, processedObjects)
err = e.getRelationsAndTypes(templatesToProcess, processedObjects)
if err != nil {
return nil, nil, nil, err
return err
}
return templateRelations, templateType, templateSetOfList, nil
return nil
}
func (e *exportContext) addRelationsAndTypes(types, relations, setOfList []string) error {
func (e *exportContext) addRelationsAndTypes() error {
types := lo.MapToSlice(e.objectTypes, func(key string, value struct{}) string { return key })
setOfList := lo.MapToSlice(e.setOfList, func(key string, value struct{}) string { return key })
relations := lo.MapToSlice(e.relations, func(key string, value struct{}) string { return key })
err := e.addRelations(relations)
if err != nil {
return err

View file

@ -947,10 +947,10 @@ func Test_docsForExport(t *testing.T) {
relationKey := domain.RelationKey("key")
storeFixture.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("id"),
domain.RelationKey(relationKey): domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyId: domain.String("id"),
relationKey: domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
})
err := storeFixture.SpaceIndex(spaceId).UpdateObjectLinks(context.Background(), "id", []string{"id1"})
@ -1016,10 +1016,10 @@ func Test_docsForExport(t *testing.T) {
storeFixture.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("id"),
domain.RelationKey(relationKey): domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyId: domain.String("id"),
relationKey: domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(relationKey),
@ -1093,10 +1093,10 @@ func Test_docsForExport(t *testing.T) {
storeFixture.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("id"),
domain.RelationKey(relationKey): domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyId: domain.String("id"),
relationKey: domain.String("value"),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(relationKey),
@ -1172,10 +1172,10 @@ func Test_docsForExport(t *testing.T) {
storeFixture.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String("id"),
domain.RelationKey(relationKey): domain.String(optionId),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyId: domain.String("id"),
relationKey: domain.String(optionId),
bundle.RelationKeyType: domain.String("objectType"),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(relationKey),
@ -1967,6 +1967,106 @@ func Test_docsForExport(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, 5, len(expCtx.docs))
})
t.Run("export template", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
objectTypeKey := "customObjectType"
objectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, objectTypeKey)
assert.Nil(t, err)
templateType := "templateType"
templateObjectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, templateType)
assert.Nil(t, err)
objectId := "objectId"
storeFixture.AddObjects(t, spaceId, []objectstore.TestObject{
{
bundle.RelationKeyId: domain.String(objectId),
bundle.RelationKeyName: domain.String("template"),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyTargetObjectType: domain.String(objectTypeKey),
bundle.RelationKeyType: domain.String(templateType),
},
{
bundle.RelationKeyId: domain.String(objectTypeKey),
bundle.RelationKeyUniqueKey: domain.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyType: domain.String(objectTypeKey),
},
{
bundle.RelationKeyId: domain.String(templateType),
bundle.RelationKeyUniqueKey: domain.String(templateObjectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyType: domain.String(objectTypeKey),
},
})
smartBlockTest := smarttest.New(objectId)
doc := smartBlockTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(objectId),
bundle.RelationKeyType: domain.String(templateType),
bundle.RelationKeyTargetObjectType: domain.String(objectTypeKey),
}))
doc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
})
smartBlockTest.Doc = doc
objectType := smarttest.New(objectTypeKey)
objectTypeDoc := objectType.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(objectTypeKey),
bundle.RelationKeyType: domain.String(objectTypeKey),
}))
objectTypeDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
objectType.Doc = objectTypeDoc
templateTypeObject := smarttest.New(templateType)
templateTypeObjectDoc := objectType.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(templateType),
bundle.RelationKeyType: domain.String(templateType),
}))
templateTypeObjectDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
templateTypeObject.Doc = templateTypeObjectDoc
objectGetter := mock_cache.NewMockObjectGetter(t)
objectGetter.EXPECT().GetObject(context.Background(), objectId).Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeKey).Return(objectType, nil)
objectGetter.EXPECT().GetObject(context.Background(), templateType).Return(templateTypeObject, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
}
expCtx := newExportContext(e, pb.RpcObjectListExportRequest{
SpaceId: spaceId,
ObjectIds: []string{objectId},
IncludeNested: false,
Format: model.Export_Protobuf,
})
// when
err = expCtx.docsForExport()
// then
assert.Nil(t, err)
assert.Equal(t, 3, len(expCtx.docs))
})
t.Run("add default object type and template from dataview", func(t *testing.T) {
// given
id := "id"

View file

@ -177,7 +177,6 @@ func getRelationDetails(name, key string, format float64) *domain.Details {
details.SetFloat64(bundle.RelationKeyRelationFormat, format)
details.SetString(bundle.RelationKeyName, name)
details.SetString(bundle.RelationKeyRelationKey, key)
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relation))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relation))
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, key)
if err != nil {

View file

@ -258,7 +258,6 @@ func (ds *Service) getRelationDetails(databaseProperty property.DatabaseProperty
details.SetString(bundle.RelationKeyName, name)
details.SetString(bundle.RelationKeyRelationKey, key)
details.SetInt64(bundle.RelationKeyCreatedDate, time.Now().Unix())
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relation))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relation))
details.SetString(bundle.RelationKeySourceFilePath, databaseProperty.GetID())
uniqueKey, err := domain.NewUniqueKey(sb.SmartBlockTypeRelation, key)

View file

@ -313,7 +313,6 @@ func (pt *Task) getRelationDetails(key string, name string, propObject property.
details.SetInt64(bundle.RelationKeyRelationFormat, int64(propObject.GetFormat()))
details.SetString(bundle.RelationKeyName, name)
details.SetString(bundle.RelationKeyRelationKey, key)
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relation))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relation))
details.SetString(bundle.RelationKeySourceFilePath, propObject.GetID())
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, key)
@ -563,7 +562,6 @@ func getDetailsForRelationOption(name, rel string) (string, *domain.Details) {
details := domain.NewDetails()
details.SetString(bundle.RelationKeyName, name)
details.SetString(bundle.RelationKeyRelationKey, rel)
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relationOption))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relationOption))
details.SetInt64(bundle.RelationKeyCreatedDate, time.Now().Unix())
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelationOption, id)

View file

@ -65,7 +65,7 @@ func (s *SpaceImport) ProvideCollection(snapshots []*common.Snapshot,
}
func (s *SpaceImport) objectShouldBeSkipped(item *common.Snapshot) bool {
return item.Snapshot.SbType == smartblock.SmartBlockTypeSubObject || item.Snapshot.SbType == smartblock.SmartBlockTypeTemplate ||
return item.Snapshot.SbType == smartblock.SmartBlockTypeSubObject ||
item.Snapshot.SbType == smartblock.SmartBlockTypeRelation || item.Snapshot.SbType == smartblock.SmartBlockTypeObjectType ||
item.Snapshot.SbType == smartblock.SmartBlockTypeRelationOption
}

View file

@ -44,7 +44,7 @@ func TestSpaceImport_ProvideCollection(t *testing.T) {
assert.Nil(t, err)
assert.Nil(t, collection)
})
t.Run("no widget object - add all objects (except template and subobjects) in Protobuf Import collection", func(t *testing.T) {
t.Run("no widget object - add all objects (except subobjects) in Protobuf Import collection", func(t *testing.T) {
// given
p := SpaceImport{}
params := &pb.RpcObjectImportRequestPbParams{NoCollection: false}
@ -85,9 +85,10 @@ func TestSpaceImport_ProvideCollection(t *testing.T) {
assert.Len(t, collection, 1)
rootCollectionState := state.NewDocFromSnapshot("", collection[0].Snapshot.ToProto()).(*state.State)
objectsInCollection := rootCollectionState.GetStoreSlice(template.CollectionStoreKey)
assert.Len(t, objectsInCollection, 2)
assert.Len(t, objectsInCollection, 3)
assert.Equal(t, objectsInCollection[0], "id1")
assert.Equal(t, objectsInCollection[1], "id4")
assert.Equal(t, objectsInCollection[1], "id3")
assert.Equal(t, objectsInCollection[2], "id4")
})
t.Run("widget with sets - add only sets in Protobuf Import collection", func(t *testing.T) {
// given
@ -106,6 +107,9 @@ func TestSpaceImport_ProvideCollection(t *testing.T) {
Id: "id3",
Snapshot: &common.SnapshotModel{
SbType: smartblock2.SmartBlockTypeTemplate,
Data: &common.StateSnapshot{
ObjectTypes: []string{bundle.TypeKeyTemplate.URL()},
},
},
},
// page
@ -191,6 +195,9 @@ func TestSpaceImport_ProvideCollection(t *testing.T) {
Id: "id3",
Snapshot: &common.SnapshotModel{
SbType: smartblock2.SmartBlockTypeTemplate,
Data: &common.StateSnapshot{
ObjectTypes: []string{bundle.TypeKeyTemplate.URL()},
},
},
},
// page
@ -277,6 +284,9 @@ func TestSpaceImport_ProvideCollection(t *testing.T) {
Id: "id3",
Snapshot: &common.SnapshotModel{
SbType: smartblock2.SmartBlockTypeTemplate,
Data: &common.StateSnapshot{
ObjectTypes: []string{bundle.TypeKeyTemplate.URL()},
},
},
},
// favorite page

View file

@ -41,7 +41,6 @@ func (s *service) createChatDerived(ctx context.Context, space clientspace.Space
}
createState := state.NewDocWithUniqueKey("", nil, key).(*state.State)
details.Set(bundle.RelationKeyResolvedLayout, domain.Int64(int64(model.ObjectType_chatDerived)))
details.Set(bundle.RelationKeyLayout, domain.Int64(int64(model.ObjectType_chatDerived)))
createState.SetDetails(details)

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)
}
@ -45,7 +45,6 @@ func (s *service) createObjectType(ctx context.Context, space clientspace.Space,
}
object.SetString(bundle.RelationKeyId, id)
object.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_objectType))
object.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_objectType))
createState := state.NewDocWithUniqueKey("", nil, uniqueKey).(*state.State)

View file

@ -59,7 +59,6 @@ func (s *service) createRelation(ctx context.Context, space clientspace.Space, d
}
// todo: check the existence of objectTypes in space. InstallBundledObjects should be called same as for recommendedRelations on type creation
object.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relation))
object.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relation))
createState := state.NewDocWithUniqueKey("", nil, uniqueKey).(*state.State)

View file

@ -36,7 +36,6 @@ func (s *service) createRelationOption(ctx context.Context, space clientspace.Sp
object = details.Copy()
object.SetString(bundle.RelationKeyUniqueKey, uniqueKey.Marshal())
object.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_relationOption))
object.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_relationOption))
createState := state.NewDocWithUniqueKey("", nil, uniqueKey).(*state.State)

View file

@ -11,7 +11,6 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/object/objectcache"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/space/clientspace"
@ -58,7 +57,6 @@ func (s *service) CreateSmartBlockFromStateInSpaceWithOptions(
if createState == nil {
createState = state.NewDoc("", nil).(*state.State)
}
startTime := time.Now()
// priority:
// 1. details
// 2. createState
@ -71,11 +69,6 @@ func (s *service) CreateSmartBlockFromStateInSpaceWithOptions(
createState.SetDetailAndBundledRelation(bundle.RelationKeySpaceId, domain.String(spc.Id()))
ev := &metrics.CreateObjectEvent{
SetDetailsMs: time.Since(startTime).Milliseconds(),
}
ctx = context.WithValue(ctx, eventCreate, ev)
initFunc := func(id string) *smartblock.InitContext {
createState.SetRootId(id)
return &smartblock.InitContext{
@ -97,10 +90,6 @@ func (s *service) CreateSmartBlockFromStateInSpaceWithOptions(
sb.Unlock()
id = sb.Id()
ev.SmartblockCreateMs = time.Since(startTime).Milliseconds() - ev.SetDetailsMs - ev.WorkspaceCreateMs - ev.GetWorkspaceBlockWaitMs
ev.SmartblockType = int(sbType)
ev.ObjectId = id
metrics.Service.Send(ev)
return id, newDetails, nil
}

View file

@ -36,7 +36,6 @@ import (
"github.com/anyproto/anytype-heart/core/files/fileobject"
"github.com/anyproto/anytype-heart/core/files/fileuploader"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core"
@ -184,27 +183,22 @@ func (s *Service) GetObjectByFullID(ctx context.Context, id domain.FullID) (sb s
func (s *Service) OpenBlock(sctx session.Context, id domain.FullID, includeRelationsAsDependentObjects bool) (obj *model.ObjectView, err error) {
id = s.resolveFullId(id)
startTime := time.Now()
err = s.DoFullId(id, func(ob smartblock.SmartBlock) error {
if includeRelationsAsDependentObjects {
ob.EnabledRelationAsDependentObjects()
}
afterSmartBlockTime := time.Now()
ob.RegisterSession(sctx)
afterDataviewTime := time.Now()
st := ob.NewState()
st.SetLocalDetail(bundle.RelationKeyLastOpenedDate, domain.Int64(time.Now().Unix()))
if err = ob.Apply(st, smartblock.NoHistory, smartblock.NoEvent, smartblock.SkipIfNoChanges, smartblock.KeepInternalFlags, smartblock.IgnoreNoPermissions); err != nil {
log.Errorf("failed to update lastOpenedDate: %s", err)
}
afterApplyTime := time.Now()
if obj, err = ob.Show(); err != nil {
return fmt.Errorf("show: %w", err)
}
afterShowTime := time.Now()
if err != nil && !errors.Is(err, treestorage.ErrUnknownTreeId) {
log.Errorf("failed to watch status for object %s: %s", id, err)
@ -214,16 +208,6 @@ func (s *Service) OpenBlock(sctx session.Context, id domain.FullID, includeRelat
v.InjectVirtualBlocks(id.ObjectID, obj)
}
afterHashesTime := time.Now()
metrics.Service.Send(&metrics.OpenBlockEvent{
ObjectId: id.ObjectID,
GetBlockMs: afterSmartBlockTime.Sub(startTime).Milliseconds(),
DataviewMs: afterDataviewTime.Sub(afterSmartBlockTime).Milliseconds(),
ApplyMs: afterApplyTime.Sub(afterDataviewTime).Milliseconds(),
ShowMs: afterShowTime.Sub(afterApplyTime).Milliseconds(),
FileWatcherMs: afterHashesTime.Sub(afterShowTime).Milliseconds(),
SmartblockType: int(ob.Type()),
})
return nil
})
if err != nil {

View file

@ -89,7 +89,6 @@ func (d *date) ReadDoc(context.Context, ChangeReceiver, bool) (doc state.Doc, er
s := state.NewDoc(d.id, nil).(*state.State)
template.InitTemplate(s,
template.WithTitle,
template.WithDefaultFeaturedRelations,
template.WithAllBlocksEditsRestricted,
)
s.SetDetails(details)

View file

@ -24,6 +24,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/template"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
@ -31,6 +32,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space/spacecore/typeprovider"
"github.com/anyproto/anytype-heart/util/reflection"
"github.com/anyproto/anytype-heart/util/slice"
)
@ -331,7 +333,6 @@ func (s *source) buildState() (doc state.Doc, err error) {
// we need to have required internal relations for all objects, including system
st.AddBundledRelationLinks(bundle.RequiredInternalRelations...)
if s.Type() == smartblock.SmartBlockTypePage || s.Type() == smartblock.SmartBlockTypeProfilePage {
template.WithAddedFeaturedRelation(bundle.RelationKeyBacklinks)(st)
template.WithRelations([]domain.RelationKey{bundle.RelationKeyBacklinks})(st)
}
@ -377,6 +378,20 @@ type PushChangeParams struct {
}
func (s *source) PushChange(params PushChangeParams) (id string, err error) {
for _, change := range params.Changes {
name := reflection.GetChangeContent(change.Value)
if name == "" {
log.Errorf("can't detect change content for %s", change.Value)
} else {
ev := &metrics.ChangeEvent{
ChangeName: name,
SbType: s.smartblockType.String(),
Count: 1,
}
metrics.Service.SendSampled(ev)
}
}
if params.Time.IsZero() {
params.Time = time.Now()
}

View file

@ -91,11 +91,9 @@ func (s *store) ReadDoc(ctx context.Context, receiver ChangeReceiver, empty bool
switch s.sbType {
case smartblock.SmartBlockTypeChatDerivedObject:
st.SetObjectTypeKey(bundle.TypeKeyChatDerived)
st.SetDetailAndBundledRelation(bundle.RelationKeyResolvedLayout, domain.Int64(int64(model.ObjectType_chatDerived)))
st.SetDetailAndBundledRelation(bundle.RelationKeyLayout, domain.Int64(int64(model.ObjectType_chatDerived)))
case smartblock.SmartBlockTypeAccountObject:
st.SetObjectTypeKey(bundle.TypeKeyProfile)
st.SetDetailAndBundledRelation(bundle.RelationKeyResolvedLayout, domain.Int64(int64(model.ObjectType_profile)))
st.SetDetailAndBundledRelation(bundle.RelationKeyLayout, domain.Int64(int64(model.ObjectType_profile)))
default:
return nil, fmt.Errorf("unsupported smartblock type: %v", s.sbType)

View file

@ -44,9 +44,11 @@ var (
log = logging.Logger("template")
templateIsPreferableRelationKeys = []domain.RelationKey{
bundle.RelationKeyFeaturedRelations, bundle.RelationKeyResolvedLayout,
bundle.RelationKeyIconEmoji, bundle.RelationKeyCoverId,
bundle.RelationKeySourceObject, bundle.RelationKeySetOf,
bundle.RelationKeyLayout,
bundle.RelationKeyIconEmoji,
bundle.RelationKeyCoverId,
bundle.RelationKeySourceObject,
bundle.RelationKeySetOf,
}
)
@ -178,13 +180,14 @@ func extractTargetDetails(originDetails *domain.Details, templateDetails *domain
return targetDetails
}
for key, originalVal := range originDetails.Iterate() {
if key == bundle.RelationKeyLayout {
// layout detail should be removed, as resolvedLayout should be derived from template state
targetDetails.Delete(key)
continue
}
templateVal := templateDetails.Get(key)
if templateVal.Ok() {
inTemplateEmpty := templateVal.IsEmpty()
if key == bundle.RelationKeyResolvedLayout {
// layout = 0 is actually basic layout, so it counts
inTemplateEmpty = false
}
inOriginEmpty := originalVal.IsEmpty()
templateValueShouldBePreferred := lo.Contains(templateIsPreferableRelationKeys, key)
if !inTemplateEmpty && (inOriginEmpty || templateValueShouldBePreferred) {
@ -236,6 +239,7 @@ func (s *service) buildState(sb smartblock.SmartBlock) (st *state.State, err err
bundle.RelationKeyTemplateIsBundled,
bundle.RelationKeyOrigin,
bundle.RelationKeyAddedDate,
bundle.RelationKeyFeaturedRelations,
)
st.SetDetailAndBundledRelation(bundle.RelationKeySourceObject, domain.String(sb.Id()))
// original created timestamp is used to set creationDate for imported objects, not for template-based objects
@ -427,9 +431,7 @@ func (s *service) SetDefaultTemplateInType(ctx context.Context, typeId, template
func (s *service) createBlankTemplateState(typeId domain.FullID, layout model.ObjectTypeLayout) (st *state.State) {
st = state.NewDoc(BlankTemplateId, nil).NewState()
template.InitTemplate(st, template.WithEmpty,
template.WithDefaultFeaturedRelations,
template.WithFeaturedRelations,
template.WithAddedFeaturedRelation(bundle.RelationKeyTag),
template.WithFeaturedRelationsBlock,
template.WithDetail(bundle.RelationKeyTag, domain.StringList(nil)),
template.WithTitle,
)

View file

@ -170,7 +170,6 @@ func TestService_CreateTemplateStateWithDetails(t *testing.T) {
// then
assert.NoError(t, err)
assert.Equal(t, BlankTemplateId, st.RootId())
assert.Contains(t, st.Details().GetStringList(bundle.RelationKeyFeaturedRelations), bundle.RelationKeyTag.String())
assert.True(t, st.Details().Has(bundle.RelationKeyTag))
})
}
@ -255,7 +254,6 @@ func TestCreateTemplateStateFromSmartBlock(t *testing.T) {
// then
assert.Equal(t, BlankTemplateId, st.RootId())
assert.Contains(t, st.Details().GetStringList(bundle.RelationKeyFeaturedRelations), bundle.RelationKeyTag.String())
assert.True(t, st.Details().Has(bundle.RelationKeyTag))
})
@ -394,15 +392,12 @@ func TestExtractTargetDetails(t *testing.T) {
OriginValue, TemplateValue domain.Value
OriginLeft bool
}{
{Key: bundle.RelationKeyResolvedLayout, OriginValue: domain.Int64(0), TemplateValue: domain.Int64(1), OriginLeft: false},
{Key: bundle.RelationKeyResolvedLayout, OriginValue: domain.Int64(5), TemplateValue: domain.Int64(0), OriginLeft: false},
{Key: bundle.RelationKeyResolvedLayout, OriginValue: domain.Int64(3), TemplateValue: domain.Int64(3), OriginLeft: false},
{Key: bundle.RelationKeyLayout, OriginValue: domain.Int64(0), TemplateValue: domain.Int64(1), OriginLeft: false},
{Key: bundle.RelationKeyLayout, OriginValue: domain.Int64(5), TemplateValue: domain.Int64(0), OriginLeft: false},
{Key: bundle.RelationKeyLayout, OriginValue: domain.Int64(3), TemplateValue: domain.Int64(3), OriginLeft: false},
{Key: bundle.RelationKeySourceObject, OriginValue: domain.String(""), TemplateValue: domain.String("s1"), OriginLeft: false},
{Key: bundle.RelationKeySourceObject, OriginValue: domain.String("s2"), TemplateValue: domain.String(""), OriginLeft: true},
{Key: bundle.RelationKeySourceObject, OriginValue: domain.String("s0"), TemplateValue: domain.String("s3"), OriginLeft: false},
{Key: bundle.RelationKeyFeaturedRelations, OriginValue: domain.StringList([]string{"tag"}), TemplateValue: domain.StringList([]string{}), OriginLeft: true},
{Key: bundle.RelationKeyFeaturedRelations, OriginValue: domain.StringList([]string{}), TemplateValue: domain.StringList([]string{"tag", "type"}), OriginLeft: false},
{Key: bundle.RelationKeyFeaturedRelations, OriginValue: domain.StringList([]string{"type"}), TemplateValue: domain.StringList([]string{"tag"}), OriginLeft: false},
{Key: bundle.RelationKeyName, OriginValue: domain.String("orig"), TemplateValue: domain.String(""), OriginLeft: true},
{Key: bundle.RelationKeyName, OriginValue: domain.String(""), TemplateValue: domain.String("tmpl"), OriginLeft: false},
{Key: bundle.RelationKeyName, OriginValue: domain.String("orig"), TemplateValue: domain.String("tmpl"), OriginLeft: true},

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

@ -88,17 +88,14 @@ func (f *file) Details(ctx context.Context) (*domain.Details, domain.TypeKey, er
if meta.Media == "application/pdf" {
typeKey = bundle.TypeKeyFile
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_pdf))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_pdf))
}
if strings.HasPrefix(meta.Media, "video") {
typeKey = bundle.TypeKeyVideo
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_video))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_video))
}
if strings.HasPrefix(meta.Media, "audio") {
details.Set(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_audio))
details.Set(bundle.RelationKeyLayout, domain.Int64(model.ObjectType_audio))
if audioDetails, err := f.audioDetails(ctx); err == nil {
details = details.Merge(audioDetails)
@ -107,7 +104,6 @@ func (f *file) Details(ctx context.Context) (*domain.Details, domain.TypeKey, er
}
if filepath.Ext(meta.Name) == constant.SvgExt {
typeKey = bundle.TypeKeyImage
details.Set(bundle.RelationKeyResolvedLayout, domain.Int64(model.ObjectType_image))
details.Set(bundle.RelationKeyLayout, domain.Int64(model.ObjectType_image))
}
@ -144,7 +140,6 @@ func calculateCommonDetails(
det := domain.NewDetails()
det.SetString(bundle.RelationKeyFileId, fileId.String())
det.SetBool(bundle.RelationKeyIsReadonly, false)
det.SetInt64(bundle.RelationKeyResolvedLayout, int64(layout))
det.SetInt64(bundle.RelationKeyLayout, int64(layout))
det.SetFloat64(bundle.RelationKeyLastModifiedDate, float64(lastModifiedDate))
return det

View file

@ -29,7 +29,7 @@ func TestFile_Details(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, bundle.TypeKeyImage, typeKey)
assert.Equal(t, int64(model.ObjectType_image), details.GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_image), details.GetInt64(bundle.RelationKeyLayout))
assert.Equal(t, "svg", details.GetString(bundle.RelationKeyFileExt))
assert.Equal(t, "image", details.GetString(bundle.RelationKeyName))
assert.Equal(t, "id", details.GetString(bundle.RelationKeyFileId))
@ -49,7 +49,7 @@ func TestFile_Details(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, bundle.TypeKeyFile, typeKey)
assert.Equal(t, int64(model.ObjectType_file), details.GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_file), details.GetInt64(bundle.RelationKeyLayout))
assert.Equal(t, "txt", details.GetString(bundle.RelationKeyFileExt))
assert.Equal(t, "file", details.GetString(bundle.RelationKeyName))
assert.Equal(t, "id", details.GetString(bundle.RelationKeyFileId))
@ -70,7 +70,7 @@ func TestFile_Details(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, bundle.TypeKeyAudio, typeKey)
assert.Equal(t, int64(model.ObjectType_audio), details.GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_audio), details.GetInt64(bundle.RelationKeyLayout))
assert.Equal(t, "mp3", details.GetString(bundle.RelationKeyFileExt))
assert.Equal(t, "file", details.GetString(bundle.RelationKeyName))
assert.Equal(t, "id", details.GetString(bundle.RelationKeyFileId))
@ -91,7 +91,7 @@ func TestFile_Details(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, bundle.TypeKeyVideo, typeKey)
assert.Equal(t, int64(model.ObjectType_video), details.GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_video), details.GetInt64(bundle.RelationKeyLayout))
assert.Equal(t, "mp4", details.GetString(bundle.RelationKeyFileExt))
assert.Equal(t, "file", details.GetString(bundle.RelationKeyName))
assert.Equal(t, "id", details.GetString(bundle.RelationKeyFileId))
@ -112,7 +112,7 @@ func TestFile_Details(t *testing.T) {
// then
assert.Nil(t, err)
assert.Equal(t, bundle.TypeKeyFile, typeKey)
assert.Equal(t, int64(model.ObjectType_pdf), details.GetInt64(bundle.RelationKeyResolvedLayout))
assert.Equal(t, int64(model.ObjectType_pdf), details.GetInt64(bundle.RelationKeyLayout))
assert.Equal(t, "pdf", details.GetString(bundle.RelationKeyFileExt))
assert.Equal(t, "file", details.GetString(bundle.RelationKeyName))
assert.Equal(t, "id", details.GetString(bundle.RelationKeyFileId))

View file

@ -16,8 +16,7 @@ func InitEmptyFileState(st *state.State) {
template.InitTemplate(st,
template.WithEmpty,
template.WithTitle,
template.WithDefaultFeaturedRelations,
template.WithFeaturedRelations,
template.WithFeaturedRelationsBlock,
template.WithAllBlocksEditsRestricted,
)
}

View file

@ -5,7 +5,6 @@ import (
"fmt"
"testing"
"github.com/anyproto/any-sync/app"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -24,16 +23,6 @@ type indexerFixture struct {
objectStoreFixture *objectstore.StoreFixture
}
type dummyAccountService struct{}
func (s dummyAccountService) MyParticipantId(spaceId string) string {
return ""
}
func (s dummyAccountService) Init(_ *app.App) error { return nil }
func (s dummyAccountService) Name() string { return "dummyAccountService" }
func newIndexerFixture(t *testing.T) *indexerFixture {
objectStore := objectstore.NewStoreFixture(t)
fileService := mock_files.NewMockService(t)

View file

@ -223,10 +223,12 @@ func (s *service) ensureNotSyncedFilesAddedToQueue() error {
for _, record := range records {
fullId := extractFullFileIdFromDetails(record.Details)
id := record.Details.GetString(bundle.RelationKeyId)
err := s.addToSyncQueue(id, fullId, false, false)
if err != nil {
log.Errorf("add to sync queue: %v", err)
if record.Details.GetString(bundle.RelationKeyCreator) == s.accountService.MyParticipantId(fullId.SpaceId) {
id := record.Details.GetString(bundle.RelationKeyId)
err := s.addToSyncQueue(id, fullId, false, false)
if err != nil {
log.Errorf("add to sync queue: %v", err)
}
}
}
@ -348,7 +350,6 @@ func (s *service) makeInitialDetails(fileId domain.FileId, origin objectorigin.O
details := domain.NewDetails()
details.SetString(bundle.RelationKeyFileId, fileId.String())
// Use general file layout. It will be changed for proper layout after indexing
details.SetInt64(bundle.RelationKeyResolvedLayout, int64(model.ObjectType_file))
details.SetInt64(bundle.RelationKeyLayout, int64(model.ObjectType_file))
details.SetInt64(bundle.RelationKeyFileIndexingStatus, int64(model.FileIndexingStatus_NotIndexed))
details.SetInt64(bundle.RelationKeySyncStatus, int64(domain.ObjectSyncStatusQueued))

View file

@ -53,6 +53,16 @@ type fixture struct {
*service
}
type dummyAccountService struct{}
func (s *dummyAccountService) MyParticipantId(spaceId string) string {
return ""
}
func (s *dummyAccountService) Init(_ *app.App) error { return nil }
func (s *dummyAccountService) Name() string { return "dummyAccountService" }
type dummyConfig struct{}
func (c *dummyConfig) IsLocalOnlyMode() bool {
@ -108,6 +118,7 @@ func newFixture(t *testing.T) *fixture {
a := new(app.App)
a.Register(&dummyConfig{})
a.Register(&dummyAccountService{})
a.Register(dataStoreProvider)
a.Register(fileStore)
a.Register(objectStore)
@ -126,7 +137,6 @@ func newFixture(t *testing.T) *fixture {
a.Register(testutil.PrepareMock(ctx, a, wallet))
a.Register(&config.Config{DisableFileConfig: true, NetworkMode: pb.RpcAccount_DefaultConfig, PeferYamuxTransport: true})
a.Register(&dummyObjectArchiver{})
a.Register(&dummyAccountService{})
err = a.Start(ctx)
require.NoError(t, err)

View file

@ -16,7 +16,7 @@ import (
"time"
"github.com/anyproto/any-sync/app"
"github.com/h2non/filetype"
"github.com/gabriel-vasile/mimetype"
"github.com/anyproto/anytype-heart/core/block/cache"
"github.com/anyproto/anytype-heart/core/block/simple"
@ -540,12 +540,12 @@ func (u *uploader) getOrCreateFileObject(ctx context.Context, addResult *files.A
}
func (u *uploader) detectType(buf *fileReader) model.BlockContentFileType {
b, err := buf.Peek(8192)
if err != nil && err != io.EOF {
mime, err := mimetype.DetectReader(buf)
if err != nil {
log.With("error", err).Error("detect MIME")
return model.BlockContentFile_File
}
tp, _ := filetype.Match(b)
return file.DetectTypeByMIME(u.name, tp.MIME.Value)
return file.DetectTypeByMIME(u.name, mime.String())
}
type FileComponent interface {

View file

@ -4,9 +4,8 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/h2non/filetype"
"github.com/gabriel-vasile/mimetype"
)
type AddOption func(*AddOptions)
@ -70,13 +69,8 @@ func (s *service) normalizeOptions(opts *AddOptions) error {
return fmt.Errorf("failed to seek underlying reader: %w", err)
}
t, err := filetype.Match(data)
if err != nil {
log.Warnf("filetype failed to match: %s", err)
opts.Media = http.DetectContentType(data)
} else {
opts.Media = t.MIME.Value
}
mime := mimetype.Detect(data)
opts.Media = mime.String()
}
return nil

View file

@ -233,7 +233,8 @@ func (c *flatStoreGarbageCollector) CollectGarbage(ctx context.Context) error {
}
c.flatStore.sendLocalBytesUsageEvent(ctx)
return results.Close()
results.Close()
return nil
}
func newFlatStoreGarbageCollector(flatStore *flatStore) LocalStoreGarbageCollector {

View file

@ -56,7 +56,6 @@ func DownloadManifest(url string, checkWhitelist bool) (info *model.ManifestInfo
if err != nil {
return nil, err
}
schemaResp := schemaResponse{}
err = json.Unmarshal(raw, &schemaResp)
if err != nil {
@ -119,7 +118,7 @@ func getRawJson(url string) ([]byte, error) {
if err != nil {
return nil, err
}
req.Close = true
res, err := client.Do(req)
if err != nil {
return nil, err

View file

@ -4,6 +4,7 @@ import (
_ "embed"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
@ -11,8 +12,6 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const port = ":7070"
//go:embed testdata/schema.json
var schemaJSON []byte
@ -37,13 +36,13 @@ func TestIsInWhitelist(t *testing.T) {
}
func TestDownloadManifestAndValidateSchema(t *testing.T) {
schema := schemaResponse{Schema: "http://localhost" + port + "/schema.json"}
server := startHttpServer()
defer server.Shutdown(nil)
defer server.Close()
schema := schemaResponse{Schema: server.URL + "/schema.json"}
t.Run("download knowledge base manifest", func(t *testing.T) {
// given
url := "http://localhost" + port + "/manifest.json"
url := server.URL + "/manifest.json"
// when
info, err := DownloadManifest(url, false)
@ -116,7 +115,7 @@ func TestDownloadManifestAndValidateSchema(t *testing.T) {
})
}
func startHttpServer() *http.Server {
func startHttpServer() *httptest.Server {
handler := http.NewServeMux()
handler.HandleFunc("/manifest.json", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -128,9 +127,7 @@ func startHttpServer() *http.Server {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(schemaJSON)
})
server := &http.Server{Addr: port, Handler: handler}
go server.ListenAndServe()
return server
return httptest.NewServer(handler)
}
func buildInfo() *model.ManifestInfo {

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
@ -558,15 +559,6 @@ func (i *indexer) logFinishedReindexStat(reindexType metrics.ReindexType, totalI
} else {
log.Info(msg)
}
if metrics.Enabled {
metrics.Service.Send(&metrics.ReindexEvent{
ReindexType: reindexType,
Total: totalIds,
Succeed: succeedIds,
SpentMs: int(spent.Milliseconds()),
})
}
}
func (i *indexer) RemoveIndexes(spaceId string) error {

View file

@ -12,7 +12,6 @@ import (
"golang.org/x/exp/slices"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
)
@ -117,7 +116,6 @@ func (i *spaceIndexer) Index(info smartblock.DocInfo, options ...smartblock.Inde
func (i *spaceIndexer) index(ctx context.Context, info smartblock.DocInfo, options ...smartblock.IndexOption) error {
// options are stored in smartblock pkg because of cyclic dependency :(
startTime := time.Now()
opts := &smartblock.IndexOptions{}
for _, o := range options {
o(opts)
@ -160,7 +158,6 @@ func (i *spaceIndexer) index(ctx context.Context, info smartblock.DocInfo, optio
details := info.Details
indexSetTime := time.Now()
var hasError bool
if indexLinks {
if err = i.spaceIndex.UpdateObjectLinks(ctx, info.Id, info.Links); err != nil {
@ -169,7 +166,6 @@ func (i *spaceIndexer) index(ctx context.Context, info smartblock.DocInfo, optio
}
}
indexLinksTime := time.Now()
if indexDetails {
if err := i.spaceIndex.UpdateObjectDetails(ctx, info.Id, details); err != nil {
hasError = true
@ -199,21 +195,11 @@ func (i *spaceIndexer) index(ctx context.Context, info smartblock.DocInfo, optio
} else {
_ = i.spaceIndex.DeleteDetails(ctx, []string{info.Id})
}
indexDetailsTime := time.Now()
detailsCount := details.Len()
if !hasError {
saveIndexedHash()
}
metrics.Service.Send(&metrics.IndexEvent{
ObjectId: info.Id,
IndexLinksTimeMs: indexLinksTime.Sub(indexSetTime).Milliseconds(),
IndexDetailsTimeMs: indexDetailsTime.Sub(indexLinksTime).Milliseconds(),
IndexSetRelationsTimeMs: indexSetTime.Sub(startTime).Milliseconds(),
DetailsCount: detailsCount,
})
return nil
}

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,10 +67,19 @@ var (
errRecommendedRelationsAlreadyFilled = fmt.Errorf("recommended featured relations are already filled")
)
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) {
@ -95,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)
@ -108,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)
}
@ -120,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

@ -33,7 +33,7 @@ const CName = "subscription"
var log = logging.Logger("anytype-mw-subscription")
var batchTime = 50 * time.Millisecond
var batchTime = 250 * time.Millisecond
func New() Service {
return &service{}

View file

@ -44,7 +44,7 @@ func TestSyncSubscriptions(t *testing.T) {
subs.(*syncSubscriptions).service = testSubs
err := subs.Run(context.Background())
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
spaceSub, err := subs.GetSubscription("spaceId")
require.NoError(t, err)
syncCnt := spaceSub.SyncingObjectsCount([]string{"1", "2"})
@ -59,7 +59,7 @@ func TestSyncSubscriptions(t *testing.T) {
objects[i][bundle.RelationKeySyncStatus] = domain.Int64(int64(domain.ObjectSyncStatusSynced))
testSubs.AddObjects(t, "spaceId", []objectstore.TestObject{objects[i]})
}
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
syncCnt = spaceSub.SyncingObjectsCount([]string{"1", "2"})
require.Equal(t, 2, syncCnt)
err = subs.Close(context.Background())

View file

@ -73,6 +73,51 @@ func (_c *MockWallet_Account_Call) RunAndReturn(run func() *accountdata.AccountK
return _c
}
// FtsPrimaryLang provides a mock function with given fields:
func (_m *MockWallet) FtsPrimaryLang() string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for FtsPrimaryLang")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// MockWallet_FtsPrimaryLang_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FtsPrimaryLang'
type MockWallet_FtsPrimaryLang_Call struct {
*mock.Call
}
// FtsPrimaryLang is a helper method to define mock.On call
func (_e *MockWallet_Expecter) FtsPrimaryLang() *MockWallet_FtsPrimaryLang_Call {
return &MockWallet_FtsPrimaryLang_Call{Call: _e.mock.On("FtsPrimaryLang")}
}
func (_c *MockWallet_FtsPrimaryLang_Call) Run(run func()) *MockWallet_FtsPrimaryLang_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockWallet_FtsPrimaryLang_Call) Return(_a0 string) *MockWallet_FtsPrimaryLang_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockWallet_FtsPrimaryLang_Call) RunAndReturn(run func() string) *MockWallet_FtsPrimaryLang_Call {
_c.Call.Return(run)
return _c
}
// GetAccountEthAddress provides a mock function with given fields:
func (_m *MockWallet) GetAccountEthAddress() wallet.EthAddress {
ret := _m.Called()

View file

@ -29,6 +29,7 @@ type EthAddress = common.Address
type wallet struct {
rootPath string
repoPath string // other components will init their files/dirs inside
lang string
deviceKeyPath string
accountKey crypto.PrivKey
@ -117,6 +118,10 @@ func (r *wallet) RepoPath() string {
return r.repoPath
}
func (r *wallet) FtsPrimaryLang() string {
return r.lang
}
func (r *wallet) RootPath() string {
return r.rootPath
}
@ -129,12 +134,13 @@ func (r *wallet) Account() *accountdata.AccountKeys {
return r.accountData
}
func NewWithAccountRepo(rootPath string, derivationResult crypto.DerivationResult) Wallet {
func NewWithAccountRepo(rootPath string, derivationResult crypto.DerivationResult, lang string) Wallet {
accountId := derivationResult.Identity.GetPublic().Account()
repoPath := filepath.Join(rootPath, accountId)
return &wallet{
rootPath: rootPath,
repoPath: repoPath,
lang: lang,
masterKey: derivationResult.MasterKey,
oldAccountKey: derivationResult.OldAccountKey,
accountKey: derivationResult.Identity,
@ -160,6 +166,7 @@ func NewWithRepoPathAndKeys(repoPath string, accountKeypair, deviceKeypair crypt
type Wallet interface {
RootPath() string
RepoPath() string
FtsPrimaryLang() string
GetAccountPrivkey() crypto.PrivKey
GetDevicePrivkey() crypto.PrivKey
GetOldAccountKey() crypto.PrivKey

View file

@ -13,8 +13,11 @@ make download-tantivy-all-force
#### Mac
As of 16.01.23 last protobuf version (21.12) broke the JS plugin support, so you can use the v3 branch:
Make sure you've removed protobuf
```
brew remove protobuf --ignore-dependencies
brew install protobuf@3
brew link --force --overwrite protobuf@3
```
To generate Swift protobuf:

View file

@ -3510,6 +3510,7 @@ TODO: Remove this request if we do not need it, GO-1926
| ----- | ---- | ----- | ----------- |
| id | [string](#string) | | Id of a selected account |
| rootPath | [string](#string) | | |
| fulltextPrimaryLanguage | [string](#string) | | optional, default fts language |
@ -3732,6 +3733,7 @@ Middleware-to-front-end response to an account recover request, that can contain
| path | [string](#string) | | |
| rootPath | [string](#string) | | |
| icon | [int64](#int64) | | |
| fulltextPrimaryLanguage | [string](#string) | | optional, default fts language |
@ -3849,6 +3851,7 @@ User can select an account from those, that came with an AccountAdd events
| networkCustomConfigFilePath | [string](#string) | | config path for the custom network mode |
| preferYamuxTransport | [bool](#bool) | | optional, default is false, recommended in case of problems with QUIC transport |
| jsonApiListenAddr | [string](#string) | | optional, if empty json api will not be started; 127.0.0.1:31009 should be the default one |
| fulltextPrimaryLanguage | [string](#string) | | optional, default fts language |
@ -20297,6 +20300,7 @@ Front-end-to-middleware request to create a new wallet
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| rootPath | [string](#string) | | Path to a wallet directory |
| fulltextPrimaryLanguage | [string](#string) | | optional, default fts language |
@ -20415,6 +20419,7 @@ Front end to middleware request-to-recover-a wallet with this mnemonic and a roo
| ----- | ---- | ----- | ----------- |
| rootPath | [string](#string) | | Path to a wallet directory |
| mnemonic | [string](#string) | | Mnemonic of a wallet to recover |
| fulltextPrimaryLanguage | [string](#string) | | optional, default fts language |
@ -29004,6 +29009,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 |

94
go.mod
View file

@ -7,17 +7,17 @@ require (
github.com/PuerkitoBio/goquery v1.10.2
github.com/VividCortex/ewma v1.2.0
github.com/adrium/goheif v0.0.0-20230113233934-ca402e77a786
github.com/anyproto/any-store v0.1.7
github.com/anyproto/any-sync v0.6.0
github.com/anyproto/any-store v0.1.8
github.com/anyproto/any-sync v0.6.1
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a
github.com/anyproto/go-chash v0.1.0
github.com/anyproto/go-naturaldate/v2 v2.0.2-0.20230524105841-9829cfd13438
github.com/anyproto/go-slip10 v1.0.0
github.com/anyproto/lexid v0.0.4
github.com/anyproto/protobuf v1.3.3-0.20240814124528-72b8c7e0e0f5
github.com/anyproto/tantivy-go v0.3.1
github.com/anyproto/tantivy-go v1.0.1
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/avast/retry-go/v4 v4.6.0
github.com/avast/retry-go/v4 v4.6.1
github.com/chai2010/webp v1.1.2-0.20240612091223-aa1b379218b7
github.com/cheggaaa/mb/v3 v3.0.2
github.com/dave/jennifer v1.7.1
@ -29,6 +29,7 @@ require (
github.com/dsoprea/go-exif/v3 v3.0.1
github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20221012074422-4f3f7e934102
github.com/ethereum/go-ethereum v1.13.15
github.com/gabriel-vasile/mimetype v1.4.8
github.com/gin-gonic/gin v1.10.0
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/go-chi/chi/v5 v5.2.1
@ -45,17 +46,17 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/h2non/filetype v1.1.3
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/yamux v0.1.2
github.com/hbagdi/go-unsplash v0.0.0-20230414214043-474fc02c9119
github.com/huandu/skiplist v1.2.1
github.com/iancoleman/strcase v0.3.0
github.com/improbable-eng/grpc-web v0.15.0
github.com/ipfs/boxo v0.27.4
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.7.0
github.com/ipfs/go-ds-flatfs v0.5.1
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 +82,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.21.0
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
@ -92,7 +93,7 @@ require (
github.com/stretchr/testify v1.10.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
github.com/swaggo/swag/v2 v2.0.0-rc4
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/valyala/fastjson v1.6.4
github.com/vektra/mockery/v2 v2.47.0
@ -103,14 +104,14 @@ require (
go.uber.org/mock v0.5.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
golang.org/x/image v0.24.0
golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.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/exp v0.0.0-20250218142911-aa4b98e5adaa
golang.org/x/image v0.25.0
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3
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
@ -125,8 +126,6 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
@ -148,7 +147,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,11 +156,10 @@ 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
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gammazero/deque v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
@ -189,7 +187,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/flatbuffers v1.12.1 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
@ -198,7 +196,6 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/bbloom v0.0.4 // indirect
@ -206,23 +203,22 @@ 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/jbenet/goprocess v0.1.4 // 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.38.2 // 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.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
@ -231,7 +227,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
@ -249,9 +245,8 @@ require (
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/pseudomuto/protokit v0.2.1 // indirect
github.com/quic-go/quic-go v0.49.0 // indirect
github.com/quic-go/quic-go v0.50.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/cors v1.11.0 // indirect
github.com/rs/zerolog v1.29.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
@ -265,7 +260,7 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/sv-tools/openapi v0.2.1 // indirect
github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect
github.com/swaggo/swag v1.16.4 // indirect
github.com/tetratelabs/wazero v1.8.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
@ -279,26 +274,27 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
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.33.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.23.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.29.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/protobuf v1.36.2 // indirect
golang.org/x/tools v0.30.0 // 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
modernc.org/sqlite v1.34.5 // indirect
modernc.org/sqlite v1.36.0 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
@ -316,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
@ -325,3 +321,5 @@ replace google.golang.org/genproto/googleapis/rpc => google.golang.org/genproto/
replace github.com/btcsuite/btcutil => github.com/btcsuite/btcd/btcutil v1.1.5
replace github.com/dsoprea/go-jpeg-image-structure/v2 => github.com/dchesterton/go-jpeg-image-structure/v2 v2.0.0-20240318203529-c3eea088bd38
replace zombiezen.com/go/sqlite => github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4

256
go.sum
View file

@ -56,10 +56,6 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8=
github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@ -82,10 +78,10 @@ github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
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.0 h1:JDxOTUuzGCHaeyid64tVIahYySCuonOK8Gwpf8s8lJ0=
github.com/anyproto/any-sync v0.6.0/go.mod h1:GrVtVp1VWqWRyYErJVsJVUhb6yzcvelQ+HWOXZ4f0tc=
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-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=
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a/go.mod h1:4fkueCZcGniSMXkrwESO8zzERrh/L7WHimRNWecfGM0=
github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580 h1:Ba80IlCCxkZ9H1GF+7vFu/TSpPvbpDCxXJ5ogc4euYc=
@ -96,14 +92,16 @@ 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=
github.com/anyproto/go-slip10 v1.0.0/go.mod h1:BCmIlM1KB8wX6K4/8pOvxPl9oVKfEvZ5vsmO5rkK6vg=
github.com/anyproto/go-slip21 v1.0.0 h1:CI7lUqTIwmPOEGVAj4jyNLoICvueh++0U2HoAi3m2ZY=
github.com/anyproto/go-slip21 v1.0.0/go.mod h1:gbIJt7HAdr5DuT4f2pFTKCBSUWYsm/fysHBNqgsuxT0=
github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4 h1:HzVjm45VOUVFUrxh2s0cRR4lqfCr/VAee6wNzPLcApI=
github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=
github.com/anyproto/html-to-markdown v0.0.0-20231025221133-830bf0a6f139 h1:Wp9z0Q2kAstznWUmTZyOb9UgpVmUgYt1LXRvK/cg10E=
github.com/anyproto/html-to-markdown v0.0.0-20231025221133-830bf0a6f139/go.mod h1:1zaDDQVWTRwNksmTUTkcVXqgNF28YHiEUIm8FL9Z+II=
github.com/anyproto/lexid v0.0.4 h1:2ztI0y5pNdtojd3vChw/YV/P6IO9pB7PccYysImDxWI=
@ -114,8 +112,8 @@ github.com/anyproto/protobuf v1.3.3-0.20240814124528-72b8c7e0e0f5 h1:aY7tBzQ+z8H
github.com/anyproto/protobuf v1.3.3-0.20240814124528-72b8c7e0e0f5/go.mod h1:5+PHE01DgsDPkralb8MYmGg2sPQahsqEJ9ue7ciDHKg=
github.com/anyproto/ristretto v0.1.2-0.20240221153107-2b23839cc50c h1:GicoaTUyB2mtCIl3YMrO0OzysqRT5GA4vuvDsqEkhSM=
github.com/anyproto/ristretto v0.1.2-0.20240221153107-2b23839cc50c/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/anyproto/tantivy-go v0.3.1 h1:59XePWnHlaTOgw1jVqpKD7oyZa8nwY5+U9UKnt0HHZU=
github.com/anyproto/tantivy-go v0.3.1/go.mod h1:7hhkPpyTq7+W1dx9Dcva4bsg4TLHq9xqmmYLCwqDq/k=
github.com/anyproto/tantivy-go v1.0.1 h1:Uc9WqwGEDsVUEwRgSg4nmhoW20GjMUBKRz5FYw4r+ns=
github.com/anyproto/tantivy-go v1.0.1/go.mod h1:LtipOpRjGtcYMGcop6gQN7rVl1Pc6BlIs9BTMqeWMsk=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20240228113933-f90a5cc4439d h1:5bj7nX/AS8sxGpTIrapE7PC4oPlhkHMwMqXlJbUHBlg=
github.com/anyproto/zeroconf/v2 v2.2.1-0.20240228113933-f90a5cc4439d/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@ -124,8 +122,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
@ -227,11 +225,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=
@ -283,8 +281,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=
@ -307,8 +305,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gammazero/chanqueue v1.0.0 h1:FER/sMailGFA3DDvFooEkipAMU+3c9Bg3bheloPSz6o=
github.com/gammazero/chanqueue v1.0.0/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc=
github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
@ -349,21 +347,15 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
@ -492,11 +484,10 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -531,8 +522,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -591,21 +580,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.27.4 h1:6nC8lY5GnR6whAbW88hFz6L13wZUj2vr5BRe3iTvYBI=
github.com/ipfs/boxo v0.27.4/go.mod h1:qEIRrGNr0bitDedTCzyzBHxzNWqYmyuHgK8LG9Q83EM=
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.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=
github.com/ipfs/go-datastore v0.7.0 h1:a6JMuRFKYhw6XXmIVoTthF8ZFm4QQXvLDXFhXRVv8Go=
github.com/ipfs/go-datastore v0.7.0/go.mod h1:ucOWMfbOPI6ZEyaIB1q/+78RPLBPERfuUVYX1EPnNpQ=
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.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4=
github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
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=
@ -616,15 +604,14 @@ github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk=
github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=
github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=
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=
@ -633,11 +620,8 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
@ -677,19 +661,18 @@ 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=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/koron/go-ssdp v0.0.5 h1:E1iSMxIs4WqxTbIBLtmNBeOOC+1sCIXQeqTWVnpmwhk=
github.com/koron/go-ssdp v0.0.5/go.mod h1:Qm59B7hpKpDqfyRNWRNr00jGwLdXjDyZh6y7rH6VS0w=
github.com/kovidgoyal/imaging v1.6.4 h1:K0idhRPXnRrJBKnBYcTfI1HTWSNDeAn7hYDvf9I0dCk=
github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnpB5EPosRfRY=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -706,22 +689,20 @@ 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.38.2 h1:9SZQDOCi82A25An4kx30lEtr6kGTxrtoaDkbs5xrK5k=
github.com/libp2p/go-libp2p v0.38.2/go.mod h1:QWV4zGL3O9nXKdHirIC59DoRcZ446dfkjbOJ55NEWFo=
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.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=
github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=
github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg=
github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
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/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/libp2p/go-yamux/v5 v5.0.0 h1:2djUh96d3Jiac/JpGkKs4TO49YhsfLopAoryfPmf+Po=
github.com/libp2p/go-yamux/v5 v5.0.0/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
@ -733,7 +714,6 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@ -763,8 +743,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
@ -886,36 +866,40 @@ github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
github.com/pion/ice/v2 v2.3.37 h1:ObIdaNDu1rCo7hObhs34YSBcO7fjslJMZV0ux+uZWh0=
github.com/pion/ice/v2 v2.3.37/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
github.com/pion/ice/v4 v4.0.6 h1:jmM9HwI9lfetQV/39uD0nY4y++XZNPhvzIPCb8EwxUM=
github.com/pion/ice/v4 v4.0.6/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
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.10 h1:puphjdbjPB+L+NFaVuZ5h6bt1g5q4kFIoI+r5q/g0CU=
github.com/pion/rtp v1.8.10/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/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
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.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=
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.3.5 h1:ZsSzaMz/i9nblPdiAkZoP+E6Kmjw+jnyq3bEmU3EtRg=
github.com/pion/webrtc/v3 v3.3.5/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE=
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.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=
@ -934,8 +918,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.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
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=
@ -965,8 +949,8 @@ github.com/pseudomuto/protokit v0.2.1 h1:kCYpE3thoR6Esm0CUvd5xbrDTOZPvQPTDeyXpZf
github.com/pseudomuto/protokit v0.2.1/go.mod h1:gt7N5Rz2flBzYafvaxyIxMZC0TTF5jDZfRnw25hAAyo=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg=
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@ -975,8 +959,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
@ -1150,16 +1134,18 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
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/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
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.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=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -1207,8 +1193,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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
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=
@ -1223,14 +1209,14 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
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=
@ -1244,8 +1230,8 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f h1:23H/YlmTHfmmvpZ+ajKZL0qLz0+IwFOIqQA0mQbmLeM=
golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f/go.mod h1:UbSUP4uu/C9hw9R2CkojhXlAxvayHjBdU9aRvE+c1To=
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 h1:0V/7Y1FEaFdAzb9DkVDh4QFp4vL4yYCiJ5cjk80lZyA=
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3/go.mod h1:j5VYNgQ6lZYZlzHFjdgS2UeqRSZunDk+/zXVTAIA3z4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@ -1260,8 +1246,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1309,7 +1295,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1324,8 +1309,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=
@ -1335,8 +1320,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.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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=
@ -1354,8 +1339,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=
@ -1415,7 +1400,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1441,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=
@ -1453,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=
@ -1470,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=
@ -1540,8 +1524,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1618,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=
@ -1645,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=
@ -1657,8 +1641,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -1700,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=
@ -1720,8 +1704,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8=
modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
@ -1738,5 +1722,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
storj.io/drpc v0.0.34 h1:q9zlQKfJ5A7x8NQNFk8x7eKUF78FMhmAbZLnFK+og7I=
storj.io/drpc v0.0.34/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg=
zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU=
zombiezen.com/go/sqlite v1.4.0/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=

View file

@ -1,4 +1,4 @@
PACK_SERVER_OS_ARCHS = windows-amd64 darwin-amd64 darwin-arm64 linux-amd64
PACK_SERVER_OS_ARCHS = windows-amd64 darwin-amd64 darwin-arm64 linux-amd64 linux-arm64
prepare-pack-server:
mkdir -p .release/
@ -6,11 +6,15 @@ pack-server-%:
@OSARCH=$*; \
if [ "$$OSARCH" = "windows-amd64" ]; then \
BINARY_NAME=grpc-server.exe; \
ARCHIVE_CMD="zip -r"; \
ARCHIVE_FILE="js_$(VERSION)_$$OSARCH.zip"; \
else \
BINARY_NAME=grpc-server; \
ARCHIVE_CMD="tar -czf"; \
ARCHIVE_FILE="js_$(VERSION)_$$OSARCH.tar.gz"; \
fi; \
cp ./$$OSARCH* ./$$BINARY_NAME; \
tar -czf js_$(VERSION)_$$OSARCH.tar.gz $$BINARY_NAME protobuf json; \
mv js_$(VERSION)_$$OSARCH.tar.gz .release/
$$ARCHIVE_CMD $$ARCHIVE_FILE $$BINARY_NAME protobuf json; \
mv $$ARCHIVE_FILE .release/
pack-server: prepare-pack-server $(addprefix pack-server-,$(PACK_SERVER_OS_ARCHS))

View file

@ -41,4 +41,4 @@ setup-protoc-jsweb:
mv deps/grpc-web/javascript/net/grpc/web/generator/protoc-gen-grpc-web deps/protoc-gen-grpc-web
@rm -rf deps/grpc-web
setup-protoc: setup-protoc-go setup-protoc-jsweb
setup-protoc: setup-protoc-go setup-protoc-jsweb

View file

@ -149,7 +149,6 @@ func (c *client) recordAggregatedData() {
toSend := c.aggregatableMap
c.aggregatableMap = make(map[string]SamplableEvent)
c.lock.Unlock()
// итерейтим сразу старую мапу и скармливаем ГЦ
for _, ev := range toSend {
c.send(ev)
}

View file

@ -7,8 +7,8 @@ import (
"time"
"github.com/cheggaaa/mb/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/valyala/fastjson"
"github.com/anyproto/anytype-heart/metrics/anymetry"
@ -79,19 +79,19 @@ func TestClient_SendEvents(t *testing.T) {
go c.startSendingBatchMessages(&testAppInfoProvider{})
c.send(&testEvent{})
time.Sleep(1 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
assert.Equal(t, 1, c.batcher.Len())
require.Equal(t, 1, c.batcher.Len())
telemetry.AssertNotCalled(t, "SendEvents", mock.Anything, mock.Anything)
c.send(&testEvent{})
time.Sleep(1 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
mutex.Lock()
assert.Equal(t, 0, c.batcher.Len())
assert.Equal(t, 2, len(events))
require.Equal(t, 0, c.batcher.Len())
require.Equal(t, 2, len(events))
mutex.Unlock()
assert.True(t, events[0].GetTimestamp() > 0)
require.True(t, events[0].GetTimestamp() > 0)
telemetry.AssertCalled(t, "SendEvents", mock.Anything, mock.Anything)
}

View file

@ -48,132 +48,6 @@ func (t ReindexType) String() string {
return "unknown"
}
const IndexEventThresholdMs = 10
type IndexEvent struct {
baseInfo
ObjectId string
IndexLinksTimeMs int64
IndexDetailsTimeMs int64
IndexSetRelationsTimeMs int64
RelationsCount int
DetailsCount int
SetRelationsCount int
}
func (c *IndexEvent) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *IndexEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
if c.IndexLinksTimeMs+c.IndexDetailsTimeMs+c.IndexSetRelationsTimeMs < IndexEventThresholdMs {
return nil
}
event, properties := setupProperties(arena, "index")
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("links_ms", arena.NewNumberInt(int(c.IndexLinksTimeMs)))
properties.Set("details_ms", arena.NewNumberInt(int(c.IndexDetailsTimeMs)))
properties.Set("set_ms", arena.NewNumberInt(int(c.IndexSetRelationsTimeMs)))
properties.Set("rel_count", arena.NewNumberInt(c.RelationsCount))
properties.Set("det_count", arena.NewNumberInt(c.DetailsCount))
properties.Set("set_rel_count", arena.NewNumberInt(c.SetRelationsCount))
properties.Set("total_ms", arena.NewNumberInt(int(c.IndexLinksTimeMs+c.IndexDetailsTimeMs+c.IndexSetRelationsTimeMs)))
return event
}
const ReindexEventThresholdsMs = 100
type ReindexEvent struct {
baseInfo
ReindexType ReindexType
Total int
Succeed int
SpentMs int
IndexesRemoved bool
}
func (c *ReindexEvent) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *ReindexEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
if c.SpentMs < ReindexEventThresholdsMs {
return nil
}
event, properties := setupProperties(arena, "store_reindex")
properties.Set("spent_ms", arena.NewNumberInt(c.SpentMs))
properties.Set("total", arena.NewNumberInt(c.Total))
properties.Set("failed", arena.NewNumberInt(c.Total-c.Succeed))
properties.Set("type", arena.NewNumberInt(int(c.ReindexType)))
var isRemoved *fastjson.Value
if c.IndexesRemoved {
isRemoved = arena.NewTrue()
} else {
isRemoved = arena.NewFalse()
}
properties.Set("ix_removed", isRemoved)
return event
}
const BlockSplitEventThresholdsMs = 10
type BlockSplit struct {
baseInfo
AlgorithmMs int64
ApplyMs int64
ObjectId string
}
func (c *BlockSplit) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *BlockSplit) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
if c.ApplyMs+c.AlgorithmMs < BlockSplitEventThresholdsMs {
return nil
}
event, properties := setupProperties(arena, "block_merge")
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("algorithm_ms", arena.NewNumberInt(int(c.AlgorithmMs)))
properties.Set("apply_ms", arena.NewNumberInt(int(c.ApplyMs)))
properties.Set("total_ms", arena.NewNumberInt(int(c.AlgorithmMs+c.ApplyMs)))
return event
}
type TreeBuild struct {
baseInfo
SbType uint64
TimeMs int64
ObjectId string
Logs int
Request string
}
func (c *TreeBuild) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *TreeBuild) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "tree_build")
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("logs", arena.NewNumberInt(c.Logs))
properties.Set("request", arena.NewString(c.Request))
properties.Set("time_ms", arena.NewNumberInt(int(c.TimeMs)))
properties.Set("sb_type", arena.NewNumberInt(int(c.SbType)))
return event
}
func setupProperties(arena *fastjson.Arena, eventType string) (*fastjson.Value, *fastjson.Value) {
event := arena.NewObject()
properties := arena.NewObject()
@ -182,153 +56,6 @@ func setupProperties(arena *fastjson.Arena, eventType string) (*fastjson.Value,
return event, properties
}
const StateApplyThresholdMs = 100
type StateApply struct {
baseInfo
BeforeApplyMs int64
StateApplyMs int64
PushChangeMs int64
ReportChangeMs int64
ApplyHookMs int64
ObjectId string
}
func (c *StateApply) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *StateApply) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
total := c.StateApplyMs + c.PushChangeMs + c.BeforeApplyMs + c.ApplyHookMs + c.ReportChangeMs
if total <= StateApplyThresholdMs {
return nil
}
event, properties := setupProperties(arena, "state_apply")
properties.Set("before_ms", arena.NewNumberInt(int(c.BeforeApplyMs)))
properties.Set("apply_ms", arena.NewNumberInt(int(c.StateApplyMs)))
properties.Set("push_ms", arena.NewNumberInt(int(c.PushChangeMs)))
properties.Set("report_ms", arena.NewNumberInt(int(c.ReportChangeMs)))
properties.Set("hook_ms", arena.NewNumberInt(int(c.ApplyHookMs)))
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("total_ms", arena.NewNumberInt(int(c.StateApplyMs+c.PushChangeMs+c.BeforeApplyMs+c.ApplyHookMs+c.ReportChangeMs)))
return event
}
type AppStart struct {
baseInfo
Request string
TotalMs int64
PerCompMs map[string]int64
Extra map[string]interface{}
}
func (c *AppStart) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *AppStart) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "app_start")
properties.Set("request", arena.NewString(c.Request))
properties.Set("time_ms", arena.NewNumberInt(int(c.TotalMs)))
for comp, ms := range c.PerCompMs {
properties.Set("spent_"+comp, arena.NewNumberInt(int(ms)))
}
for key, val := range c.Extra {
switch val := val.(type) {
case string:
properties.Set(key, arena.NewString(val))
case int64:
properties.Set(key, arena.NewNumberInt(int(val)))
}
}
return event
}
type BlockMerge struct {
baseInfo
AlgorithmMs int64
ApplyMs int64
ObjectId string
}
func (c *BlockMerge) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *BlockMerge) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "block_split")
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("algorithm_ms", arena.NewNumberInt(int(c.AlgorithmMs)))
properties.Set("apply_ms", arena.NewNumberInt(int(c.ApplyMs)))
properties.Set("total_ms", arena.NewNumberInt(int(c.AlgorithmMs+c.ApplyMs)))
return event
}
type CreateObjectEvent struct {
baseInfo
SetDetailsMs int64
GetWorkspaceBlockWaitMs int64
WorkspaceCreateMs int64
SmartblockCreateMs int64
SmartblockType int
ObjectId string
}
func (c *CreateObjectEvent) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *CreateObjectEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "create_object")
properties.Set("set_details_ms", arena.NewNumberInt(int(c.SetDetailsMs)))
properties.Set("get_workspace_block_wait_ms", arena.NewNumberInt(int(c.GetWorkspaceBlockWaitMs)))
properties.Set("workspace_create_ms", arena.NewNumberInt(int(c.WorkspaceCreateMs)))
properties.Set("smartblock_create_ms", arena.NewNumberInt(int(c.SmartblockCreateMs)))
properties.Set("total_ms", arena.NewNumberInt(int(c.SetDetailsMs+c.GetWorkspaceBlockWaitMs+c.WorkspaceCreateMs+c.SmartblockCreateMs)))
properties.Set("smartblock_type", arena.NewNumberInt(c.SmartblockType))
properties.Set("object_id", arena.NewString(c.ObjectId))
return event
}
type OpenBlockEvent struct {
baseInfo
GetBlockMs int64
DataviewMs int64
ApplyMs int64
ShowMs int64
FileWatcherMs int64
SmartblockType int
ObjectId string
}
func (c *OpenBlockEvent) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *OpenBlockEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "open_block")
properties.Set("object_id", arena.NewString(c.ObjectId))
properties.Set("get_block_ms", arena.NewNumberInt(int(c.GetBlockMs)))
properties.Set("dataview_notify_ms", arena.NewNumberInt(int(c.DataviewMs)))
properties.Set("apply_ms", arena.NewNumberInt(int(c.ApplyMs)))
properties.Set("show_ms", arena.NewNumberInt(int(c.ShowMs)))
properties.Set("file_watchers_ms", arena.NewNumberInt(int(c.FileWatcherMs)))
properties.Set("total_ms", arena.NewNumberInt(int(c.GetBlockMs+c.DataviewMs+c.ApplyMs+c.ShowMs+c.FileWatcherMs)))
properties.Set("smartblock_type", arena.NewNumberInt(c.SmartblockType))
return event
}
type ImportStartedEvent struct {
baseInfo
ID string
@ -401,22 +128,31 @@ func (c *MethodEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent
return event
}
type LongMethodEvent struct {
type ChangeEvent struct {
baseInfo
methodName string
middleTime int64
stack string
ChangeName string
SbType string
Count int
}
func (c *LongMethodEvent) GetBackend() anymetry.MetricsBackend {
func (c *ChangeEvent) Key() string {
return c.ChangeName
}
func (c *ChangeEvent) Aggregate(other SamplableEvent) SamplableEvent {
o := other.(*ChangeEvent)
c.Count += o.Count
return c
}
func (c *ChangeEvent) GetBackend() anymetry.MetricsBackend {
return inhouse
}
func (c *LongMethodEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "LongMethodEvent")
properties.Set("methodName", arena.NewString(c.methodName))
properties.Set("middleTime", arena.NewNumberInt(int(c.middleTime)))
properties.Set("stack", arena.NewString(c.stack))
func (c *ChangeEvent) MarshalFastJson(arena *fastjson.Arena) anymetry.JsonEvent {
event, properties := setupProperties(arena, "ChangeEvent")
properties.Set("changeName", arena.NewString(c.ChangeName))
properties.Set("sbType", arena.NewString(c.SbType))
properties.Set("count", arena.NewNumberInt(c.Count))
return event
}

View file

@ -230,14 +230,6 @@ func SharedLongMethodsInterceptor(ctx context.Context, req any, methodName strin
Warnf("grpc unary request took too long")
cache.addMethod(methodName)
}
Service.Send(
&LongMethodEvent{
methodName: methodName,
middleTime: time.Since(start).Milliseconds(),
stack: debug.ParseGoroutinesDump(lastTrace.String(), "core.(*Middleware)."+methodName),
},
)
}
return resp, err
}

View file

@ -16,9 +16,10 @@ import (
var (
Service = NewService()
clientMetricsLog = logging.Logger("service-metrics")
sendInterval = 10.0 * time.Second
sendInterval = 30.0 * time.Second
maxTimeout = 30 * time.Second
bufferSize = 500
eventsLimit = 1000 // throttle
)
// First constants must repeat syncstatus.SyncStatus constants for
@ -200,7 +201,7 @@ func (s *service) Run() {
for _, c := range s.clients {
c.ctx, c.cancel = context.WithCancel(context.Background())
c.setBatcher(mb.New[anymetry.Event](0))
c.setBatcher(mb.New[anymetry.Event](eventsLimit))
go c.startAggregating()
go c.startSendingBatchMessages(s)
}

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