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

GO-2827 Merge branch 'main' of github.com:anyproto/anytype-heart into go-2827-migration-unbind-user-files

# Conflicts:
#	.mockery.yaml
#	clientlibrary/service/service.pb.go
#	core/files/fileobject/service.go
#	docs/proto.md
#	pb/commands.pb.go
#	pb/service/service.pb.go
This commit is contained in:
Sergey 2024-06-10 14:30:17 +02:00
commit 431a455e94
No known key found for this signature in database
GPG key ID: 3B6BEF79160221C6
220 changed files with 13982 additions and 5137 deletions

View file

@ -16,18 +16,17 @@ jobs:
GOPRIVATE: github.com/anyproto
steps:
- name: Install Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.22
cache: false
- name: git config
run: git config --global url.https://${{ secrets.ANYTYPE_PAT }}@github.com/.insteadOf https://github.com/
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup GO
- name: Setup GO env
run: |
echo GOPATH=$(go env GOPATH) >> $GITHUB_ENV
echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
@ -61,7 +60,7 @@ jobs:
PACKAGE_NAMES=$(go list -tags nogrpcserver ./... | grep -v "github.com/anyproto/anytype-heart/cmd/grpserver" | grep -v "github.com/anyproto/anytype-heart/clientlibrary/clib")
rm -rf ~/gotestsum-report
mkdir ~/gotestsum-report
CGO_CFLAGS="-Wno-deprecated-declarations -Wno-deprecated-non-prototype -Wno-xor-used-as-pow" gotestsum --junitfile ~/gotestsum-report/gotestsum-report.xml -- -tags "nogrpcserver nographviz" -p 1 $(echo $PACKAGE_NAMES) -race -coverprofile=coverage.out -covermode=atomic ./...
CGO_CFLAGS="-Wno-deprecated-non-prototype -Wno-unknown-warning-option -Wno-deprecated-declarations -Wno-xor-used-as-pow -Wno-single-bit-bitfield-constant-conversion" gotestsum --junitfile ~/gotestsum-report/gotestsum-report.xml -- -tags "nogrpcserver nographviz" -ldflags="-extldflags=-Wl,-ld_classic" -p 1 $(echo $PACKAGE_NAMES) -race -coverprofile=coverage.out -covermode=atomic ./...
generated_pattern='^\/\/ Code generated .* DO NOT EDIT\.$'
files_list=$(grep -rl "$generated_pattern" . | grep '\.go$' | sed 's/^\.\///')

View file

@ -9,6 +9,8 @@ run:
go: '1.22'
linters-settings:
unused:
field-writes-are-uses: false
errcheck:
check-blank: true
errchkjson:

View file

@ -10,6 +10,9 @@ packages:
github.com/anyproto/anytype-heart/core/wallet:
interfaces:
Wallet:
github.com/anyproto/anytype-heart/core/nameservice:
interfaces:
Service:
github.com/anyproto/anytype-heart/core/event:
interfaces:
Sender:
@ -169,3 +172,6 @@ packages:
github.com/anyproto/anytype-heart/core/filestorage:
interfaces:
FileStorage:
github.com/anyproto/anytype-heart/util/linkpreview:
interfaces:
LinkPreview:

View file

@ -4,7 +4,7 @@ CLIENT_ANDROID_PATH ?= ../anytype-kotlin
CLIENT_IOS_PATH ?= ../anytype-swift
BUILD_FLAGS ?=
export GOLANGCI_LINT_VERSION=v1.54.2
export GOLANGCI_LINT_VERSION=1.58.1
export CGO_CFLAGS=-Wno-deprecated-non-prototype -Wno-unknown-warning-option -Wno-deprecated-declarations -Wno-xor-used-as-pow -Wno-single-bit-bitfield-constant-conversion
ifndef $(GOPATH)

View file

@ -25,306 +25,308 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func init() { proto.RegisterFile("pb/protos/service/service.proto", fileDescriptor_93a29dc403579097) }
var fileDescriptor_93a29dc403579097 = []byte{
// 4771 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x9d, 0xdd, 0x6f, 0x24, 0x49,
0x52, 0xc0, 0xb7, 0x5f, 0x58, 0xa8, 0xe3, 0x16, 0xa8, 0x85, 0x65, 0x6f, 0xb9, 0x9b, 0x99, 0x9d,
0x9d, 0x6f, 0x8f, 0xdb, 0xde, 0x99, 0xfd, 0x38, 0xf6, 0x90, 0x90, 0xc7, 0x1e, 0x7b, 0xcd, 0xd9,
0x1e, 0xe3, 0xb6, 0x67, 0xa4, 0x95, 0x90, 0x48, 0x57, 0xc7, 0xb4, 0x0b, 0x57, 0x57, 0xd6, 0x55,
0x65, 0xb7, 0xa7, 0x0f, 0x81, 0x40, 0x20, 0x10, 0x08, 0xc4, 0x89, 0xaf, 0x17, 0x1e, 0x90, 0xf8,
0x6b, 0x78, 0x3c, 0xf1, 0xc4, 0x23, 0xda, 0x95, 0xf8, 0x3b, 0x4e, 0x95, 0x95, 0x95, 0x1f, 0x51,
0x19, 0x59, 0xe5, 0x7d, 0x9a, 0x51, 0xc7, 0x2f, 0x22, 0x32, 0x2b, 0x23, 0x33, 0x23, 0x3f, 0xaa,
0x1c, 0xdd, 0x2c, 0xce, 0x37, 0x8a, 0x92, 0x0b, 0x5e, 0x6d, 0x54, 0x50, 0x2e, 0xd3, 0x04, 0xda,
0x7f, 0xc7, 0xf2, 0xe7, 0xf8, 0x6d, 0x96, 0xaf, 0xc4, 0xaa, 0x80, 0x0f, 0xde, 0x37, 0x64, 0xc2,
0xe7, 0x73, 0x96, 0x4f, 0xab, 0x06, 0xf9, 0xe0, 0x3d, 0x23, 0x81, 0x25, 0xe4, 0x42, 0xfd, 0xfe,
0xe4, 0xff, 0xff, 0x67, 0x14, 0xbd, 0xb3, 0x9d, 0xa5, 0x90, 0x8b, 0x6d, 0xa5, 0x11, 0x7f, 0x15,
0x7d, 0x77, 0xab, 0x28, 0xf6, 0x40, 0xbc, 0x84, 0xb2, 0x4a, 0x79, 0x1e, 0x7f, 0x34, 0x56, 0x0e,
0xc6, 0x27, 0x45, 0x32, 0xde, 0x2a, 0x8a, 0xb1, 0x11, 0x8e, 0x4f, 0xe0, 0x27, 0x0b, 0xa8, 0xc4,
0x07, 0x77, 0xc2, 0x50, 0x55, 0xf0, 0xbc, 0x82, 0xf8, 0x75, 0xf4, 0x1b, 0x5b, 0x45, 0x31, 0x01,
0xb1, 0x03, 0x75, 0x05, 0x26, 0x82, 0x09, 0x88, 0xef, 0x77, 0x54, 0x5d, 0x40, 0xfb, 0x78, 0xd0,
0x0f, 0x2a, 0x3f, 0xa7, 0xd1, 0x77, 0x6a, 0x3f, 0x17, 0x0b, 0x31, 0xe5, 0x57, 0x79, 0xfc, 0x61,
0x57, 0x51, 0x89, 0xb4, 0xed, 0xdb, 0x21, 0x44, 0x59, 0x7d, 0x15, 0xfd, 0xea, 0x2b, 0x96, 0x65,
0x20, 0xb6, 0x4b, 0xa8, 0x0b, 0xee, 0xea, 0x34, 0xa2, 0x71, 0x23, 0xd3, 0x76, 0x3f, 0x0a, 0x32,
0xca, 0xf0, 0x57, 0xd1, 0x77, 0x1b, 0xc9, 0x09, 0x24, 0x7c, 0x09, 0x65, 0xec, 0xd5, 0x52, 0x42,
0xe2, 0x91, 0x77, 0x20, 0x6c, 0x7b, 0x9b, 0xe7, 0x4b, 0x28, 0x85, 0xdf, 0xb6, 0x12, 0x86, 0x6d,
0x1b, 0x48, 0xd9, 0xfe, 0xbb, 0x51, 0xf4, 0xfd, 0xad, 0x24, 0xe1, 0x8b, 0x5c, 0x1c, 0xf0, 0x84,
0x65, 0x07, 0x69, 0x7e, 0x79, 0x04, 0x57, 0xdb, 0x17, 0x35, 0x9f, 0xcf, 0x20, 0x7e, 0xea, 0x3e,
0xd5, 0x06, 0x1d, 0x6b, 0x76, 0x6c, 0xc3, 0xda, 0xf7, 0x27, 0xd7, 0x53, 0x52, 0x65, 0xf9, 0xa7,
0x51, 0x74, 0x03, 0x97, 0x65, 0xc2, 0xb3, 0x25, 0x98, 0xd2, 0x7c, 0xda, 0x63, 0xd8, 0xc5, 0x75,
0x79, 0x3e, 0xbb, 0xae, 0x9a, 0x2a, 0x51, 0x16, 0xbd, 0x6b, 0x87, 0xcb, 0x04, 0x2a, 0xd9, 0x9d,
0x1e, 0xd2, 0x11, 0xa1, 0x10, 0xed, 0xf9, 0xd1, 0x10, 0x54, 0x79, 0x4b, 0xa3, 0x58, 0x79, 0xcb,
0x78, 0xa5, 0x9d, 0x3d, 0xf0, 0x5a, 0xb0, 0x08, 0xed, 0xeb, 0xe1, 0x00, 0x52, 0xb9, 0xfa, 0xe3,
0xe8, 0xd7, 0x5e, 0xf1, 0xf2, 0xb2, 0x2a, 0x58, 0x02, 0xaa, 0x2b, 0xdc, 0x75, 0xb5, 0x5b, 0x29,
0xee, 0x0d, 0xf7, 0xfa, 0x30, 0x2b, 0x68, 0x5b, 0xe1, 0x8b, 0x02, 0xf0, 0x18, 0x64, 0x14, 0x6b,
0x21, 0x15, 0xb4, 0x18, 0x52, 0xb6, 0x2f, 0xa3, 0xd8, 0xd8, 0x3e, 0xff, 0x13, 0x48, 0xc4, 0xd6,
0x74, 0x8a, 0x5b, 0xc5, 0xe8, 0x4a, 0x62, 0xbc, 0x35, 0x9d, 0x52, 0xad, 0xe2, 0x47, 0x95, 0xb3,
0xab, 0xe8, 0x3d, 0xe4, 0xec, 0x20, 0xad, 0xa4, 0xc3, 0xf5, 0xb0, 0x15, 0x85, 0x69, 0xa7, 0xe3,
0xa1, 0xb8, 0x72, 0xfc, 0x17, 0xa3, 0xe8, 0x7b, 0x1e, 0xcf, 0x27, 0x30, 0xe7, 0x4b, 0x88, 0x37,
0xfb, 0xad, 0x35, 0xa4, 0xf6, 0xff, 0xf1, 0x35, 0x34, 0x3c, 0x61, 0x32, 0x81, 0x0c, 0x12, 0x41,
0x86, 0x49, 0x23, 0xee, 0x0d, 0x13, 0x8d, 0x59, 0x3d, 0xac, 0x15, 0xee, 0x81, 0xd8, 0x5e, 0x94,
0x25, 0xe4, 0x82, 0x6c, 0x4b, 0x83, 0xf4, 0xb6, 0xa5, 0x83, 0x7a, 0xea, 0xb3, 0x07, 0x62, 0x2b,
0xcb, 0xc8, 0xfa, 0x34, 0xe2, 0xde, 0xfa, 0x68, 0x4c, 0x79, 0x48, 0xa2, 0x5f, 0xb7, 0x9e, 0x98,
0xd8, 0xcf, 0x5f, 0xf3, 0x98, 0x7e, 0x16, 0x52, 0xae, 0x7d, 0xdc, 0xef, 0xe5, 0x3c, 0xd5, 0x78,
0xfe, 0xa6, 0xe0, 0x25, 0xdd, 0x2c, 0x8d, 0xb8, 0xb7, 0x1a, 0x1a, 0x53, 0x1e, 0xfe, 0x28, 0x7a,
0x47, 0x8d, 0x92, 0xed, 0x7c, 0x76, 0xc7, 0x3b, 0x84, 0xe2, 0x09, 0xed, 0x6e, 0x0f, 0x65, 0x06,
0x07, 0x25, 0x53, 0x83, 0xcf, 0x47, 0x5e, 0x3d, 0x34, 0xf4, 0xdc, 0x09, 0x43, 0x1d, 0xdb, 0x3b,
0x90, 0x01, 0x69, 0xbb, 0x11, 0xf6, 0xd8, 0xd6, 0x90, 0xb2, 0x5d, 0x46, 0xbf, 0xa5, 0x1f, 0x4b,
0x3d, 0x8f, 0x4a, 0x79, 0x3d, 0x48, 0xaf, 0x11, 0xf5, 0xb6, 0x21, 0xed, 0xeb, 0xf1, 0x30, 0xb8,
0x53, 0x1f, 0xd5, 0x03, 0xfd, 0xf5, 0x41, 0xfd, 0xef, 0x4e, 0x18, 0x52, 0xb6, 0xff, 0x7e, 0x14,
0xfd, 0x40, 0xc9, 0x9e, 0xe7, 0xec, 0x3c, 0x03, 0x39, 0x25, 0x1e, 0x81, 0xb8, 0xe2, 0xe5, 0xe5,
0x64, 0x95, 0x27, 0xc4, 0xf4, 0xef, 0x87, 0x7b, 0xa6, 0x7f, 0x52, 0xc9, 0xca, 0xf8, 0x54, 0x45,
0x05, 0x2f, 0x70, 0xc6, 0xd7, 0xd6, 0x40, 0xf0, 0x82, 0xca, 0xf8, 0x5c, 0xa4, 0x63, 0xf5, 0xb0,
0x1e, 0x36, 0xfd, 0x56, 0x0f, 0xed, 0x71, 0xf2, 0x76, 0x08, 0x31, 0xc3, 0x56, 0x1b, 0xc0, 0x3c,
0x7f, 0x9d, 0xce, 0xce, 0x8a, 0x69, 0x1d, 0xc6, 0x0f, 0xfd, 0x11, 0x6a, 0x21, 0xc4, 0xb0, 0x45,
0xa0, 0xca, 0xdb, 0x3f, 0x9a, 0xc4, 0x48, 0x75, 0xa5, 0xdd, 0x92, 0xcf, 0x0f, 0x60, 0xc6, 0x92,
0x95, 0xea, 0xff, 0x9f, 0x84, 0x3a, 0x1e, 0xa6, 0x75, 0x21, 0x3e, 0xbd, 0xa6, 0x96, 0x2a, 0xcf,
0x7f, 0x8e, 0xa2, 0x3b, 0x6d, 0xf5, 0x2f, 0x58, 0x3e, 0x03, 0xd5, 0x9e, 0x4d, 0xe9, 0xb7, 0xf2,
0xe9, 0x09, 0x54, 0x82, 0x95, 0x22, 0xfe, 0xc2, 0x5f, 0xc9, 0x90, 0x8e, 0x2e, 0xdb, 0x8f, 0xbe,
0x95, 0xae, 0x69, 0xf5, 0x49, 0x3d, 0xb0, 0xa9, 0x21, 0xc0, 0x6d, 0x75, 0x29, 0xc1, 0x03, 0xc0,
0xed, 0x10, 0x62, 0x5a, 0x5d, 0x0a, 0xf6, 0xf3, 0x65, 0x2a, 0x60, 0x0f, 0x72, 0x28, 0xbb, 0xad,
0xde, 0xa8, 0xba, 0x08, 0xd1, 0xea, 0x04, 0x6a, 0x06, 0x1b, 0xc7, 0x9b, 0x9e, 0x1c, 0xd7, 0x02,
0x46, 0x3a, 0xd3, 0xe3, 0xe3, 0x61, 0xb0, 0x59, 0xdd, 0x59, 0x3e, 0x4f, 0x60, 0xc9, 0x2f, 0xf1,
0xea, 0xce, 0x36, 0xd1, 0x00, 0xc4, 0xea, 0xce, 0x0b, 0x9a, 0x19, 0xcc, 0xf2, 0xf3, 0x32, 0x85,
0x2b, 0x34, 0x83, 0xd9, 0xca, 0xb5, 0x98, 0x98, 0xc1, 0x3c, 0x98, 0xf2, 0x70, 0x14, 0xfd, 0x8a,
0x14, 0xfe, 0x01, 0x4f, 0xf3, 0xf8, 0xa6, 0x47, 0xa9, 0x16, 0x68, 0xab, 0xb7, 0x68, 0x00, 0x95,
0xb8, 0xfe, 0x75, 0x9b, 0xe5, 0x09, 0x64, 0xde, 0x12, 0x1b, 0x71, 0xb0, 0xc4, 0x0e, 0x66, 0x52,
0x07, 0x29, 0xac, 0xc7, 0xaf, 0xc9, 0x05, 0x2b, 0xd3, 0x7c, 0x16, 0xfb, 0x74, 0x2d, 0x39, 0x91,
0x3a, 0xf8, 0x38, 0x14, 0xc2, 0x4a, 0x71, 0xab, 0x28, 0xca, 0x7a, 0x58, 0xf4, 0x85, 0xb0, 0x8b,
0x04, 0x43, 0xb8, 0x83, 0xfa, 0xbd, 0xed, 0x40, 0x92, 0xa5, 0x79, 0xd0, 0x9b, 0x42, 0x86, 0x78,
0x33, 0x28, 0x0a, 0xde, 0x03, 0x60, 0x4b, 0x68, 0x6b, 0xe6, 0x7b, 0x32, 0x36, 0x10, 0x0c, 0x5e,
0x04, 0x9a, 0x75, 0x9a, 0x14, 0x1f, 0xb2, 0x4b, 0xa8, 0x1f, 0x30, 0xd4, 0xf3, 0x5a, 0xec, 0xd3,
0x77, 0x08, 0x62, 0x9d, 0xe6, 0x27, 0x95, 0xab, 0x45, 0xf4, 0x9e, 0x94, 0x1f, 0xb3, 0x52, 0xa4,
0x49, 0x5a, 0xb0, 0xbc, 0xcd, 0xff, 0x7d, 0xfd, 0xba, 0x43, 0x69, 0x97, 0xeb, 0x03, 0x69, 0xe5,
0xf6, 0xdf, 0x47, 0xd1, 0x87, 0xd8, 0xef, 0x31, 0x94, 0xf3, 0x54, 0x2e, 0x23, 0xab, 0x66, 0x10,
0x8e, 0x3f, 0x0f, 0x1b, 0xed, 0x28, 0xe8, 0xd2, 0xfc, 0xf0, 0xfa, 0x8a, 0xaa, 0x60, 0x7f, 0x18,
0x45, 0xcd, 0x72, 0x45, 0x2e, 0x29, 0xdd, 0x5e, 0xab, 0xd6, 0x31, 0xce, 0x7a, 0xf2, 0xc3, 0x00,
0x61, 0xa6, 0x8a, 0xe6, 0x77, 0xb9, 0x52, 0x8e, 0xbd, 0x1a, 0x52, 0x44, 0x4c, 0x15, 0x08, 0xc1,
0x05, 0x9d, 0x5c, 0xf0, 0x2b, 0x7f, 0x41, 0x6b, 0x49, 0xb8, 0xa0, 0x8a, 0x30, 0x7b, 0x57, 0xaa,
0xa0, 0xbe, 0xbd, 0xab, 0xb6, 0x18, 0xa1, 0xbd, 0x2b, 0xcc, 0x28, 0xc3, 0x3c, 0xfa, 0x4d, 0xdb,
0xf0, 0x33, 0xce, 0x2f, 0xe7, 0xac, 0xbc, 0x8c, 0x1f, 0xd1, 0xca, 0x2d, 0xa3, 0x1d, 0xad, 0x0d,
0x62, 0xcd, 0xb0, 0x60, 0x3b, 0xac, 0x13, 0x8d, 0xb3, 0x32, 0x43, 0xc3, 0x82, 0x63, 0x43, 0x21,
0xc4, 0xb0, 0x40, 0xa0, 0x66, 0xe4, 0xb6, 0xbd, 0x4d, 0x00, 0xaf, 0x96, 0x1c, 0xf5, 0x09, 0x50,
0xab, 0x25, 0x0f, 0x86, 0x43, 0x68, 0xaf, 0x64, 0xc5, 0x85, 0x3f, 0x84, 0xa4, 0x28, 0x1c, 0x42,
0x2d, 0x82, 0xdb, 0x7b, 0x02, 0xac, 0x4c, 0x2e, 0xfc, 0xed, 0xdd, 0xc8, 0xc2, 0xed, 0xad, 0x19,
0x93, 0x58, 0xd8, 0x86, 0x27, 0x8b, 0xf3, 0x2a, 0x29, 0xd3, 0x73, 0x88, 0xd7, 0x68, 0x6d, 0x0d,
0x11, 0x89, 0x05, 0x09, 0x9b, 0x31, 0x53, 0xf9, 0x6c, 0x65, 0xfb, 0xd3, 0x0a, 0x8d, 0x99, 0xad,
0x0d, 0x8b, 0x20, 0xc6, 0x4c, 0x3f, 0x89, 0xab, 0xb7, 0x57, 0xf2, 0x45, 0x51, 0xf5, 0x54, 0x0f,
0x41, 0xe1, 0xea, 0x75, 0x61, 0xe5, 0xf3, 0x4d, 0xf4, 0xdb, 0xf6, 0x23, 0x3d, 0xcb, 0x2b, 0xed,
0x75, 0x9d, 0x7e, 0x4e, 0x16, 0x46, 0xec, 0x12, 0x05, 0x70, 0x93, 0x35, 0xb4, 0x9e, 0xc5, 0x0e,
0x08, 0x96, 0x66, 0x55, 0x7c, 0xcf, 0x6f, 0xa3, 0x95, 0x13, 0x59, 0x83, 0x8f, 0xc3, 0x5d, 0x68,
0x67, 0x51, 0x64, 0x69, 0xd2, 0xdd, 0x2e, 0x54, 0xba, 0x5a, 0x1c, 0xee, 0x42, 0x36, 0x86, 0x87,
0x84, 0x09, 0x88, 0xe6, 0x3f, 0xa7, 0xab, 0x02, 0xfc, 0x43, 0x82, 0x83, 0x84, 0x87, 0x04, 0x8c,
0xe2, 0xfa, 0x4c, 0x40, 0x1c, 0xb0, 0x15, 0x5f, 0x10, 0x43, 0x82, 0x16, 0x87, 0xeb, 0x63, 0x63,
0x66, 0xe2, 0xd6, 0x1e, 0xf6, 0x73, 0x01, 0x65, 0xce, 0xb2, 0xdd, 0x8c, 0xcd, 0xaa, 0x98, 0xe8,
0x37, 0x2e, 0x45, 0x4c, 0xdc, 0x34, 0xed, 0x79, 0x8c, 0xfb, 0xd5, 0x2e, 0x5b, 0xf2, 0x32, 0x15,
0xf4, 0x63, 0x34, 0x48, 0xef, 0x63, 0x74, 0x50, 0xaf, 0xb7, 0xad, 0x32, 0xb9, 0x48, 0x97, 0x30,
0x0d, 0x78, 0x6b, 0x91, 0x01, 0xde, 0x2c, 0xd4, 0xd3, 0x68, 0x13, 0xbe, 0x28, 0x13, 0x20, 0x1b,
0xad, 0x11, 0xf7, 0x36, 0x9a, 0xc6, 0x94, 0x87, 0xbf, 0x1e, 0x45, 0xbf, 0xd3, 0x48, 0xed, 0x3d,
0xbc, 0x1d, 0x56, 0x5d, 0x9c, 0x73, 0x56, 0x4e, 0xe3, 0x8f, 0x7d, 0x76, 0xbc, 0xa8, 0x76, 0xfd,
0xe4, 0x3a, 0x2a, 0xf8, 0xb1, 0x1e, 0xa4, 0x95, 0xd5, 0xe3, 0xbc, 0x8f, 0xd5, 0x41, 0xc2, 0x8f,
0x15, 0xa3, 0x78, 0x00, 0x91, 0xf2, 0x66, 0xbd, 0x7c, 0x8f, 0xd4, 0x77, 0x17, 0xcd, 0xf7, 0x7b,
0x39, 0x3c, 0x3e, 0xd6, 0x42, 0x37, 0x5a, 0xd6, 0x29, 0x1b, 0xfe, 0x88, 0x19, 0x0f, 0xc5, 0x49,
0xcf, 0xba, 0x57, 0x84, 0x3d, 0x77, 0x7a, 0xc6, 0x78, 0x28, 0x4e, 0x78, 0xb6, 0x86, 0xb5, 0x90,
0x67, 0xcf, 0xd0, 0x36, 0x1e, 0x8a, 0xe3, 0x84, 0x4e, 0x31, 0xed, 0xbc, 0xf0, 0x28, 0x60, 0x07,
0xcf, 0x0d, 0x6b, 0x83, 0x58, 0x1c, 0xb1, 0x5b, 0x45, 0x91, 0xad, 0x4e, 0x61, 0x5e, 0x64, 0x64,
0xc4, 0x3a, 0x48, 0x38, 0x62, 0x31, 0x8a, 0xd3, 0xad, 0x53, 0x5e, 0x27, 0x73, 0xde, 0x74, 0x4b,
0x8a, 0xc2, 0xe9, 0x56, 0x8b, 0xe0, 0x0c, 0xe5, 0x94, 0x6f, 0xf3, 0x2c, 0x83, 0x44, 0x74, 0x4f,
0xdf, 0xb4, 0xa6, 0x21, 0xc2, 0x19, 0x0a, 0x22, 0xcd, 0x42, 0xb5, 0x5d, 0x1c, 0xb0, 0x12, 0x9e,
0xad, 0x0e, 0xd2, 0xfc, 0x32, 0xf6, 0x4f, 0xc6, 0x06, 0x20, 0x16, 0xaa, 0x5e, 0x10, 0x2f, 0x42,
0xce, 0xf2, 0x29, 0xf7, 0x2f, 0x42, 0x6a, 0x49, 0x78, 0x11, 0xa2, 0x08, 0x6c, 0xf2, 0x04, 0x28,
0x93, 0xb5, 0x24, 0x6c, 0x52, 0x11, 0xbe, 0x01, 0x48, 0x6d, 0x67, 0x92, 0x03, 0x10, 0xda, 0xc0,
0xbc, 0xdf, 0xcb, 0xe1, 0x08, 0x6d, 0x57, 0x23, 0xbb, 0x20, 0x92, 0x0b, 0x7f, 0x84, 0x3a, 0x48,
0x38, 0x42, 0x31, 0x8a, 0xab, 0x74, 0xca, 0xf5, 0x6a, 0xea, 0x9e, 0x3f, 0x3e, 0x3a, 0x2b, 0xa9,
0xfb, 0xbd, 0x1c, 0x5e, 0x1f, 0xec, 0xcf, 0xe5, 0x33, 0xf3, 0x06, 0x79, 0x23, 0x0b, 0xaf, 0x0f,
0x34, 0x83, 0x4b, 0xdf, 0x08, 0xea, 0xc7, 0xe9, 0x2f, 0xbd, 0x91, 0x87, 0x4b, 0xef, 0x70, 0xca,
0xc9, 0xbf, 0x8e, 0xa2, 0x9b, 0xb6, 0x97, 0x23, 0x5e, 0xf7, 0x91, 0x97, 0x2c, 0x4b, 0xa7, 0x4c,
0xc0, 0x29, 0xbf, 0x84, 0x1c, 0x6d, 0x30, 0xb8, 0xa5, 0x6d, 0xf8, 0xb1, 0xa3, 0x40, 0x6c, 0x30,
0x0c, 0x52, 0xc4, 0x71, 0xd2, 0xd0, 0x67, 0x15, 0x6c, 0xb3, 0x8a, 0x18, 0xc9, 0x1c, 0x24, 0x1c,
0x27, 0x18, 0xc5, 0x59, 0x62, 0x23, 0x7f, 0xfe, 0xa6, 0x80, 0x32, 0x85, 0x3c, 0x01, 0x7f, 0x96,
0x88, 0xa9, 0x70, 0x96, 0xe8, 0xa1, 0x3b, 0xeb, 0x6f, 0x3d, 0x38, 0x75, 0x0f, 0xd0, 0x31, 0x11,
0x38, 0x40, 0x27, 0x50, 0x5c, 0x49, 0x03, 0x78, 0xf7, 0xb0, 0x3a, 0x56, 0x82, 0x7b, 0x58, 0x34,
0xdd, 0xd9, 0xd5, 0xd0, 0xcc, 0xa4, 0xee, 0x26, 0x3d, 0x45, 0x9f, 0xd8, 0xdd, 0x65, 0x6d, 0x10,
0xeb, 0xdf, 0x46, 0x39, 0x81, 0x8c, 0xc9, 0x29, 0x24, 0xb0, 0x57, 0xd1, 0x32, 0x43, 0xb6, 0x51,
0x2c, 0x56, 0x39, 0xfc, 0xcb, 0x51, 0xf4, 0x81, 0xcf, 0xe3, 0x8b, 0x42, 0xfa, 0xdd, 0xec, 0xb7,
0xd5, 0x90, 0xc4, 0x0d, 0x81, 0xb0, 0x86, 0x2a, 0xc3, 0x9f, 0x46, 0xef, 0xb7, 0x22, 0x73, 0x81,
0x40, 0x15, 0xc0, 0x4d, 0x5b, 0x74, 0xf9, 0x31, 0xa7, 0xdd, 0x6f, 0x0c, 0xe6, 0xcd, 0x8a, 0xc0,
0x2d, 0x57, 0x85, 0x56, 0x04, 0xda, 0x86, 0x12, 0x13, 0x2b, 0x02, 0x0f, 0x86, 0x67, 0xea, 0x16,
0xa9, 0xfb, 0x89, 0x6f, 0x8c, 0xd3, 0x26, 0xec, 0x5e, 0xf2, 0xa0, 0x1f, 0xc4, 0xb1, 0xd3, 0x8a,
0x55, 0x22, 0xfe, 0x28, 0x64, 0x01, 0x25, 0xe3, 0x6b, 0x83, 0x58, 0xe5, 0xf0, 0xcf, 0xa3, 0xef,
0x75, 0x2a, 0xb6, 0x0b, 0x4c, 0x2c, 0x4a, 0x98, 0xc6, 0x1b, 0x3d, 0xe5, 0x6e, 0x41, 0xed, 0x7a,
0x73, 0xb8, 0x82, 0xf2, 0xff, 0xb7, 0xa3, 0xe8, 0xfb, 0x2e, 0xd7, 0x34, 0xb1, 0x2e, 0xc3, 0x93,
0x90, 0x49, 0x97, 0xd5, 0xc5, 0x78, 0x7a, 0x2d, 0x9d, 0xce, 0xa2, 0xcf, 0x0e, 0xe4, 0xad, 0x25,
0x4b, 0x33, 0xb9, 0xaf, 0xff, 0x71, 0xc8, 0xa8, 0x83, 0x06, 0x17, 0x7d, 0xa4, 0x4a, 0x67, 0x94,
0x94, 0xfd, 0xcd, 0x5a, 0x2c, 0x3c, 0xa6, 0x7b, 0xa5, 0x67, 0xad, 0xb0, 0x3e, 0x90, 0x56, 0x6e,
0x45, 0xbb, 0x59, 0x56, 0xff, 0x6c, 0x07, 0xb9, 0xcf, 0xab, 0x52, 0xf5, 0x44, 0xfa, 0xfa, 0x40,
0x5a, 0x79, 0xfd, 0xb3, 0xe8, 0xfd, 0xae, 0x57, 0x35, 0x29, 0x6c, 0xf4, 0x9a, 0x42, 0xf3, 0xc2,
0xe6, 0x70, 0x05, 0x93, 0xea, 0x7f, 0x99, 0x56, 0x82, 0x97, 0xab, 0xc9, 0x05, 0xbf, 0x6a, 0x2f,
0xc9, 0xba, 0xbd, 0x55, 0x01, 0x63, 0x8b, 0x20, 0x52, 0x7d, 0x3f, 0xd9, 0x71, 0x65, 0x2e, 0xd3,
0x56, 0x84, 0x2b, 0x8b, 0xe8, 0x71, 0xe5, 0x92, 0x66, 0xac, 0x6a, 0x6b, 0x65, 0x6e, 0xfe, 0xde,
0xf7, 0x17, 0xb5, 0x7b, 0xfb, 0xf7, 0x41, 0x3f, 0x68, 0xd2, 0xc3, 0xdd, 0x34, 0x03, 0x79, 0x6c,
0xf3, 0xe2, 0xf5, 0xeb, 0x8c, 0xb3, 0x29, 0x4a, 0x0f, 0x6b, 0xf1, 0xd8, 0x96, 0x13, 0xe9, 0xa1,
0x8f, 0x33, 0xb7, 0x5e, 0x6a, 0xe9, 0x09, 0x24, 0x3c, 0x4f, 0xd2, 0x0c, 0xdf, 0xe2, 0x91, 0x9a,
0x5a, 0x48, 0xdc, 0x7a, 0xe9, 0x40, 0x66, 0xda, 0xa8, 0x45, 0x75, 0x47, 0x6c, 0xcb, 0x7f, 0xb7,
0xab, 0x68, 0x89, 0x89, 0x69, 0xc3, 0x83, 0x99, 0x55, 0x52, 0x2d, 0x3c, 0x2b, 0xa4, 0xf1, 0x5b,
0x5d, 0xad, 0x46, 0x42, 0xac, 0x92, 0x5c, 0xc2, 0x64, 0xfb, 0xf5, 0xef, 0x3b, 0xfc, 0x2a, 0x97,
0x46, 0x6f, 0x77, 0x55, 0x5a, 0x19, 0x91, 0xed, 0x63, 0x46, 0x19, 0xfe, 0x71, 0xf4, 0xcb, 0xd2,
0x70, 0xc9, 0x8b, 0xf8, 0x86, 0x47, 0xa1, 0xb4, 0x2e, 0xdc, 0xdc, 0x24, 0xe5, 0xe6, 0xde, 0x98,
0x8e, 0x8d, 0xb3, 0x8a, 0xcd, 0x20, 0xbe, 0x43, 0xb4, 0xb8, 0x94, 0x12, 0xf7, 0xc6, 0xba, 0x94,
0x1b, 0x15, 0x47, 0x7c, 0xaa, 0xac, 0x7b, 0x6a, 0xa8, 0x85, 0xa1, 0xa8, 0xb0, 0x21, 0x73, 0x6c,
0x70, 0xc4, 0x96, 0xe9, 0x4c, 0x4f, 0x01, 0xcd, 0x48, 0x52, 0xa1, 0x63, 0x03, 0xc3, 0x8c, 0x2d,
0x88, 0x38, 0x36, 0x20, 0x61, 0xe5, 0xf3, 0x5f, 0x46, 0xd1, 0x2d, 0xc3, 0xec, 0xb5, 0xbb, 0x39,
0xfb, 0xf9, 0x6b, 0xfe, 0x2a, 0x15, 0x17, 0xf5, 0x6a, 0xbe, 0x8a, 0x3f, 0xa3, 0x4c, 0xfa, 0x79,
0x5d, 0x94, 0xcf, 0xaf, 0xad, 0x67, 0x72, 0xba, 0x76, 0xd3, 0xc5, 0x1c, 0xa9, 0x35, 0x1a, 0x28,
0xa7, 0xd3, 0x7b, 0x33, 0x98, 0x23, 0x72, 0xba, 0x10, 0x6f, 0x9a, 0x58, 0x3b, 0xcf, 0x78, 0x8e,
0x9b, 0xd8, 0x58, 0xa8, 0x85, 0x44, 0x13, 0x77, 0x20, 0x33, 0x42, 0xb6, 0xa2, 0x66, 0x7f, 0x60,
0x2b, 0xcb, 0xd0, 0x08, 0xa9, 0x55, 0x35, 0x40, 0x8c, 0x90, 0x5e, 0x50, 0xf9, 0x39, 0x89, 0xbe,
0x53, 0x3f, 0xd2, 0xe3, 0x12, 0x96, 0x29, 0xe0, 0xd3, 0x5f, 0x4b, 0x42, 0xf4, 0x7f, 0x97, 0x30,
0x3d, 0xeb, 0x2c, 0xaf, 0x8a, 0x8c, 0x55, 0x17, 0xea, 0x3c, 0xd0, 0xad, 0x73, 0x2b, 0xc4, 0x27,
0x82, 0x77, 0x7b, 0x28, 0x33, 0xa8, 0xb7, 0x32, 0x3d, 0xc4, 0xdc, 0xf3, 0xab, 0x76, 0x86, 0x99,
0xfb, 0xbd, 0x9c, 0xd9, 0x11, 0xdd, 0x63, 0x59, 0x06, 0xe5, 0xaa, 0x95, 0x1d, 0xb2, 0x3c, 0x7d,
0x0d, 0x95, 0x40, 0x3b, 0xa2, 0x8a, 0x1a, 0x63, 0x8c, 0xd8, 0x11, 0x0d, 0xe0, 0x26, 0xbf, 0x46,
0x9e, 0xf7, 0xf3, 0x29, 0xbc, 0x41, 0xf9, 0x35, 0xb6, 0x23, 0x19, 0x22, 0xbf, 0xa6, 0x58, 0xb3,
0x47, 0xf9, 0x2c, 0xe3, 0xc9, 0xa5, 0x9a, 0x02, 0xdc, 0x06, 0x96, 0x12, 0x3c, 0x07, 0xdc, 0x0e,
0x21, 0x66, 0x12, 0x90, 0x82, 0x13, 0x28, 0x32, 0x96, 0xe0, 0x2b, 0x00, 0x8d, 0x8e, 0x92, 0x11,
0x93, 0x00, 0x66, 0x50, 0x71, 0xd5, 0xd5, 0x02, 0x5f, 0x71, 0xd1, 0xcd, 0x82, 0xdb, 0x21, 0xc4,
0x4c, 0x83, 0x52, 0x30, 0x29, 0xb2, 0x54, 0xa0, 0x6e, 0xd0, 0x68, 0x48, 0x09, 0xd1, 0x0d, 0x5c,
0x02, 0x99, 0x3c, 0x84, 0x72, 0x06, 0x5e, 0x93, 0x52, 0x12, 0x34, 0xd9, 0x12, 0xe6, 0xa6, 0x58,
0x53, 0x77, 0x5e, 0xac, 0xd0, 0x4d, 0x31, 0x55, 0x2d, 0x5e, 0xac, 0x88, 0x9b, 0x62, 0x0e, 0x80,
0x8a, 0x78, 0xcc, 0x2a, 0xe1, 0x2f, 0xa2, 0x94, 0x04, 0x8b, 0xd8, 0x12, 0x66, 0x8e, 0x6e, 0x8a,
0xb8, 0x10, 0x68, 0x8e, 0x56, 0x05, 0xb0, 0x4e, 0x28, 0x6f, 0x92, 0x72, 0x33, 0x92, 0x34, 0xad,
0x02, 0x62, 0x37, 0x85, 0x6c, 0x5a, 0xa1, 0x91, 0x44, 0x3d, 0xf7, 0x56, 0x4a, 0x8c, 0x24, 0x5d,
0x0a, 0x85, 0x92, 0xda, 0xc9, 0xf5, 0xd5, 0x0e, 0x6d, 0xe2, 0xde, 0x0e, 0x21, 0x66, 0x7c, 0x6a,
0x0b, 0xbd, 0xcd, 0xca, 0x32, 0xad, 0x27, 0xff, 0x7b, 0xfe, 0x02, 0xb5, 0x72, 0x62, 0x7c, 0xf2,
0x71, 0xa8, 0x7b, 0xb5, 0x03, 0xb7, 0xaf, 0x60, 0x78, 0xe8, 0xfe, 0x28, 0xc8, 0x98, 0x8c, 0x53,
0x4a, 0xac, 0x23, 0x36, 0xdf, 0xd3, 0xf4, 0x9c, 0xb0, 0xdd, 0xeb, 0xc3, 0xac, 0x9b, 0xdc, 0xda,
0xc5, 0x21, 0x5f, 0xc2, 0x29, 0x7f, 0xfe, 0x26, 0xad, 0x44, 0x9a, 0xcf, 0xd4, 0xcc, 0xfd, 0x94,
0xb0, 0xe4, 0x83, 0x89, 0x9b, 0xdc, 0xbd, 0x4a, 0x26, 0x81, 0x40, 0x65, 0x39, 0x82, 0x2b, 0x6f,
0x02, 0x81, 0x2d, 0x6a, 0x8e, 0x48, 0x20, 0x42, 0xbc, 0xd9, 0xd9, 0xd0, 0xce, 0xd5, 0xeb, 0x6e,
0xa7, 0xbc, 0xcd, 0xe5, 0x28, 0x6b, 0x18, 0x24, 0x16, 0x97, 0x41, 0x05, 0xb3, 0xe2, 0xd3, 0xfe,
0x4d, 0x17, 0x7b, 0x40, 0xd8, 0xe9, 0x76, 0xb3, 0x87, 0x03, 0x48, 0x8f, 0x2b, 0x73, 0x4e, 0x4c,
0xb9, 0xea, 0x1e, 0x13, 0x3f, 0x1c, 0x40, 0x5a, 0xbb, 0x24, 0x76, 0xb5, 0x9e, 0xb1, 0xe4, 0x72,
0x56, 0xf2, 0x45, 0x3e, 0xdd, 0xe6, 0x19, 0x2f, 0xd1, 0x2e, 0x89, 0x53, 0x6a, 0x84, 0x12, 0xbb,
0x24, 0x3d, 0x2a, 0x26, 0x83, 0xb3, 0x4b, 0xb1, 0x95, 0xa5, 0x33, 0xbc, 0xc6, 0x75, 0x0c, 0x49,
0x80, 0xc8, 0xe0, 0xbc, 0xa0, 0x27, 0x88, 0x9a, 0x35, 0xb0, 0x48, 0x13, 0x96, 0x35, 0xfe, 0x36,
0x68, 0x33, 0x0e, 0xd8, 0x1b, 0x44, 0x1e, 0x05, 0x4f, 0x3d, 0x4f, 0x17, 0x65, 0xbe, 0x9f, 0x0b,
0x4e, 0xd6, 0xb3, 0x05, 0x7a, 0xeb, 0x69, 0x81, 0x68, 0x58, 0x3d, 0x85, 0x37, 0x75, 0x69, 0xea,
0x7f, 0x7c, 0xc3, 0x6a, 0xfd, 0xfb, 0x58, 0xc9, 0x43, 0xc3, 0x2a, 0xe2, 0x50, 0x65, 0x94, 0x93,
0x26, 0x60, 0x02, 0xda, 0x6e, 0x98, 0x3c, 0xe8, 0x07, 0xfd, 0x7e, 0x26, 0x62, 0x95, 0x41, 0xc8,
0x8f, 0x04, 0x86, 0xf8, 0x69, 0x41, 0x73, 0x7c, 0xe2, 0xd4, 0xe7, 0x02, 0x92, 0xcb, 0xce, 0xb5,
0x17, 0xb7, 0xa0, 0x0d, 0x42, 0x1c, 0x9f, 0x10, 0xa8, 0xbf, 0x89, 0xf6, 0x13, 0x9e, 0x87, 0x9a,
0xa8, 0x96, 0x0f, 0x69, 0x22, 0xc5, 0x99, 0xc5, 0xaf, 0x96, 0xaa, 0xc8, 0x6c, 0x9a, 0x69, 0x8d,
0xb0, 0x60, 0x43, 0xc4, 0xe2, 0x97, 0x84, 0x4d, 0x4e, 0x8e, 0x7d, 0x1e, 0x76, 0xaf, 0x9d, 0x76,
0xac, 0x1c, 0xd2, 0xd7, 0x4e, 0x29, 0x96, 0xae, 0x64, 0x13, 0x23, 0x3d, 0x56, 0xdc, 0x38, 0x79,
0x3c, 0x0c, 0x36, 0x4b, 0x1e, 0xc7, 0xe7, 0x76, 0x06, 0xac, 0x6c, 0xbc, 0xae, 0x07, 0x0c, 0x19,
0x8c, 0x58, 0xf2, 0x04, 0x70, 0x34, 0x84, 0x39, 0x9e, 0xb7, 0x79, 0x2e, 0x20, 0x17, 0xbe, 0x21,
0xcc, 0x35, 0xa6, 0xc0, 0xd0, 0x10, 0x46, 0x29, 0xa0, 0xb8, 0x95, 0xfb, 0x41, 0x20, 0x8e, 0xd8,
0xdc, 0x9b, 0xb1, 0x35, 0x7b, 0x3d, 0x8d, 0x3c, 0x14, 0xb7, 0x88, 0xb3, 0x8e, 0xc0, 0x6c, 0x2f,
0xa7, 0xac, 0x9c, 0xe9, 0xdd, 0x8d, 0x69, 0xbc, 0x49, 0xdb, 0x71, 0x49, 0xe2, 0x08, 0x2c, 0xac,
0x81, 0x86, 0x9d, 0xfd, 0x39, 0x9b, 0xe9, 0x9a, 0x7a, 0x6a, 0x20, 0xe5, 0x9d, 0xaa, 0x3e, 0xe8,
0x07, 0x91, 0x9f, 0x97, 0xe9, 0x14, 0x78, 0xc0, 0x8f, 0x94, 0x0f, 0xf1, 0x83, 0x41, 0x94, 0xbd,
0xd5, 0xf5, 0x6e, 0x56, 0x74, 0x5b, 0xf9, 0x54, 0xad, 0x63, 0xc7, 0xc4, 0xe3, 0x41, 0x5c, 0x28,
0x7b, 0x23, 0x78, 0xd4, 0x47, 0xdb, 0x0d, 0xda, 0x50, 0x1f, 0xd5, 0xfb, 0xaf, 0x43, 0xfa, 0xa8,
0x0f, 0x56, 0x3e, 0x7f, 0xaa, 0xfa, 0xe8, 0x0e, 0x13, 0xac, 0xce, 0xdb, 0x5f, 0xa6, 0x70, 0xa5,
0x16, 0xc2, 0x9e, 0xfa, 0xb6, 0xd4, 0x58, 0xbe, 0x6f, 0x84, 0x56, 0xc5, 0x1b, 0x83, 0xf9, 0x80,
0x6f, 0xb5, 0x42, 0xe8, 0xf5, 0x8d, 0x96, 0x0a, 0x1b, 0x83, 0xf9, 0x80, 0x6f, 0xf5, 0x22, 0x63,
0xaf, 0x6f, 0xf4, 0x36, 0xe3, 0xc6, 0x60, 0x5e, 0xf9, 0xfe, 0xab, 0xb6, 0xe3, 0xda, 0xce, 0xeb,
0x3c, 0x2c, 0x11, 0xe9, 0x12, 0x7c, 0xe9, 0xa4, 0x6b, 0x4f, 0xa3, 0xa1, 0x74, 0x92, 0x56, 0xb1,
0xbe, 0x7e, 0xe1, 0x2b, 0xc5, 0x31, 0xaf, 0x52, 0x79, 0x84, 0xfd, 0x74, 0x80, 0xd1, 0x16, 0x0e,
0x2d, 0x9a, 0x42, 0x4a, 0xe6, 0x00, 0xd0, 0x41, 0xcd, 0x2d, 0xd7, 0xc7, 0x01, 0x7b, 0xdd, 0xcb,
0xae, 0xeb, 0x03, 0x69, 0x73, 0x14, 0xe7, 0x30, 0xf6, 0x19, 0x60, 0xa8, 0x55, 0xbd, 0xc7, 0x80,
0x9b, 0xc3, 0x15, 0x94, 0xfb, 0xbf, 0x69, 0xd7, 0x15, 0xd8, 0xbf, 0xea, 0x04, 0x4f, 0x86, 0x58,
0x44, 0x1d, 0xe1, 0xe9, 0xb5, 0x74, 0x54, 0x41, 0xfe, 0x63, 0x14, 0xdd, 0xf6, 0x16, 0xc4, 0x3d,
0x0d, 0xfe, 0xdd, 0x21, 0xb6, 0xfd, 0xa7, 0xc2, 0x5f, 0x7c, 0x1b, 0x55, 0x55, 0xba, 0x7f, 0x68,
0x97, 0xf7, 0xad, 0x86, 0x7c, 0x13, 0xe1, 0x45, 0x39, 0x85, 0x52, 0xf5, 0xd8, 0x50, 0xd0, 0x19,
0x18, 0xf7, 0xdb, 0x4f, 0xaf, 0xa9, 0x65, 0x7d, 0xa9, 0xc5, 0x81, 0xd5, 0x4b, 0x59, 0x56, 0x79,
0x42, 0x96, 0x2d, 0x1a, 0x17, 0xe8, 0xb3, 0xeb, 0xaa, 0x51, 0x3d, 0xd9, 0x82, 0xe5, 0x8b, 0xdf,
0x4f, 0x07, 0x1a, 0x76, 0x5e, 0x05, 0xff, 0xe4, 0x7a, 0x4a, 0xaa, 0x2c, 0xff, 0x35, 0x8a, 0xee,
0x3a, 0xac, 0x39, 0xed, 0x40, 0x7b, 0x32, 0x3f, 0x0a, 0xd8, 0xa7, 0x94, 0x74, 0xe1, 0x7e, 0xef,
0xdb, 0x29, 0x9b, 0xcf, 0x9a, 0x38, 0x2a, 0xbb, 0x69, 0x26, 0xa0, 0xec, 0x7e, 0xd6, 0xc4, 0xb5,
0xdb, 0x50, 0x63, 0xfa, 0xb3, 0x26, 0x01, 0xdc, 0xfa, 0xac, 0x89, 0xc7, 0xb3, 0xf7, 0xb3, 0x26,
0x5e, 0x6b, 0xc1, 0xcf, 0x9a, 0x84, 0x35, 0xa8, 0xc9, 0xa7, 0x2d, 0x42, 0xb3, 0xab, 0x3e, 0xc8,
0xa2, 0xbb, 0xc9, 0xfe, 0xe4, 0x3a, 0x2a, 0xc4, 0xf4, 0xdb, 0x70, 0xf2, 0x8e, 0xda, 0x80, 0x67,
0xea, 0xdc, 0x53, 0xdb, 0x18, 0xcc, 0x2b, 0xdf, 0x3f, 0x51, 0x6b, 0x2f, 0x3d, 0xd9, 0xf0, 0x52,
0x7e, 0xd2, 0x66, 0x2d, 0x34, 0x79, 0xd4, 0x16, 0xec, 0x96, 0x7f, 0x3c, 0x0c, 0x26, 0xaa, 0x5b,
0x13, 0xaa, 0xd1, 0xc7, 0x7d, 0x86, 0x50, 0x93, 0x6f, 0x0c, 0xe6, 0x89, 0x49, 0xae, 0xf1, 0xdd,
0xb4, 0xf6, 0x00, 0x63, 0x6e, 0x5b, 0x6f, 0x0e, 0x57, 0x50, 0xee, 0x97, 0x2a, 0xa9, 0xb5, 0xdd,
0xcb, 0x76, 0x5e, 0xef, 0x33, 0x35, 0x71, 0x9a, 0x79, 0x3c, 0x14, 0x0f, 0xa5, 0x37, 0xf6, 0x04,
0xdf, 0x97, 0xde, 0x78, 0x27, 0xf9, 0x4f, 0xae, 0xa7, 0xa4, 0xca, 0xf2, 0xcf, 0xa3, 0xe8, 0x26,
0x59, 0x16, 0x15, 0x07, 0x9f, 0x0d, 0xb5, 0x8c, 0xe2, 0xe1, 0xf3, 0x6b, 0xeb, 0xa9, 0x42, 0xfd,
0xdb, 0x28, 0xba, 0x15, 0x28, 0x54, 0x13, 0x20, 0xd7, 0xb0, 0xee, 0x06, 0xca, 0x0f, 0xaf, 0xaf,
0x48, 0x4d, 0xf7, 0x36, 0x3e, 0xe9, 0x7e, 0xef, 0x23, 0x60, 0x7b, 0x42, 0x7f, 0xef, 0xa3, 0x5f,
0x0b, 0x6f, 0x41, 0xd5, 0x49, 0x89, 0x5a, 0x19, 0xf9, 0xb6, 0xa0, 0x64, 0xce, 0x82, 0x56, 0x44,
0xf7, 0x7b, 0x39, 0x9f, 0x93, 0xe7, 0x6f, 0x0a, 0x96, 0x4f, 0x69, 0x27, 0x8d, 0xbc, 0xdf, 0x89,
0xe6, 0xf0, 0xd6, 0x5d, 0x2d, 0x3d, 0xe1, 0xed, 0x32, 0xef, 0x21, 0xa5, 0xaf, 0x91, 0xe0, 0xd6,
0x5d, 0x07, 0x25, 0xbc, 0xa9, 0x9c, 0x36, 0xe4, 0x0d, 0xa5, 0xb2, 0x8f, 0x86, 0xa0, 0x68, 0x01,
0xa1, 0xbd, 0xe9, 0x13, 0x81, 0xc7, 0x21, 0x2b, 0x9d, 0x53, 0x81, 0xf5, 0x81, 0x34, 0xe1, 0x76,
0x02, 0xe2, 0x4b, 0x60, 0x53, 0x28, 0x83, 0x6e, 0x35, 0x35, 0xc8, 0xad, 0x4d, 0xfb, 0xdc, 0x6e,
0xf3, 0x6c, 0x31, 0xcf, 0x55, 0x63, 0x92, 0x6e, 0x6d, 0xaa, 0xdf, 0x2d, 0xa2, 0xf1, 0xa6, 0xa5,
0x71, 0x2b, 0xd3, 0xcb, 0x47, 0x61, 0x33, 0x4e, 0x56, 0xb9, 0x36, 0x88, 0xa5, 0xeb, 0xa9, 0xc2,
0xa8, 0xa7, 0x9e, 0x28, 0x92, 0xd6, 0x07, 0xd2, 0x78, 0xf7, 0xd0, 0x72, 0xab, 0xe3, 0x69, 0xa3,
0xc7, 0x56, 0x27, 0xa4, 0x36, 0x87, 0x2b, 0xe0, 0xbd, 0x5a, 0x15, 0x55, 0xf5, 0xba, 0x68, 0x37,
0xcd, 0xb2, 0x78, 0x2d, 0x10, 0x26, 0x2d, 0x14, 0xdc, 0xab, 0xf5, 0xc0, 0x44, 0x24, 0xb7, 0x7b,
0x9b, 0x79, 0xdc, 0x67, 0x47, 0x52, 0x83, 0x22, 0xd9, 0xa6, 0xd1, 0x7e, 0x9b, 0xf5, 0xa8, 0x75,
0x6d, 0xc7, 0xe1, 0x07, 0xd7, 0xa9, 0xf0, 0xc6, 0x60, 0x1e, 0x5d, 0x06, 0x90, 0x94, 0x9c, 0x59,
0xee, 0x50, 0x26, 0x9c, 0x99, 0xe4, 0x6e, 0x0f, 0x85, 0xf6, 0x2c, 0x9b, 0x6e, 0xf4, 0x2a, 0x9d,
0xce, 0x40, 0x78, 0xcf, 0xb1, 0x6c, 0x20, 0x78, 0x8e, 0x85, 0x40, 0xd4, 0x74, 0xcd, 0xef, 0x7a,
0xb3, 0x76, 0x7f, 0xea, 0x6b, 0x3a, 0xa5, 0x6c, 0x51, 0xa1, 0xa6, 0xf3, 0xd2, 0x68, 0x34, 0xd0,
0x6e, 0xd5, 0xcb, 0xe4, 0x8f, 0x42, 0x66, 0xd0, 0x1b, 0xe5, 0x6b, 0x83, 0x58, 0x34, 0xa3, 0x18,
0x87, 0xe9, 0x3c, 0x15, 0xbe, 0x19, 0xc5, 0xb2, 0x51, 0x23, 0xa1, 0x19, 0xa5, 0x8b, 0x52, 0xd5,
0xab, 0x73, 0x84, 0xfd, 0x69, 0xb8, 0x7a, 0x0d, 0x33, 0xac, 0x7a, 0x9a, 0xed, 0x1c, 0xbb, 0xe6,
0x3a, 0x64, 0xc4, 0x85, 0x5a, 0x2c, 0x7b, 0x62, 0x5b, 0xbe, 0xee, 0x88, 0xc1, 0xd0, 0xa8, 0x43,
0x29, 0xe0, 0xe3, 0x84, 0x9a, 0x6b, 0x4f, 0x86, 0x8b, 0x02, 0x58, 0xc9, 0xf2, 0xc4, 0xbb, 0x38,
0x95, 0x06, 0x3b, 0x64, 0x68, 0x71, 0x4a, 0x6a, 0xa0, 0x43, 0x7d, 0xf7, 0x45, 0x45, 0x4f, 0x57,
0xd0, 0x6f, 0x04, 0xba, 0xef, 0x29, 0x3e, 0x1c, 0x40, 0xe2, 0x43, 0xfd, 0x16, 0xd0, 0xdb, 0xf2,
0x8d, 0xd3, 0x8f, 0x03, 0xa6, 0x5c, 0x34, 0xb4, 0x10, 0xa6, 0x55, 0x50, 0x50, 0xeb, 0x04, 0x17,
0xc4, 0x8f, 0x61, 0xe5, 0x0b, 0x6a, 0x93, 0x9f, 0x4a, 0x24, 0x14, 0xd4, 0x5d, 0x14, 0xe5, 0x99,
0xf6, 0x3a, 0xe8, 0x5e, 0x40, 0xdf, 0x5e, 0xfa, 0xdc, 0xef, 0xe5, 0x50, 0xcf, 0xd9, 0x49, 0x97,
0xce, 0x29, 0x86, 0xa7, 0xa0, 0x3b, 0xe9, 0xd2, 0x7f, 0x88, 0xb1, 0x36, 0x88, 0xc5, 0x17, 0x06,
0x98, 0x80, 0x37, 0xed, 0x49, 0xbe, 0xa7, 0xb8, 0x52, 0xde, 0x39, 0xca, 0x7f, 0xd0, 0x0f, 0x9a,
0xeb, 0xb9, 0xc7, 0x25, 0x4f, 0xa0, 0xaa, 0xd4, 0x47, 0xd0, 0xdc, 0xfb, 0x4f, 0x4a, 0x36, 0x46,
0x9f, 0x40, 0xbb, 0x13, 0x86, 0x94, 0xed, 0x2f, 0xa3, 0xb7, 0x0f, 0xf8, 0x6c, 0x02, 0xf9, 0x34,
0xfe, 0x81, 0x7b, 0x21, 0x96, 0xcf, 0xc6, 0xf5, 0xcf, 0xda, 0xde, 0x0d, 0x4a, 0x6c, 0xae, 0xf4,
0xed, 0xc0, 0xf9, 0x62, 0x36, 0x11, 0x4c, 0xa0, 0x2b, 0x7d, 0xf2, 0xf7, 0x71, 0x2d, 0x20, 0xae,
0xf4, 0x39, 0x00, 0xb2, 0x77, 0x5a, 0x02, 0x78, 0xed, 0xd5, 0x82, 0xa0, 0x3d, 0x05, 0x98, 0x59,
0x57, 0xdb, 0xab, 0x13, 0x5b, 0x7c, 0x05, 0xcf, 0xe8, 0x48, 0x29, 0x31, 0xeb, 0x76, 0x29, 0x13,
0x0c, 0x4d, 0xf5, 0xe5, 0x37, 0x26, 0x16, 0xf3, 0x39, 0x2b, 0x57, 0x28, 0x18, 0x54, 0x2d, 0x2d,
0x80, 0x08, 0x06, 0x2f, 0x68, 0xa2, 0xbc, 0x7d, 0xcc, 0xc9, 0xe5, 0x1e, 0x2f, 0xf9, 0x42, 0xa4,
0x39, 0xe0, 0xef, 0x0c, 0xe8, 0x07, 0x6a, 0x33, 0x44, 0x94, 0x53, 0xac, 0xc9, 0x0a, 0x25, 0xd1,
0xdc, 0x0e, 0x94, 0x9f, 0x12, 0xad, 0x04, 0x2f, 0xf1, 0xe9, 0x60, 0x63, 0x05, 0x43, 0x44, 0x56,
0x48, 0xc2, 0xa8, 0xed, 0x8f, 0xd3, 0x7c, 0xe6, 0x6d, 0xfb, 0x63, 0xfb, 0x43, 0x7c, 0xb7, 0x68,
0xc0, 0x8c, 0xef, 0xcd, 0x43, 0x6b, 0xbe, 0xe5, 0xa3, 0xde, 0x5b, 0xf4, 0x3e, 0x74, 0x9b, 0x20,
0xc6, 0x77, 0x3f, 0x89, 0x5c, 0xbd, 0x28, 0x20, 0x87, 0x69, 0x7b, 0x07, 0xce, 0xe7, 0xca, 0x21,
0x82, 0xae, 0x30, 0x69, 0x42, 0xe1, 0x10, 0x44, 0x99, 0x26, 0xd5, 0x04, 0xc4, 0x31, 0x2b, 0xd9,
0x1c, 0x04, 0x94, 0x38, 0x14, 0x14, 0x32, 0x76, 0x18, 0x22, 0x14, 0x28, 0x56, 0x39, 0xfc, 0xfd,
0xe8, 0xdd, 0x7a, 0x24, 0x84, 0x5c, 0x7d, 0xdb, 0xfc, 0xb9, 0xfc, 0xa3, 0x08, 0xf1, 0x7b, 0xda,
0xc6, 0x44, 0x94, 0xc0, 0xe6, 0xad, 0xed, 0x77, 0xf4, 0xef, 0x12, 0xdc, 0x1c, 0xd5, 0xf3, 0xc0,
0x11, 0x17, 0xe9, 0xeb, 0x7a, 0xe1, 0xa1, 0x8e, 0x79, 0xd0, 0x3c, 0x60, 0x8b, 0xc7, 0x81, 0xb7,
0xdc, 0x7d, 0x9c, 0xe9, 0x89, 0xb6, 0xf4, 0x04, 0x8a, 0x0c, 0xf7, 0x44, 0x47, 0x5b, 0x02, 0x44,
0x4f, 0xf4, 0x82, 0x66, 0x52, 0xb3, 0xc5, 0xa7, 0x10, 0xae, 0xcc, 0x29, 0x0c, 0xab, 0xcc, 0xa9,
0x73, 0x89, 0x3e, 0x8b, 0xde, 0x3d, 0x84, 0xf9, 0x39, 0x94, 0xd5, 0x45, 0x5a, 0xec, 0xd5, 0x53,
0x10, 0x13, 0x8b, 0x0a, 0xcd, 0xd3, 0x86, 0x18, 0x6b, 0x84, 0x98, 0xa7, 0x09, 0xd4, 0xf4, 0x75,
0x03, 0xec, 0x57, 0x47, 0x6c, 0x0e, 0xf2, 0x9d, 0xfd, 0x78, 0x8d, 0x32, 0x62, 0x41, 0x44, 0x5f,
0x27, 0x61, 0xeb, 0x7d, 0x1c, 0xc3, 0x9c, 0xc0, 0xac, 0x8e, 0xb0, 0xf2, 0x98, 0xad, 0xe6, 0x90,
0x0b, 0x65, 0x12, 0xed, 0x52, 0x5a, 0x26, 0xfd, 0x3c, 0xb1, 0x4b, 0x39, 0x44, 0xcf, 0xca, 0x4a,
0x9d, 0x07, 0x7f, 0xcc, 0x4b, 0xd1, 0xfc, 0xe5, 0x82, 0xb3, 0x32, 0x43, 0x59, 0xa9, 0xfb, 0x50,
0x1d, 0x92, 0xc8, 0x4a, 0xc3, 0x1a, 0xd6, 0x27, 0x7f, 0x9d, 0x32, 0xbc, 0x84, 0x52, 0xc7, 0xc9,
0xf3, 0x39, 0x4b, 0x33, 0x15, 0x0d, 0x5f, 0x04, 0x6c, 0x13, 0x3a, 0xc4, 0x27, 0x7f, 0x87, 0xea,
0x5a, 0x1f, 0x49, 0x0e, 0x97, 0x10, 0x6d, 0x9a, 0xf6, 0xd8, 0x27, 0x36, 0x4d, 0xfb, 0xb5, 0xcc,
0x5a, 0xc6, 0xb0, 0x92, 0x5b, 0x49, 0x62, 0x9b, 0x4f, 0xf1, 0x0e, 0x8a, 0x65, 0x13, 0x81, 0xc4,
0x5a, 0x26, 0xa8, 0x60, 0x06, 0x7f, 0x83, 0xed, 0xa6, 0x39, 0xcb, 0xd2, 0x9f, 0xe2, 0xcb, 0xc1,
0x96, 0x9d, 0x96, 0x20, 0x06, 0x7f, 0x3f, 0xe9, 0x73, 0xb5, 0x07, 0xe2, 0x34, 0xad, 0x87, 0xfe,
0x07, 0x81, 0xe7, 0x26, 0x89, 0x7e, 0x57, 0x16, 0xa9, 0x5c, 0xfd, 0x6c, 0x14, 0xdd, 0xc4, 0x8f,
0x75, 0xab, 0x28, 0x26, 0xf5, 0x9c, 0x7d, 0x02, 0x09, 0xa4, 0x85, 0x88, 0x3f, 0x0d, 0x3f, 0x2b,
0x84, 0x13, 0x47, 0xcf, 0x03, 0xd4, 0xac, 0x03, 0xcd, 0x7a, 0x2c, 0x99, 0x34, 0x7f, 0xd2, 0xe7,
0xac, 0x82, 0x52, 0x7d, 0xad, 0x7a, 0x0f, 0x04, 0xea, 0x9d, 0x16, 0x37, 0xb6, 0xc0, 0xba, 0xa2,
0x44, 0xef, 0x0c, 0x6b, 0x98, 0xed, 0x0f, 0x8b, 0x3b, 0x81, 0x8a, 0x67, 0x4b, 0x90, 0xf7, 0xc3,
0x1e, 0x93, 0xc6, 0x2c, 0x8a, 0xd8, 0xfe, 0xa0, 0x69, 0x73, 0xb9, 0xb1, 0xeb, 0x76, 0x2b, 0x5f,
0xed, 0xe3, 0x43, 0x64, 0x8f, 0x25, 0x89, 0x11, 0x27, 0x5b, 0x01, 0xdc, 0xda, 0x1e, 0x2c, 0x39,
0x9b, 0x26, 0xac, 0x12, 0xc7, 0x6c, 0x95, 0x71, 0x36, 0x95, 0xf3, 0x3a, 0xde, 0x1e, 0x6c, 0x99,
0xb1, 0x0d, 0x51, 0xdb, 0x83, 0x14, 0xdc, 0xf8, 0x7c, 0xf6, 0xe1, 0x7f, 0x7f, 0x7d, 0x63, 0xf4,
0xf3, 0xaf, 0x6f, 0x8c, 0xfe, 0xef, 0xeb, 0x1b, 0xa3, 0x9f, 0x7d, 0x73, 0xe3, 0xad, 0x9f, 0x7f,
0x73, 0xe3, 0xad, 0xff, 0xfd, 0xe6, 0xc6, 0x5b, 0x5f, 0xbd, 0xad, 0xfe, 0x9a, 0xd3, 0xf9, 0x2f,
0xc9, 0xbf, 0xc9, 0xf4, 0xf4, 0x17, 0x01, 0x00, 0x00, 0xff, 0xff, 0x82, 0x93, 0xb3, 0x87, 0xf1,
0x69, 0x00, 0x00,
// 4805 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x9d, 0xcd, 0x6f, 0x24, 0x49,
0x56, 0xc0, 0xa7, 0x2e, 0x0c, 0xe4, 0xb2, 0x03, 0xd4, 0xc0, 0x30, 0x3b, 0xec, 0x76, 0xf7, 0xf4,
0x74, 0xdb, 0xee, 0x76, 0xbb, 0xec, 0xe9, 0x9e, 0x8f, 0x65, 0x16, 0x09, 0xb9, 0xed, 0xb6, 0xc7,
0xac, 0xed, 0x36, 0x2e, 0xbb, 0x5b, 0x1a, 0x09, 0x89, 0x70, 0xd6, 0x73, 0x39, 0x71, 0x56, 0x46,
0x6e, 0x66, 0x54, 0xb9, 0x6b, 0x11, 0x08, 0x04, 0x02, 0x81, 0x40, 0xac, 0xf8, 0xba, 0x70, 0x40,
0xe2, 0x1f, 0xe1, 0xca, 0x71, 0x0f, 0x1c, 0x38, 0xa2, 0x99, 0x7f, 0x64, 0x95, 0x91, 0x91, 0xf1,
0xf1, 0x32, 0x5e, 0x64, 0x7a, 0x4e, 0xdd, 0xaa, 0xf7, 0x7b, 0xef, 0x45, 0x64, 0x7c, 0xbd, 0x17,
0x11, 0x99, 0x8e, 0xee, 0xe6, 0x17, 0x9b, 0x79, 0xc1, 0x05, 0x2f, 0x37, 0x4b, 0x28, 0x16, 0x49,
0x0c, 0xcd, 0xbf, 0x23, 0xf9, 0xf3, 0xf0, 0x6d, 0x96, 0x2d, 0xc5, 0x32, 0x87, 0x0f, 0xde, 0x37,
0x64, 0xcc, 0x67, 0x33, 0x96, 0x4d, 0xca, 0x1a, 0xf9, 0xe0, 0x3d, 0x23, 0x81, 0x05, 0x64, 0x42,
0xfd, 0xfe, 0xf4, 0xbf, 0xff, 0x77, 0x10, 0xbd, 0xb3, 0x93, 0x26, 0x90, 0x89, 0x1d, 0xa5, 0x31,
0xfc, 0x2a, 0xfa, 0xee, 0x76, 0x9e, 0xef, 0x83, 0x78, 0x05, 0x45, 0x99, 0xf0, 0x6c, 0xf8, 0xd1,
0x48, 0x39, 0x18, 0x9d, 0xe6, 0xf1, 0x68, 0x3b, 0xcf, 0x47, 0x46, 0x38, 0x3a, 0x85, 0x9f, 0xcc,
0xa1, 0x14, 0x1f, 0x3c, 0x08, 0x43, 0x65, 0xce, 0xb3, 0x12, 0x86, 0x97, 0xd1, 0x6f, 0x6c, 0xe7,
0xf9, 0x18, 0xc4, 0x2e, 0x54, 0x15, 0x18, 0x0b, 0x26, 0x60, 0xb8, 0xda, 0x52, 0x75, 0x01, 0xed,
0x63, 0xad, 0x1b, 0x54, 0x7e, 0xce, 0xa2, 0xef, 0x54, 0x7e, 0xae, 0xe6, 0x62, 0xc2, 0x6f, 0xb2,
0xe1, 0x87, 0x6d, 0x45, 0x25, 0xd2, 0xb6, 0xef, 0x87, 0x10, 0x65, 0xf5, 0x75, 0xf4, 0xab, 0xaf,
0x59, 0x9a, 0x82, 0xd8, 0x29, 0xa0, 0x2a, 0xb8, 0xab, 0x53, 0x8b, 0x46, 0xb5, 0x4c, 0xdb, 0xfd,
0x28, 0xc8, 0x28, 0xc3, 0x5f, 0x45, 0xdf, 0xad, 0x25, 0xa7, 0x10, 0xf3, 0x05, 0x14, 0x43, 0xaf,
0x96, 0x12, 0x12, 0x8f, 0xbc, 0x05, 0x61, 0xdb, 0x3b, 0x3c, 0x5b, 0x40, 0x21, 0xfc, 0xb6, 0x95,
0x30, 0x6c, 0xdb, 0x40, 0xca, 0xf6, 0xdf, 0x0d, 0xa2, 0xef, 0x6f, 0xc7, 0x31, 0x9f, 0x67, 0xe2,
0x90, 0xc7, 0x2c, 0x3d, 0x4c, 0xb2, 0xeb, 0x63, 0xb8, 0xd9, 0xb9, 0xaa, 0xf8, 0x6c, 0x0a, 0xc3,
0x67, 0xee, 0x53, 0xad, 0xd1, 0x91, 0x66, 0x47, 0x36, 0xac, 0x7d, 0x7f, 0x72, 0x3b, 0x25, 0x55,
0x96, 0x7f, 0x1a, 0x44, 0x77, 0x70, 0x59, 0xc6, 0x3c, 0x5d, 0x80, 0x29, 0xcd, 0xa7, 0x1d, 0x86,
0x5d, 0x5c, 0x97, 0xe7, 0xb3, 0xdb, 0xaa, 0xa9, 0x12, 0xa5, 0xd1, 0xbb, 0x76, 0x77, 0x19, 0x43,
0x29, 0x87, 0xd3, 0x23, 0xba, 0x47, 0x28, 0x44, 0x7b, 0x7e, 0xdc, 0x07, 0x55, 0xde, 0x92, 0x68,
0xa8, 0xbc, 0xa5, 0xbc, 0xd4, 0xce, 0xd6, 0xbc, 0x16, 0x2c, 0x42, 0xfb, 0x7a, 0xd4, 0x83, 0x54,
0xae, 0xfe, 0x38, 0xfa, 0xb5, 0xd7, 0xbc, 0xb8, 0x2e, 0x73, 0x16, 0x83, 0x1a, 0x0a, 0x0f, 0x5d,
0xed, 0x46, 0x8a, 0x47, 0xc3, 0x4a, 0x17, 0x66, 0x75, 0xda, 0x46, 0xf8, 0x32, 0x07, 0x3c, 0x07,
0x19, 0xc5, 0x4a, 0x48, 0x75, 0x5a, 0x0c, 0x29, 0xdb, 0xd7, 0xd1, 0xd0, 0xd8, 0xbe, 0xf8, 0x13,
0x88, 0xc5, 0xf6, 0x64, 0x82, 0x5b, 0xc5, 0xe8, 0x4a, 0x62, 0xb4, 0x3d, 0x99, 0x50, 0xad, 0xe2,
0x47, 0x95, 0xb3, 0x9b, 0xe8, 0x3d, 0xe4, 0xec, 0x30, 0x29, 0xa5, 0xc3, 0x8d, 0xb0, 0x15, 0x85,
0x69, 0xa7, 0xa3, 0xbe, 0xb8, 0x72, 0xfc, 0x17, 0x83, 0xe8, 0x7b, 0x1e, 0xcf, 0xa7, 0x30, 0xe3,
0x0b, 0x18, 0x6e, 0x75, 0x5b, 0xab, 0x49, 0xed, 0xff, 0xe3, 0x5b, 0x68, 0x78, 0xba, 0xc9, 0x18,
0x52, 0x88, 0x05, 0xd9, 0x4d, 0x6a, 0x71, 0x67, 0x37, 0xd1, 0x98, 0x35, 0xc2, 0x1a, 0xe1, 0x3e,
0x88, 0x9d, 0x79, 0x51, 0x40, 0x26, 0xc8, 0xb6, 0x34, 0x48, 0x67, 0x5b, 0x3a, 0xa8, 0xa7, 0x3e,
0xfb, 0x20, 0xb6, 0xd3, 0x94, 0xac, 0x4f, 0x2d, 0xee, 0xac, 0x8f, 0xc6, 0x94, 0x87, 0x38, 0xfa,
0x75, 0xeb, 0x89, 0x89, 0x83, 0xec, 0x92, 0x0f, 0xe9, 0x67, 0x21, 0xe5, 0xda, 0xc7, 0x6a, 0x27,
0xe7, 0xa9, 0xc6, 0x8b, 0x37, 0x39, 0x2f, 0xe8, 0x66, 0xa9, 0xc5, 0x9d, 0xd5, 0xd0, 0x98, 0xf2,
0xf0, 0x47, 0xd1, 0x3b, 0x6a, 0x96, 0x6c, 0xd6, 0xb3, 0x07, 0xde, 0x29, 0x14, 0x2f, 0x68, 0x0f,
0x3b, 0x28, 0x33, 0x39, 0x28, 0x99, 0x9a, 0x7c, 0x3e, 0xf2, 0xea, 0xa1, 0xa9, 0xe7, 0x41, 0x18,
0x6a, 0xd9, 0xde, 0x85, 0x14, 0x48, 0xdb, 0xb5, 0xb0, 0xc3, 0xb6, 0x86, 0x94, 0xed, 0x22, 0xfa,
0x2d, 0xfd, 0x58, 0xaa, 0x75, 0x54, 0xca, 0xab, 0x49, 0x7a, 0x9d, 0xa8, 0xb7, 0x0d, 0x69, 0x5f,
0x4f, 0xfa, 0xc1, 0xad, 0xfa, 0xa8, 0x11, 0xe8, 0xaf, 0x0f, 0x1a, 0x7f, 0x0f, 0xc2, 0x90, 0xb2,
0xfd, 0xf7, 0x83, 0xe8, 0x07, 0x4a, 0xf6, 0x22, 0x63, 0x17, 0x29, 0xc8, 0x25, 0xf1, 0x18, 0xc4,
0x0d, 0x2f, 0xae, 0xc7, 0xcb, 0x2c, 0x26, 0x96, 0x7f, 0x3f, 0xdc, 0xb1, 0xfc, 0x93, 0x4a, 0x56,
0xc4, 0xa7, 0x2a, 0x2a, 0x78, 0x8e, 0x23, 0xbe, 0xa6, 0x06, 0x82, 0xe7, 0x54, 0xc4, 0xe7, 0x22,
0x2d, 0xab, 0x47, 0xd5, 0xb4, 0xe9, 0xb7, 0x7a, 0x64, 0xcf, 0x93, 0xf7, 0x43, 0x88, 0x99, 0xb6,
0x9a, 0x0e, 0xcc, 0xb3, 0xcb, 0x64, 0x7a, 0x9e, 0x4f, 0xaa, 0x6e, 0xfc, 0xc8, 0xdf, 0x43, 0x2d,
0x84, 0x98, 0xb6, 0x08, 0x54, 0x79, 0xfb, 0x47, 0x13, 0x18, 0xa9, 0xa1, 0xb4, 0x57, 0xf0, 0xd9,
0x21, 0x4c, 0x59, 0xbc, 0x54, 0xe3, 0xff, 0x93, 0xd0, 0xc0, 0xc3, 0xb4, 0x2e, 0xc4, 0xa7, 0xb7,
0xd4, 0x52, 0xe5, 0xf9, 0xcf, 0x41, 0xf4, 0xa0, 0xa9, 0xfe, 0x15, 0xcb, 0xa6, 0xa0, 0xda, 0xb3,
0x2e, 0xfd, 0x76, 0x36, 0x39, 0x85, 0x52, 0xb0, 0x42, 0x0c, 0xbf, 0xf0, 0x57, 0x32, 0xa4, 0xa3,
0xcb, 0xf6, 0xa3, 0x6f, 0xa5, 0x6b, 0x5a, 0x7d, 0x5c, 0x4d, 0x6c, 0x6a, 0x0a, 0x70, 0x5b, 0x5d,
0x4a, 0xf0, 0x04, 0x70, 0x3f, 0x84, 0x98, 0x56, 0x97, 0x82, 0x83, 0x6c, 0x91, 0x08, 0xd8, 0x87,
0x0c, 0x8a, 0x76, 0xab, 0xd7, 0xaa, 0x2e, 0x42, 0xb4, 0x3a, 0x81, 0x9a, 0xc9, 0xc6, 0xf1, 0xa6,
0x17, 0xc7, 0xf5, 0x80, 0x91, 0xd6, 0xf2, 0xf8, 0xa4, 0x1f, 0x6c, 0xb2, 0x3b, 0xcb, 0xe7, 0x29,
0x2c, 0xf8, 0x35, 0xce, 0xee, 0x6c, 0x13, 0x35, 0x40, 0x64, 0x77, 0x5e, 0xd0, 0xac, 0x60, 0x96,
0x9f, 0x57, 0x09, 0xdc, 0xa0, 0x15, 0xcc, 0x56, 0xae, 0xc4, 0xc4, 0x0a, 0xe6, 0xc1, 0x94, 0x87,
0xe3, 0xe8, 0x57, 0xa4, 0xf0, 0x0f, 0x78, 0x92, 0x0d, 0xef, 0x7a, 0x94, 0x2a, 0x81, 0xb6, 0x7a,
0x8f, 0x06, 0x50, 0x89, 0xab, 0x5f, 0x77, 0x58, 0x16, 0x43, 0xea, 0x2d, 0xb1, 0x11, 0x07, 0x4b,
0xec, 0x60, 0x26, 0x74, 0x90, 0xc2, 0x6a, 0xfe, 0x1a, 0x5f, 0xb1, 0x22, 0xc9, 0xa6, 0x43, 0x9f,
0xae, 0x25, 0x27, 0x42, 0x07, 0x1f, 0x87, 0xba, 0xb0, 0x52, 0xdc, 0xce, 0xf3, 0xa2, 0x9a, 0x16,
0x7d, 0x5d, 0xd8, 0x45, 0x82, 0x5d, 0xb8, 0x85, 0xfa, 0xbd, 0xed, 0x42, 0x9c, 0x26, 0x59, 0xd0,
0x9b, 0x42, 0xfa, 0x78, 0x33, 0x28, 0xea, 0xbc, 0x87, 0xc0, 0x16, 0xd0, 0xd4, 0xcc, 0xf7, 0x64,
0x6c, 0x20, 0xd8, 0x79, 0x11, 0x68, 0xf2, 0x34, 0x29, 0x3e, 0x62, 0xd7, 0x50, 0x3d, 0x60, 0xa8,
0xd6, 0xb5, 0xa1, 0x4f, 0xdf, 0x21, 0x88, 0x3c, 0xcd, 0x4f, 0x2a, 0x57, 0xf3, 0xe8, 0x3d, 0x29,
0x3f, 0x61, 0x85, 0x48, 0xe2, 0x24, 0x67, 0x59, 0x13, 0xff, 0xfb, 0xc6, 0x75, 0x8b, 0xd2, 0x2e,
0x37, 0x7a, 0xd2, 0xca, 0xed, 0xbf, 0x0f, 0xa2, 0x0f, 0xb1, 0xdf, 0x13, 0x28, 0x66, 0x89, 0x4c,
0x23, 0xcb, 0x7a, 0x12, 0x1e, 0x7e, 0x1e, 0x36, 0xda, 0x52, 0xd0, 0xa5, 0xf9, 0xe1, 0xed, 0x15,
0x55, 0xc1, 0xfe, 0x30, 0x8a, 0xea, 0x74, 0x45, 0xa6, 0x94, 0xee, 0xa8, 0x55, 0x79, 0x8c, 0x93,
0x4f, 0x7e, 0x18, 0x20, 0xcc, 0x52, 0x51, 0xff, 0x2e, 0x33, 0xe5, 0xa1, 0x57, 0x43, 0x8a, 0x88,
0xa5, 0x02, 0x21, 0xb8, 0xa0, 0xe3, 0x2b, 0x7e, 0xe3, 0x2f, 0x68, 0x25, 0x09, 0x17, 0x54, 0x11,
0x66, 0xef, 0x4a, 0x15, 0xd4, 0xb7, 0x77, 0xd5, 0x14, 0x23, 0xb4, 0x77, 0x85, 0x19, 0x65, 0x98,
0x47, 0xbf, 0x69, 0x1b, 0x7e, 0xce, 0xf9, 0xf5, 0x8c, 0x15, 0xd7, 0xc3, 0xc7, 0xb4, 0x72, 0xc3,
0x68, 0x47, 0xeb, 0xbd, 0x58, 0x33, 0x2d, 0xd8, 0x0e, 0xab, 0x40, 0xe3, 0xbc, 0x48, 0xd1, 0xb4,
0xe0, 0xd8, 0x50, 0x08, 0x31, 0x2d, 0x10, 0xa8, 0x99, 0xb9, 0x6d, 0x6f, 0x63, 0xc0, 0xd9, 0x92,
0xa3, 0x3e, 0x06, 0x2a, 0x5b, 0xf2, 0x60, 0xb8, 0x0b, 0xed, 0x17, 0x2c, 0xbf, 0xf2, 0x77, 0x21,
0x29, 0x0a, 0x77, 0xa1, 0x06, 0xc1, 0xed, 0x3d, 0x06, 0x56, 0xc4, 0x57, 0xfe, 0xf6, 0xae, 0x65,
0xe1, 0xf6, 0xd6, 0x0c, 0x6e, 0xef, 0x5a, 0xf0, 0x3a, 0x11, 0x57, 0x47, 0x20, 0x98, 0xbf, 0xbd,
0x5d, 0x26, 0xdc, 0xde, 0x2d, 0xd6, 0x44, 0x32, 0xb6, 0xc3, 0xf1, 0xfc, 0xa2, 0x8c, 0x8b, 0xe4,
0x02, 0x86, 0x01, 0x2b, 0x1a, 0x22, 0x22, 0x19, 0x12, 0x36, 0x93, 0xb4, 0xf2, 0xd9, 0xc8, 0x0e,
0x26, 0x25, 0x9a, 0xa4, 0x1b, 0x1b, 0x16, 0x41, 0x4c, 0xd2, 0x7e, 0x12, 0x57, 0x6f, 0xbf, 0xe0,
0xf3, 0xbc, 0xec, 0xa8, 0x1e, 0x82, 0xc2, 0xd5, 0x6b, 0xc3, 0xca, 0xe7, 0x9b, 0xe8, 0xb7, 0xed,
0x47, 0x7a, 0x9e, 0x95, 0xda, 0xeb, 0x06, 0xfd, 0x9c, 0x2c, 0x8c, 0xd8, 0x96, 0x0a, 0xe0, 0x26,
0x4c, 0x69, 0x3c, 0x8b, 0x5d, 0x10, 0x2c, 0x49, 0xcb, 0xe1, 0x8a, 0xdf, 0x46, 0x23, 0x27, 0xc2,
0x14, 0x1f, 0x87, 0xc7, 0xec, 0xee, 0x3c, 0x4f, 0x93, 0xb8, 0xbd, 0x3f, 0xa9, 0x74, 0xb5, 0x38,
0x3c, 0x66, 0x6d, 0x0c, 0xcf, 0x41, 0x63, 0x10, 0xf5, 0x7f, 0xce, 0x96, 0x39, 0xf8, 0xe7, 0x20,
0x07, 0x09, 0xcf, 0x41, 0x18, 0xc5, 0xf5, 0x19, 0x83, 0x38, 0x64, 0x4b, 0x3e, 0x27, 0xe6, 0x20,
0x2d, 0x0e, 0xd7, 0xc7, 0xc6, 0x4c, 0xa4, 0xa0, 0x3d, 0x1c, 0x64, 0x02, 0x8a, 0x8c, 0xa5, 0x7b,
0x29, 0x9b, 0x96, 0x43, 0x62, 0xdc, 0xb8, 0x14, 0x11, 0x29, 0xd0, 0xb4, 0xe7, 0x31, 0x1e, 0x94,
0x7b, 0x6c, 0xc1, 0x8b, 0x44, 0xd0, 0x8f, 0xd1, 0x20, 0x9d, 0x8f, 0xd1, 0x41, 0xbd, 0xde, 0xb6,
0x8b, 0xf8, 0x2a, 0x59, 0xc0, 0x24, 0xe0, 0xad, 0x41, 0x7a, 0x78, 0xb3, 0x50, 0x4f, 0xa3, 0x8d,
0xf9, 0xbc, 0x88, 0x81, 0x6c, 0xb4, 0x5a, 0xdc, 0xd9, 0x68, 0x1a, 0x53, 0x1e, 0xfe, 0x7a, 0x10,
0xfd, 0x4e, 0x2d, 0xb5, 0x37, 0x0d, 0x77, 0x59, 0x79, 0x75, 0xc1, 0x59, 0x31, 0x19, 0x7e, 0xec,
0xb3, 0xe3, 0x45, 0xb5, 0xeb, 0xa7, 0xb7, 0x51, 0xc1, 0x8f, 0xf5, 0x30, 0x29, 0xad, 0x11, 0xe7,
0x7d, 0xac, 0x0e, 0x12, 0x7e, 0xac, 0x18, 0xc5, 0x13, 0x88, 0x94, 0xd7, 0x09, 0xfa, 0x0a, 0xa9,
0xef, 0x66, 0xe9, 0xab, 0x9d, 0x1c, 0x9e, 0x1f, 0x2b, 0xa1, 0xdb, 0x5b, 0x36, 0x28, 0x1b, 0xfe,
0x1e, 0x33, 0xea, 0x8b, 0x93, 0x9e, 0xf5, 0xa8, 0x08, 0x7b, 0x6e, 0x8d, 0x8c, 0x51, 0x5f, 0x9c,
0xf0, 0x6c, 0x4d, 0x6b, 0x21, 0xcf, 0x9e, 0xa9, 0x6d, 0xd4, 0x17, 0xc7, 0x11, 0x85, 0x62, 0x9a,
0x75, 0xe1, 0x71, 0xc0, 0x0e, 0x5e, 0x1b, 0xd6, 0x7b, 0xb1, 0xb8, 0xc7, 0x6e, 0xe7, 0x79, 0xba,
0x3c, 0x83, 0x59, 0x9e, 0x92, 0x3d, 0xd6, 0x41, 0xc2, 0x3d, 0x16, 0xa3, 0x38, 0xbe, 0x3b, 0xe3,
0x55, 0xf4, 0xe8, 0x8d, 0xef, 0xa4, 0x28, 0x1c, 0xdf, 0x35, 0x08, 0x8e, 0x50, 0xce, 0xf8, 0x0e,
0x4f, 0x53, 0x88, 0x45, 0xfb, 0xb8, 0x4f, 0x6b, 0x1a, 0x22, 0x1c, 0xa1, 0x20, 0xd2, 0x64, 0xc6,
0x4d, 0x36, 0xc2, 0x0a, 0x78, 0xbe, 0x3c, 0x4c, 0xb2, 0xeb, 0xa1, 0x7f, 0x31, 0x36, 0x00, 0x91,
0x19, 0x7b, 0x41, 0x9c, 0xf5, 0x9c, 0x67, 0x13, 0xee, 0xcf, 0x7a, 0x2a, 0x49, 0x38, 0xeb, 0x51,
0x04, 0x36, 0x79, 0x0a, 0x94, 0xc9, 0x4a, 0x12, 0x36, 0xa9, 0x08, 0xdf, 0x04, 0xa4, 0xf6, 0x4f,
0xc9, 0x09, 0x08, 0xed, 0x98, 0xae, 0x76, 0x72, 0xb8, 0x87, 0x36, 0xe9, 0xcf, 0x1e, 0x88, 0xf8,
0xca, 0xdf, 0x43, 0x1d, 0x24, 0xdc, 0x43, 0x31, 0x8a, 0xab, 0x74, 0xc6, 0x75, 0xfa, 0xb6, 0xe2,
0xef, 0x1f, 0xad, 0xd4, 0x6d, 0xb5, 0x93, 0xc3, 0x09, 0xc9, 0xc1, 0x4c, 0x3e, 0x33, 0x6f, 0x27,
0xaf, 0x65, 0xe1, 0x84, 0x44, 0x33, 0xb8, 0xf4, 0xb5, 0xa0, 0x7a, 0x9c, 0xfe, 0xd2, 0x1b, 0x79,
0xb8, 0xf4, 0x0e, 0xa7, 0x9c, 0xfc, 0xeb, 0x20, 0xba, 0x6b, 0x7b, 0x39, 0xe6, 0xd5, 0x18, 0x79,
0xc5, 0xd2, 0x64, 0xc2, 0x04, 0x9c, 0xf1, 0x6b, 0xc8, 0xd0, 0x8e, 0x86, 0x5b, 0xda, 0x9a, 0x1f,
0x39, 0x0a, 0xc4, 0x8e, 0x46, 0x2f, 0x45, 0xdc, 0x4f, 0x6a, 0xfa, 0xbc, 0x84, 0x1d, 0x56, 0x12,
0x33, 0x99, 0x83, 0x84, 0xfb, 0x09, 0x46, 0x71, 0x94, 0x58, 0xcb, 0x5f, 0xbc, 0xc9, 0xa1, 0x48,
0x20, 0x8b, 0xc1, 0x1f, 0x25, 0x62, 0x2a, 0x1c, 0x25, 0x7a, 0xe8, 0x56, 0xc2, 0xaf, 0x27, 0xa7,
0xf6, 0x89, 0x3d, 0x26, 0x02, 0x27, 0xf6, 0x04, 0x8a, 0x2b, 0x69, 0x00, 0xef, 0xa6, 0x59, 0xcb,
0x4a, 0x70, 0xd3, 0x8c, 0xa6, 0x5b, 0xdb, 0x28, 0x9a, 0x19, 0x57, 0xc3, 0xa4, 0xa3, 0xe8, 0x63,
0x7b, 0xb8, 0xac, 0xf7, 0x62, 0xfd, 0xfb, 0x36, 0xa7, 0x90, 0x32, 0xb9, 0x84, 0x04, 0x36, 0x47,
0x1a, 0xa6, 0xcf, 0xbe, 0x8d, 0xc5, 0x2a, 0x87, 0x7f, 0x39, 0x88, 0x3e, 0xf0, 0x79, 0x7c, 0x99,
0x4b, 0xbf, 0x5b, 0xdd, 0xb6, 0x6a, 0x92, 0xb8, 0x92, 0x10, 0xd6, 0x50, 0x65, 0xf8, 0xd3, 0xe8,
0xfd, 0x46, 0x64, 0x6e, 0x2c, 0xa8, 0x02, 0xb8, 0x61, 0x8b, 0x2e, 0x3f, 0xe6, 0xb4, 0xfb, 0xcd,
0xde, 0xbc, 0xc9, 0x08, 0xdc, 0x72, 0x95, 0x28, 0x23, 0xd0, 0x36, 0x94, 0x98, 0xc8, 0x08, 0x3c,
0x18, 0x5e, 0xa9, 0x1b, 0xa4, 0x1a, 0x27, 0xbe, 0x39, 0x4e, 0x9b, 0xb0, 0x47, 0xc9, 0x5a, 0x37,
0x88, 0xfb, 0x4e, 0x23, 0x56, 0x81, 0xf8, 0xe3, 0x90, 0x05, 0x14, 0x8c, 0xaf, 0xf7, 0x62, 0x95,
0xc3, 0x3f, 0x8f, 0xbe, 0xd7, 0xaa, 0xd8, 0x1e, 0x30, 0x31, 0x2f, 0x60, 0x32, 0xdc, 0xec, 0x28,
0x77, 0x03, 0x6a, 0xd7, 0x5b, 0xfd, 0x15, 0x94, 0xff, 0xbf, 0x1d, 0x44, 0xdf, 0x77, 0xb9, 0xba,
0x89, 0x75, 0x19, 0x9e, 0x86, 0x4c, 0xba, 0xac, 0x2e, 0xc6, 0xb3, 0x5b, 0xe9, 0xb4, 0x92, 0x3e,
0xbb, 0x23, 0x6f, 0x2f, 0x58, 0x92, 0xca, 0x83, 0x84, 0x8f, 0x43, 0x46, 0x1d, 0x34, 0x98, 0xf4,
0x91, 0x2a, 0xad, 0x59, 0x52, 0x8e, 0x37, 0x2b, 0x59, 0x78, 0x42, 0x8f, 0x4a, 0x4f, 0xae, 0xb0,
0xd1, 0x93, 0x56, 0x6e, 0x45, 0xb3, 0x59, 0x56, 0xfd, 0x6c, 0x77, 0x72, 0x9f, 0x57, 0xa5, 0xea,
0xe9, 0xe9, 0x1b, 0x3d, 0x69, 0xe5, 0xf5, 0xcf, 0xa2, 0xf7, 0xdb, 0x5e, 0xd5, 0xa2, 0xb0, 0xd9,
0x69, 0x0a, 0xad, 0x0b, 0x5b, 0xfd, 0x15, 0x4c, 0xa8, 0xff, 0x65, 0x52, 0x0a, 0x5e, 0x2c, 0xc7,
0x57, 0xfc, 0xa6, 0xb9, 0x95, 0xeb, 0x8e, 0x56, 0x05, 0x8c, 0x2c, 0x82, 0x08, 0xf5, 0xfd, 0x64,
0xcb, 0x95, 0xb9, 0xbd, 0x5b, 0x12, 0xae, 0x2c, 0xa2, 0xc3, 0x95, 0x4b, 0x9a, 0xb9, 0xaa, 0xa9,
0x95, 0xb9, 0x6a, 0xbc, 0xea, 0x2f, 0x6a, 0xfb, 0xba, 0xf1, 0x5a, 0x37, 0x68, 0xa2, 0x07, 0x25,
0xde, 0x4d, 0x2e, 0x2f, 0x75, 0x9d, 0xfc, 0x25, 0xb5, 0x11, 0x22, 0x7a, 0x20, 0x50, 0x93, 0xec,
0xed, 0x25, 0x29, 0xbc, 0xbc, 0xbc, 0x4c, 0x39, 0x9b, 0xa0, 0x64, 0xaf, 0x92, 0x8c, 0x94, 0x88,
0x48, 0xf6, 0x10, 0x62, 0x42, 0xdc, 0x4a, 0x20, 0xcf, 0xba, 0x1a, 0xd3, 0x2b, 0x6d, 0x3d, 0x5b,
0x4e, 0x84, 0xb8, 0x3e, 0xce, 0x2c, 0x4f, 0x95, 0xb4, 0x1a, 0xf0, 0x8d, 0x8f, 0x87, 0x6d, 0x5d,
0x4b, 0x4c, 0x2c, 0x4f, 0x1e, 0xcc, 0x64, 0x63, 0x95, 0xf0, 0x3c, 0x97, 0xc6, 0xef, 0xb5, 0xb5,
0x6a, 0x09, 0x91, 0x8d, 0xb9, 0x84, 0xc9, 0x2a, 0xaa, 0xdf, 0x77, 0xf9, 0x4d, 0x26, 0x8d, 0x7a,
0x9e, 0x66, 0x23, 0x23, 0xb2, 0x0a, 0xcc, 0x28, 0xc3, 0x3f, 0x8e, 0x7e, 0x59, 0x1a, 0x2e, 0x78,
0x3e, 0xbc, 0xe3, 0x51, 0x28, 0xac, 0x9b, 0x44, 0x77, 0x49, 0xb9, 0xb9, 0x10, 0xa7, 0xdb, 0xef,
0xbc, 0x64, 0x53, 0x40, 0x17, 0xe2, 0x4c, 0xab, 0x48, 0x29, 0x71, 0x21, 0xae, 0x4d, 0x99, 0x4b,
0x5e, 0x95, 0xec, 0x98, 0x4f, 0x94, 0x75, 0x4f, 0x0d, 0xb5, 0x90, 0xb8, 0xe4, 0xd5, 0x82, 0xcc,
0xf1, 0xc4, 0x31, 0x5b, 0x24, 0x53, 0xbd, 0xd4, 0xd4, 0x33, 0x56, 0x89, 0x8e, 0x27, 0x0c, 0x33,
0xb2, 0x20, 0xe2, 0x78, 0x82, 0x84, 0x95, 0xcf, 0x7f, 0x19, 0x44, 0xf7, 0x0c, 0xb3, 0xdf, 0xec,
0x1a, 0x1d, 0x64, 0x97, 0xfc, 0x75, 0x22, 0xae, 0x0e, 0x93, 0xec, 0xba, 0x1c, 0x7e, 0x46, 0x99,
0xf4, 0xf3, 0xba, 0x28, 0x9f, 0xdf, 0x5a, 0xcf, 0xc4, 0x8e, 0xcd, 0xe6, 0x8e, 0x39, 0x2b, 0xac,
0x35, 0x50, 0xec, 0xa8, 0xf7, 0x80, 0x30, 0x47, 0xc4, 0x8e, 0x21, 0xde, 0x34, 0xb1, 0x76, 0x9e,
0xf2, 0x0c, 0x37, 0xb1, 0xb1, 0x50, 0x09, 0x89, 0x26, 0x6e, 0x41, 0x66, 0x26, 0x6e, 0x44, 0xf5,
0x3e, 0xc4, 0x76, 0x9a, 0xa2, 0x99, 0x58, 0xab, 0x6a, 0x80, 0x98, 0x89, 0xbd, 0xa0, 0xf2, 0x73,
0x1a, 0x7d, 0xa7, 0x7a, 0xa4, 0x27, 0x05, 0x2c, 0x12, 0xc0, 0xc7, 0xda, 0x96, 0x84, 0x18, 0xff,
0x2e, 0x61, 0x46, 0xd6, 0x79, 0x56, 0xe6, 0x29, 0x2b, 0xaf, 0xd4, 0x41, 0xa7, 0x5b, 0xe7, 0x46,
0x88, 0x8f, 0x3a, 0x1f, 0x76, 0x50, 0x66, 0xe2, 0x6d, 0x64, 0x7a, 0x8a, 0x59, 0xf1, 0xab, 0xb6,
0xa6, 0x99, 0xd5, 0x4e, 0xce, 0xec, 0xbc, 0xee, 0xb3, 0x34, 0x85, 0x62, 0xd9, 0xc8, 0x8e, 0x58,
0x96, 0x5c, 0x42, 0x29, 0xd0, 0xce, 0xab, 0xa2, 0x46, 0x18, 0x23, 0x76, 0x5e, 0x03, 0xb8, 0x89,
0xe3, 0x91, 0xe7, 0x83, 0x6c, 0x02, 0x6f, 0x50, 0x1c, 0x8f, 0xed, 0x48, 0x86, 0x88, 0xe3, 0x29,
0xd6, 0x2c, 0x8f, 0xcf, 0x53, 0x1e, 0x5f, 0xab, 0x25, 0xc0, 0x6d, 0x60, 0x29, 0xc1, 0x6b, 0xc0,
0xfd, 0x10, 0x62, 0x16, 0x01, 0x29, 0x38, 0x85, 0x3c, 0x65, 0x31, 0xbe, 0xdb, 0x50, 0xeb, 0x28,
0x19, 0xb1, 0x08, 0x60, 0x06, 0x15, 0x57, 0xdd, 0x99, 0xf0, 0x15, 0x17, 0x5d, 0x99, 0xb8, 0x1f,
0x42, 0xcc, 0x32, 0x28, 0x05, 0xe3, 0x3c, 0x4d, 0x04, 0x1a, 0x06, 0xb5, 0x86, 0x94, 0x10, 0xc3,
0xc0, 0x25, 0x90, 0xc9, 0x23, 0x28, 0xa6, 0xe0, 0x35, 0x29, 0x25, 0x41, 0x93, 0x0d, 0x61, 0xae,
0xc0, 0xd5, 0x75, 0xe7, 0xf9, 0x12, 0x5d, 0x81, 0x53, 0xd5, 0xe2, 0xf9, 0x92, 0xb8, 0x02, 0xe7,
0x00, 0xa8, 0x88, 0x27, 0xac, 0x14, 0xfe, 0x22, 0x4a, 0x49, 0xb0, 0x88, 0x0d, 0x61, 0xd6, 0xe8,
0xba, 0x88, 0x73, 0x81, 0xd6, 0x68, 0x55, 0x00, 0xeb, 0x24, 0xf4, 0x2e, 0x29, 0x37, 0x33, 0x49,
0xdd, 0x2a, 0x20, 0xf6, 0x12, 0x48, 0x27, 0x25, 0x9a, 0x49, 0xd4, 0x73, 0x6f, 0xa4, 0xc4, 0x4c,
0xd2, 0xa6, 0x50, 0x57, 0x52, 0x3b, 0xc6, 0xbe, 0xda, 0xa1, 0xcd, 0xe2, 0xfb, 0x21, 0xc4, 0xcc,
0x4f, 0x4d, 0xa1, 0x77, 0x58, 0x51, 0x24, 0xd5, 0xe2, 0xbf, 0xe2, 0x2f, 0x50, 0x23, 0x27, 0xe6,
0x27, 0x1f, 0x87, 0x86, 0x57, 0x33, 0x71, 0xfb, 0x0a, 0x86, 0xa7, 0xee, 0x8f, 0x82, 0x8c, 0x89,
0x38, 0xa5, 0xc4, 0x3a, 0xca, 0xf3, 0x3d, 0x4d, 0xcf, 0x49, 0xde, 0x4a, 0x17, 0x66, 0x5d, 0x51,
0xd7, 0x2e, 0x8e, 0xf8, 0x02, 0xce, 0xf8, 0x8b, 0x37, 0x49, 0x29, 0x92, 0x6c, 0xaa, 0x56, 0xee,
0x67, 0x84, 0x25, 0x1f, 0x4c, 0x5c, 0x51, 0xef, 0x54, 0x32, 0x01, 0x04, 0x2a, 0xcb, 0x31, 0xdc,
0x78, 0x03, 0x08, 0x6c, 0x51, 0x73, 0x44, 0x00, 0x11, 0xe2, 0xcd, 0x0e, 0x8a, 0x76, 0xae, 0xde,
0xe3, 0x3b, 0xe3, 0x4d, 0x2c, 0x47, 0x59, 0xc3, 0x20, 0x91, 0xc4, 0x06, 0x15, 0x4c, 0x66, 0xa9,
0xfd, 0x9b, 0x21, 0xb6, 0x46, 0xd8, 0x69, 0x0f, 0xb3, 0x47, 0x3d, 0x48, 0x8f, 0x2b, 0x73, 0x1e,
0x4d, 0xb9, 0x6a, 0x1f, 0x47, 0x3f, 0xea, 0x41, 0x5a, 0xbb, 0x31, 0x76, 0xb5, 0x9e, 0xb3, 0xf8,
0x7a, 0x5a, 0xf0, 0x79, 0x36, 0xd9, 0xe1, 0x29, 0x2f, 0xd0, 0x6e, 0x8c, 0x53, 0x6a, 0x84, 0x12,
0xbb, 0x31, 0x1d, 0x2a, 0x26, 0x82, 0xb3, 0x4b, 0xb1, 0x9d, 0x26, 0x53, 0x9c, 0x4b, 0x3b, 0x86,
0x24, 0x40, 0x44, 0x70, 0x5e, 0xd0, 0xd3, 0x89, 0xea, 0x5c, 0x5b, 0x24, 0x31, 0x4b, 0x6b, 0x7f,
0x9b, 0xb4, 0x19, 0x07, 0xec, 0xec, 0x44, 0x1e, 0x05, 0x4f, 0x3d, 0xcf, 0xe6, 0x45, 0x76, 0x90,
0x09, 0x4e, 0xd6, 0xb3, 0x01, 0x3a, 0xeb, 0x69, 0x81, 0x68, 0x5a, 0x3d, 0x83, 0x37, 0x55, 0x69,
0xaa, 0x7f, 0x7c, 0xd3, 0x6a, 0xf5, 0xfb, 0x48, 0xc9, 0x43, 0xd3, 0x2a, 0xe2, 0x50, 0x65, 0x94,
0x93, 0xba, 0xc3, 0x04, 0xb4, 0xdd, 0x6e, 0xb2, 0xd6, 0x0d, 0xfa, 0xfd, 0x8c, 0xc5, 0x32, 0x85,
0x90, 0x1f, 0x09, 0xf4, 0xf1, 0xd3, 0x80, 0x66, 0xa3, 0xc5, 0xa9, 0xcf, 0x15, 0xc4, 0xd7, 0xad,
0xeb, 0x35, 0x6e, 0x41, 0x6b, 0x84, 0xd8, 0x68, 0x21, 0x50, 0x7f, 0x13, 0x1d, 0xc4, 0x3c, 0x0b,
0x35, 0x51, 0x25, 0xef, 0xd3, 0x44, 0x8a, 0x33, 0xc9, 0xaf, 0x96, 0xaa, 0x9e, 0x59, 0x37, 0xd3,
0x3a, 0x61, 0xc1, 0x86, 0x88, 0xe4, 0x97, 0x84, 0x4d, 0x4c, 0x8e, 0x7d, 0x1e, 0xb5, 0xef, 0xd3,
0xb6, 0xac, 0x1c, 0xd1, 0xf7, 0x69, 0x29, 0x96, 0xae, 0x64, 0xdd, 0x47, 0x3a, 0xac, 0xb8, 0xfd,
0xe4, 0x49, 0x3f, 0xd8, 0xa4, 0x3c, 0x8e, 0xcf, 0x9d, 0x14, 0x58, 0x51, 0x7b, 0xdd, 0x08, 0x18,
0x32, 0x18, 0x91, 0xf2, 0x04, 0x70, 0x34, 0x85, 0x39, 0x9e, 0x77, 0x78, 0x26, 0x20, 0x13, 0xbe,
0x29, 0xcc, 0x35, 0xa6, 0xc0, 0xd0, 0x14, 0x46, 0x29, 0xa0, 0x7e, 0x2b, 0xf7, 0x83, 0x40, 0x1c,
0xb3, 0x99, 0x37, 0x62, 0xab, 0xf7, 0x7a, 0x6a, 0x79, 0xa8, 0xdf, 0x22, 0xce, 0x3a, 0x6a, 0xb3,
0xbd, 0x9c, 0xb1, 0x62, 0xaa, 0x77, 0x37, 0x26, 0xc3, 0x2d, 0xda, 0x8e, 0x4b, 0x12, 0x47, 0x6d,
0x61, 0x0d, 0x34, 0xed, 0x1c, 0xcc, 0xd8, 0x54, 0xd7, 0xd4, 0x53, 0x03, 0x29, 0x6f, 0x55, 0x75,
0xad, 0x1b, 0x44, 0x7e, 0x5e, 0x25, 0x13, 0xe0, 0x01, 0x3f, 0x52, 0xde, 0xc7, 0x0f, 0x06, 0x51,
0xf4, 0x56, 0xd5, 0xbb, 0xce, 0xe8, 0xb6, 0xb3, 0x89, 0xca, 0x63, 0x47, 0xc4, 0xe3, 0x41, 0x5c,
0x28, 0x7a, 0x23, 0x78, 0x34, 0x46, 0x9b, 0x0d, 0xda, 0xd0, 0x18, 0xd5, 0xfb, 0xaf, 0x7d, 0xc6,
0xa8, 0x0f, 0x56, 0x3e, 0x7f, 0xaa, 0xc6, 0xe8, 0x2e, 0x13, 0xac, 0x8a, 0xdb, 0x5f, 0x25, 0x70,
0xa3, 0x12, 0x61, 0x4f, 0x7d, 0x1b, 0x6a, 0x24, 0x5f, 0xa4, 0x42, 0x59, 0xf1, 0x66, 0x6f, 0x3e,
0xe0, 0x5b, 0x65, 0x08, 0x9d, 0xbe, 0x51, 0xaa, 0xb0, 0xd9, 0x9b, 0x0f, 0xf8, 0x56, 0x6f, 0x68,
0x76, 0xfa, 0x46, 0xaf, 0x69, 0x6e, 0xf6, 0xe6, 0x95, 0xef, 0xbf, 0x6a, 0x06, 0xae, 0xed, 0xbc,
0x8a, 0xc3, 0x62, 0x91, 0x2c, 0xc0, 0x17, 0x4e, 0xba, 0xf6, 0x34, 0x1a, 0x0a, 0x27, 0x69, 0x15,
0xeb, 0xb3, 0x1e, 0xbe, 0x52, 0x9c, 0xf0, 0x32, 0x91, 0x47, 0xe5, 0xcf, 0x7a, 0x18, 0x6d, 0xe0,
0x50, 0xd2, 0x14, 0x52, 0x32, 0x07, 0x8d, 0x0e, 0x6a, 0x6e, 0xd3, 0x3e, 0x09, 0xd8, 0x6b, 0x5f,
0xaa, 0xdd, 0xe8, 0x49, 0x9b, 0x23, 0x3f, 0x87, 0xb1, 0xcf, 0x1a, 0x43, 0xad, 0xea, 0x3d, 0x6e,
0xdc, 0xea, 0xaf, 0xa0, 0xdc, 0xff, 0x4d, 0x93, 0x57, 0x60, 0xff, 0x6a, 0x10, 0x3c, 0xed, 0x63,
0x11, 0x0d, 0x84, 0x67, 0xb7, 0xd2, 0x51, 0x05, 0xf9, 0x8f, 0x41, 0x74, 0xdf, 0x5b, 0x10, 0xf7,
0xd4, 0xf9, 0x77, 0xfb, 0xd8, 0xf6, 0x9f, 0x3e, 0x7f, 0xf1, 0x6d, 0x54, 0x55, 0xe9, 0xfe, 0xa1,
0x49, 0xef, 0x1b, 0x0d, 0xf9, 0xc6, 0xc3, 0xcb, 0x62, 0x02, 0x85, 0x1a, 0xb1, 0xa1, 0x4e, 0x67,
0x60, 0x3c, 0x6e, 0x3f, 0xbd, 0xa5, 0x96, 0xf5, 0x09, 0x1a, 0x07, 0x56, 0x6f, 0x9b, 0x59, 0xe5,
0x09, 0x59, 0xb6, 0x68, 0x5c, 0xa0, 0xcf, 0x6e, 0xab, 0x46, 0x8d, 0x64, 0x0b, 0x96, 0x6f, 0xb4,
0x3f, 0xeb, 0x69, 0xd8, 0x79, 0xc7, 0xfd, 0x93, 0xdb, 0x29, 0xa9, 0xb2, 0xfc, 0xd7, 0x20, 0x7a,
0xe8, 0xb0, 0xe6, 0xb4, 0x03, 0xed, 0xc9, 0xfc, 0x28, 0x60, 0x9f, 0x52, 0xd2, 0x85, 0xfb, 0xbd,
0x6f, 0xa7, 0x6c, 0xbe, 0xd7, 0xe2, 0xa8, 0xec, 0x25, 0xa9, 0x80, 0xa2, 0xfd, 0xbd, 0x16, 0xd7,
0x6e, 0x4d, 0x8d, 0xe8, 0xef, 0xb5, 0x04, 0x70, 0xeb, 0x7b, 0x2d, 0x1e, 0xcf, 0xde, 0xef, 0xb5,
0x78, 0xad, 0x05, 0xbf, 0xd7, 0x12, 0xd6, 0xa0, 0x16, 0x9f, 0xa6, 0x08, 0xf5, 0xae, 0x7a, 0x2f,
0x8b, 0xee, 0x26, 0xfb, 0xd3, 0xdb, 0xa8, 0x10, 0xcb, 0x6f, 0xcd, 0xc9, 0xbb, 0x70, 0x3d, 0x9e,
0xa9, 0x73, 0x1f, 0x6e, 0xb3, 0x37, 0xaf, 0x7c, 0xff, 0x44, 0xe5, 0x5e, 0x7a, 0xb1, 0xe1, 0x85,
0xfc, 0x56, 0xcf, 0x7a, 0x68, 0xf1, 0xa8, 0x2c, 0xd8, 0x2d, 0xff, 0xa4, 0x1f, 0x4c, 0x54, 0xb7,
0x22, 0x54, 0xa3, 0x8f, 0xba, 0x0c, 0xa1, 0x26, 0xdf, 0xec, 0xcd, 0x13, 0x8b, 0x5c, 0xed, 0xbb,
0x6e, 0xed, 0x1e, 0xc6, 0xdc, 0xb6, 0xde, 0xea, 0xaf, 0xa0, 0xdc, 0x2f, 0x54, 0x50, 0x6b, 0xbb,
0x97, 0xed, 0xbc, 0xd1, 0x65, 0x6a, 0xec, 0x34, 0xf3, 0xa8, 0x2f, 0x1e, 0x0a, 0x6f, 0xec, 0x05,
0xbe, 0x2b, 0xbc, 0xf1, 0x2e, 0xf2, 0x9f, 0xdc, 0x4e, 0x49, 0x95, 0xe5, 0x9f, 0x07, 0xd1, 0x5d,
0xb2, 0x2c, 0xaa, 0x1f, 0x7c, 0xd6, 0xd7, 0x32, 0xea, 0x0f, 0x9f, 0xdf, 0x5a, 0x4f, 0x15, 0xea,
0xdf, 0x06, 0xd1, 0xbd, 0x40, 0xa1, 0xea, 0x0e, 0x72, 0x0b, 0xeb, 0x6e, 0x47, 0xf9, 0xe1, 0xed,
0x15, 0xa9, 0xe5, 0xde, 0xc6, 0xc7, 0xed, 0x0f, 0x99, 0x04, 0x6c, 0x8f, 0xe9, 0x0f, 0x99, 0x74,
0x6b, 0xe1, 0x2d, 0xa8, 0x2a, 0x28, 0x51, 0x99, 0x91, 0x6f, 0x0b, 0x4a, 0xc6, 0x2c, 0x28, 0x23,
0x5a, 0xed, 0xe4, 0x7c, 0x4e, 0x5e, 0xbc, 0xc9, 0x59, 0x36, 0xa1, 0x9d, 0xd4, 0xf2, 0x6e, 0x27,
0x9a, 0xc3, 0x5b, 0x77, 0x95, 0xf4, 0x94, 0x37, 0x69, 0xde, 0x23, 0x4a, 0x5f, 0x23, 0xc1, 0xad,
0xbb, 0x16, 0x4a, 0x78, 0x53, 0x31, 0x6d, 0xc8, 0x1b, 0x0a, 0x65, 0x1f, 0xf7, 0x41, 0x51, 0x02,
0xa1, 0xbd, 0xe9, 0x13, 0x81, 0x27, 0x21, 0x2b, 0xad, 0x53, 0x81, 0x8d, 0x9e, 0x34, 0xe1, 0x76,
0x0c, 0xe2, 0x4b, 0x60, 0x13, 0x28, 0x82, 0x6e, 0x35, 0xd5, 0xcb, 0xad, 0x4d, 0xfb, 0xdc, 0xee,
0xf0, 0x74, 0x3e, 0xcb, 0x54, 0x63, 0x92, 0x6e, 0x6d, 0xaa, 0xdb, 0x2d, 0xa2, 0xf1, 0xa6, 0xa5,
0x71, 0x2b, 0xc3, 0xcb, 0xc7, 0x61, 0x33, 0x4e, 0x54, 0xb9, 0xde, 0x8b, 0xa5, 0xeb, 0xa9, 0xba,
0x51, 0x47, 0x3d, 0x51, 0x4f, 0xda, 0xe8, 0x49, 0xe3, 0xdd, 0x43, 0xcb, 0xad, 0xee, 0x4f, 0x9b,
0x1d, 0xb6, 0x5a, 0x5d, 0x6a, 0xab, 0xbf, 0x02, 0xde, 0xab, 0x55, 0xbd, 0xaa, 0xca, 0x8b, 0xf6,
0x92, 0x34, 0x1d, 0xae, 0x07, 0xba, 0x49, 0x03, 0x05, 0xf7, 0x6a, 0x3d, 0x30, 0xd1, 0x93, 0x9b,
0xbd, 0xcd, 0x6c, 0xd8, 0x65, 0x47, 0x52, 0xbd, 0x7a, 0xb2, 0x4d, 0xa3, 0xfd, 0x36, 0xeb, 0x51,
0xeb, 0xda, 0x8e, 0xc2, 0x0f, 0xae, 0x55, 0xe1, 0xcd, 0xde, 0x3c, 0xba, 0x0c, 0x20, 0x29, 0xb9,
0xb2, 0x3c, 0xa0, 0x4c, 0x38, 0x2b, 0xc9, 0xc3, 0x0e, 0x0a, 0xed, 0x59, 0xd6, 0xc3, 0xe8, 0x75,
0x32, 0x99, 0x82, 0xf0, 0x9e, 0x63, 0xd9, 0x40, 0xf0, 0x1c, 0x0b, 0x81, 0xa8, 0xe9, 0xea, 0xdf,
0xf5, 0x66, 0xed, 0xc1, 0xc4, 0xd7, 0x74, 0x4a, 0xd9, 0xa2, 0x42, 0x4d, 0xe7, 0xa5, 0xd1, 0x6c,
0xa0, 0xdd, 0xaa, 0x97, 0xd6, 0x1f, 0x87, 0xcc, 0xa0, 0x37, 0xd7, 0xd7, 0x7b, 0xb1, 0x68, 0x45,
0x31, 0x0e, 0x93, 0x59, 0x22, 0x7c, 0x2b, 0x8a, 0x65, 0xa3, 0x42, 0x42, 0x2b, 0x4a, 0x1b, 0xa5,
0xaa, 0x57, 0xc5, 0x08, 0x07, 0x93, 0x70, 0xf5, 0x6a, 0xa6, 0x5f, 0xf5, 0x34, 0xdb, 0x3a, 0x76,
0xcd, 0x74, 0x97, 0x11, 0x57, 0x2a, 0x59, 0xf6, 0xf4, 0x6d, 0xf9, 0x5a, 0x25, 0x06, 0x43, 0xb3,
0x0e, 0xa5, 0x80, 0x8f, 0x13, 0x2a, 0xae, 0x39, 0x19, 0xce, 0x73, 0x60, 0x05, 0xcb, 0x62, 0x6f,
0x72, 0x2a, 0x0d, 0xb6, 0xc8, 0x50, 0x72, 0x4a, 0x6a, 0xa0, 0x43, 0x7d, 0xf7, 0x85, 0x48, 0xcf,
0x50, 0xd0, 0x6f, 0x1e, 0xba, 0xef, 0x43, 0x3e, 0xea, 0x41, 0xe2, 0x43, 0xfd, 0x06, 0xd0, 0xdb,
0xf2, 0xb5, 0xd3, 0x8f, 0x03, 0xa6, 0x5c, 0x34, 0x94, 0x08, 0xd3, 0x2a, 0xa8, 0x53, 0xeb, 0x00,
0x17, 0xc4, 0x8f, 0x61, 0xe9, 0xeb, 0xd4, 0x26, 0x3e, 0x95, 0x48, 0xa8, 0x53, 0xb7, 0x51, 0x14,
0x67, 0xda, 0x79, 0xd0, 0x4a, 0x40, 0xdf, 0x4e, 0x7d, 0x56, 0x3b, 0x39, 0x34, 0x72, 0x76, 0x93,
0x85, 0x73, 0x8a, 0xe1, 0x29, 0xe8, 0x6e, 0xb2, 0xf0, 0x1f, 0x62, 0xac, 0xf7, 0x62, 0xf1, 0x85,
0x01, 0x26, 0xe0, 0x4d, 0x73, 0x92, 0xef, 0x29, 0xae, 0x94, 0xb7, 0x8e, 0xf2, 0xd7, 0xba, 0x41,
0x73, 0x3d, 0xf7, 0xa4, 0xe0, 0x31, 0x94, 0xa5, 0xfa, 0xba, 0x9b, 0x7b, 0xff, 0x49, 0xc9, 0x46,
0xe8, 0xdb, 0x6e, 0x0f, 0xc2, 0x90, 0xb2, 0xfd, 0x65, 0xf4, 0xf6, 0x21, 0x9f, 0x8e, 0x21, 0x9b,
0x0c, 0x7f, 0xe0, 0x5e, 0x88, 0xe5, 0xd3, 0x51, 0xf5, 0xb3, 0xb6, 0x77, 0x87, 0x12, 0x9b, 0x2b,
0x7d, 0xbb, 0x70, 0x31, 0x9f, 0x8e, 0x05, 0x13, 0xe8, 0x4a, 0x9f, 0xfc, 0x7d, 0x54, 0x09, 0x88,
0x2b, 0x7d, 0x0e, 0x80, 0xec, 0x9d, 0x15, 0x00, 0x5e, 0x7b, 0x95, 0x20, 0x68, 0x4f, 0x01, 0x66,
0xd5, 0xd5, 0xf6, 0xaa, 0xc0, 0x16, 0x5f, 0xc1, 0x33, 0x3a, 0x52, 0x4a, 0xac, 0xba, 0x6d, 0xca,
0x74, 0x86, 0xba, 0xfa, 0xf2, 0x5b, 0x16, 0xf3, 0xd9, 0x8c, 0x15, 0x4b, 0xd4, 0x19, 0x54, 0x2d,
0x2d, 0x80, 0xe8, 0x0c, 0x5e, 0xd0, 0xf4, 0xf2, 0xe6, 0x31, 0xc7, 0xd7, 0xfb, 0xbc, 0xe0, 0x73,
0x91, 0x64, 0x80, 0xbf, 0x67, 0xa0, 0x1f, 0xa8, 0xcd, 0x10, 0xbd, 0x9c, 0x62, 0x4d, 0x54, 0x28,
0x89, 0xfa, 0x76, 0xa0, 0xfc, 0x46, 0x6a, 0x29, 0x78, 0x81, 0x4f, 0x07, 0x6b, 0x2b, 0x18, 0x22,
0xa2, 0x42, 0x12, 0x46, 0x6d, 0x7f, 0x92, 0x64, 0x53, 0x6f, 0xdb, 0x9f, 0xd8, 0x5f, 0x18, 0xbc,
0x47, 0x03, 0x66, 0x7e, 0xaf, 0x1f, 0x5a, 0xfd, 0xcd, 0x20, 0xf5, 0x7e, 0xa4, 0xf7, 0xa1, 0xdb,
0x04, 0x31, 0xbf, 0xfb, 0x49, 0xe4, 0xea, 0x65, 0x0e, 0x19, 0x4c, 0x9a, 0x3b, 0x70, 0x3e, 0x57,
0x0e, 0x11, 0x74, 0x85, 0x49, 0xd3, 0x15, 0x8e, 0x40, 0x14, 0x49, 0x5c, 0x8e, 0x41, 0x9c, 0xb0,
0x82, 0xcd, 0x40, 0x40, 0x81, 0xbb, 0x82, 0x42, 0x46, 0x0e, 0x43, 0x74, 0x05, 0x8a, 0x55, 0x0e,
0x7f, 0x3f, 0x7a, 0xb7, 0x9a, 0x09, 0x21, 0x53, 0x1f, 0x6d, 0x7f, 0x21, 0xff, 0xda, 0xc3, 0xf0,
0x3d, 0x6d, 0x63, 0x2c, 0x0a, 0x60, 0xb3, 0xc6, 0xf6, 0x3b, 0xfa, 0x77, 0x09, 0x6e, 0x0d, 0xaa,
0x75, 0xe0, 0x98, 0x8b, 0xe4, 0xb2, 0x4a, 0x3c, 0xd4, 0x31, 0x0f, 0x5a, 0x07, 0x6c, 0xf1, 0x28,
0xf0, 0x36, 0xbd, 0x8f, 0x33, 0x23, 0xd1, 0x96, 0x9e, 0x42, 0x9e, 0xe2, 0x91, 0xe8, 0x68, 0x4b,
0x80, 0x18, 0x89, 0x5e, 0xd0, 0x2c, 0x6a, 0xb6, 0xf8, 0x0c, 0xc2, 0x95, 0x39, 0x83, 0x7e, 0x95,
0x39, 0x73, 0x2e, 0xd1, 0xa7, 0xd1, 0xbb, 0x47, 0x30, 0xbb, 0x80, 0xa2, 0xbc, 0x4a, 0xf2, 0xfd,
0x6a, 0x09, 0x62, 0x62, 0x8e, 0x5f, 0x30, 0x33, 0xc4, 0x48, 0x23, 0xc4, 0x3a, 0x4d, 0xa0, 0x66,
0xac, 0x1b, 0xe0, 0xa0, 0x3c, 0x66, 0x33, 0x90, 0xdf, 0x06, 0x18, 0xae, 0x53, 0x46, 0x2c, 0x88,
0x18, 0xeb, 0x24, 0x6c, 0xbd, 0x8f, 0x63, 0x98, 0x53, 0x98, 0x56, 0x3d, 0xac, 0x38, 0x61, 0xcb,
0x19, 0x64, 0x42, 0x99, 0x44, 0xbb, 0x94, 0x96, 0x49, 0x3f, 0x4f, 0xec, 0x52, 0xf6, 0xd1, 0xb3,
0xa2, 0x52, 0xe7, 0xc1, 0x9f, 0xf0, 0x42, 0xd4, 0x7f, 0x92, 0xe1, 0xbc, 0x48, 0x51, 0x54, 0xea,
0x3e, 0x54, 0x87, 0x24, 0xa2, 0xd2, 0xb0, 0x86, 0xf5, 0x2d, 0x63, 0xa7, 0x0c, 0xaf, 0xa0, 0xd0,
0xfd, 0xe4, 0xc5, 0x8c, 0x25, 0xa9, 0xea, 0x0d, 0x5f, 0x04, 0x6c, 0x13, 0x3a, 0xc4, 0xb7, 0x8c,
0xfb, 0xea, 0x5a, 0x5f, 0x7f, 0x0e, 0x97, 0x10, 0x6d, 0x9a, 0x76, 0xd8, 0x27, 0x36, 0x4d, 0xbb,
0xb5, 0x4c, 0x2e, 0x63, 0x58, 0xc9, 0x2d, 0x25, 0xb1, 0xc3, 0x27, 0x78, 0x07, 0xc5, 0xb2, 0x89,
0x40, 0x22, 0x97, 0x09, 0x2a, 0x98, 0xc9, 0xdf, 0x60, 0x7b, 0x49, 0xc6, 0xd2, 0xe4, 0xa7, 0xf8,
0x72, 0xb0, 0x65, 0xa7, 0x21, 0x88, 0xc9, 0xdf, 0x4f, 0xfa, 0x5c, 0xed, 0x83, 0x38, 0x4b, 0xaa,
0xa9, 0x7f, 0x2d, 0xf0, 0xdc, 0x24, 0xd1, 0xed, 0xca, 0x22, 0x95, 0xab, 0x9f, 0x0d, 0xa2, 0xbb,
0xf8, 0xb1, 0x6e, 0xe7, 0xf9, 0xb8, 0x5a, 0xb3, 0x4f, 0x21, 0x86, 0x24, 0x17, 0xc3, 0x4f, 0xc3,
0xcf, 0x0a, 0xe1, 0xc4, 0xd1, 0x73, 0x0f, 0x35, 0xeb, 0x40, 0xb3, 0x9a, 0x4b, 0xc6, 0xf5, 0xdf,
0x2a, 0x3a, 0x2f, 0xa1, 0x50, 0x9f, 0xe1, 0xde, 0x07, 0x81, 0x46, 0xa7, 0xc5, 0x8d, 0x2c, 0xb0,
0xaa, 0x28, 0x31, 0x3a, 0xc3, 0x1a, 0x66, 0xfb, 0xc3, 0xe2, 0x4e, 0xa1, 0xe4, 0xe9, 0x02, 0xe4,
0xfd, 0xb0, 0x27, 0xa4, 0x31, 0x8b, 0x22, 0xb6, 0x3f, 0x68, 0xda, 0x5c, 0x6e, 0x6c, 0xbb, 0xdd,
0xce, 0x96, 0x07, 0xf8, 0x10, 0xd9, 0x63, 0x49, 0x62, 0xc4, 0xc9, 0x56, 0x00, 0xb7, 0xb6, 0x07,
0x0b, 0xce, 0x26, 0x31, 0x2b, 0xc5, 0x09, 0x5b, 0xa6, 0x9c, 0x4d, 0xe4, 0xba, 0x8e, 0xb7, 0x07,
0x1b, 0x66, 0x64, 0x43, 0xd4, 0xf6, 0x20, 0x05, 0xd7, 0x3e, 0x9f, 0x7f, 0xf8, 0x3f, 0x5f, 0xdf,
0x19, 0xfc, 0xfc, 0xeb, 0x3b, 0x83, 0xff, 0xff, 0xfa, 0xce, 0xe0, 0x67, 0xdf, 0xdc, 0x79, 0xeb,
0xe7, 0xdf, 0xdc, 0x79, 0xeb, 0xff, 0xbe, 0xb9, 0xf3, 0xd6, 0x57, 0x6f, 0xab, 0x3f, 0x53, 0x75,
0xf1, 0x4b, 0xf2, 0x8f, 0x4d, 0x3d, 0xfb, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0x71, 0x16, 0x84,
0x92, 0xca, 0x6a, 0x00, 0x00,
}
// This is a compile-time assertion to ensure that this generated file
@ -400,6 +402,7 @@ type ClientCommandsHandler interface {
ObjectCreateSet(context.Context, *pb.RpcObjectCreateSetRequest) *pb.RpcObjectCreateSetResponse
ObjectGraph(context.Context, *pb.RpcObjectGraphRequest) *pb.RpcObjectGraphResponse
ObjectSearch(context.Context, *pb.RpcObjectSearchRequest) *pb.RpcObjectSearchResponse
ObjectSearchWithMeta(context.Context, *pb.RpcObjectSearchWithMetaRequest) *pb.RpcObjectSearchWithMetaResponse
ObjectSearchSubscribe(context.Context, *pb.RpcObjectSearchSubscribeRequest) *pb.RpcObjectSearchSubscribeResponse
ObjectSubscribeIds(context.Context, *pb.RpcObjectSubscribeIdsRequest) *pb.RpcObjectSubscribeIdsResponse
ObjectGroupsSubscribe(context.Context, *pb.RpcObjectGroupsSubscribeRequest) *pb.RpcObjectGroupsSubscribeResponse
@ -461,10 +464,11 @@ type ClientCommandsHandler interface {
HistoryShowVersion(context.Context, *pb.RpcHistoryShowVersionRequest) *pb.RpcHistoryShowVersionResponse
HistoryGetVersions(context.Context, *pb.RpcHistoryGetVersionsRequest) *pb.RpcHistoryGetVersionsResponse
HistorySetVersion(context.Context, *pb.RpcHistorySetVersionRequest) *pb.RpcHistorySetVersionResponse
HistoryDiffVersions(context.Context, *pb.RpcHistoryDiffVersionsRequest) *pb.RpcHistoryDiffVersionsResponse
// Files
// ***
FileOffload(context.Context, *pb.RpcFileOffloadRequest) *pb.RpcFileOffloadResponse
FileSpaceOffload(context.Context, *pb.RpcFileSpaceOffloadRequest) *pb.RpcFileSpaceOffloadResponse
FileReconcile(context.Context, *pb.RpcFileReconcileRequest) *pb.RpcFileReconcileResponse
FileListOffload(context.Context, *pb.RpcFileListOffloadRequest) *pb.RpcFileListOffloadResponse
FileUpload(context.Context, *pb.RpcFileUploadRequest) *pb.RpcFileUploadResponse
FileDownload(context.Context, *pb.RpcFileDownloadRequest) *pb.RpcFileDownloadResponse
@ -609,6 +613,7 @@ type ClientCommandsHandler interface {
// Get current subscription status (tier, expiration date, etc.)
// WARNING: can be cached by Anytype Heart
MembershipGetStatus(context.Context, *pb.RpcMembershipGetStatusRequest) *pb.RpcMembershipGetStatusResponse
// Check if the requested name is valid and vacant for the requested tier
MembershipIsNameValid(context.Context, *pb.RpcMembershipIsNameValidRequest) *pb.RpcMembershipIsNameValidResponse
// Buy a subscription, will return a payment URL. The user should be redirected to this URL to complete the payment.
MembershipRegisterPaymentRequest(context.Context, *pb.RpcMembershipRegisterPaymentRequestRequest) *pb.RpcMembershipRegisterPaymentRequestResponse
@ -1722,6 +1727,26 @@ func ObjectSearch(b []byte) (resp []byte) {
return resp
}
func ObjectSearchWithMeta(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
if r := recover(); r != nil {
resp, _ = (&pb.RpcObjectSearchWithMetaResponse{Error: &pb.RpcObjectSearchWithMetaResponseError{Code: pb.RpcObjectSearchWithMetaResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
PanicHandler(r)
}
}
}()
in := new(pb.RpcObjectSearchWithMetaRequest)
if err := in.Unmarshal(b); err != nil {
resp, _ = (&pb.RpcObjectSearchWithMetaResponse{Error: &pb.RpcObjectSearchWithMetaResponseError{Code: pb.RpcObjectSearchWithMetaResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
return resp
}
resp, _ = clientCommandsHandler.ObjectSearchWithMeta(context.Background(), in).Marshal()
return resp
}
func ObjectSearchSubscribe(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
@ -2742,6 +2767,46 @@ func HistorySetVersion(b []byte) (resp []byte) {
return resp
}
func HistoryDiffVersions(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
if r := recover(); r != nil {
resp, _ = (&pb.RpcHistoryDiffVersionsResponse{Error: &pb.RpcHistoryDiffVersionsResponseError{Code: pb.RpcHistoryDiffVersionsResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
PanicHandler(r)
}
}
}()
in := new(pb.RpcHistoryDiffVersionsRequest)
if err := in.Unmarshal(b); err != nil {
resp, _ = (&pb.RpcHistoryDiffVersionsResponse{Error: &pb.RpcHistoryDiffVersionsResponseError{Code: pb.RpcHistoryDiffVersionsResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
return resp
}
resp, _ = clientCommandsHandler.HistoryDiffVersions(context.Background(), in).Marshal()
return resp
}
func FileOffload(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
if r := recover(); r != nil {
resp, _ = (&pb.RpcFileOffloadResponse{Error: &pb.RpcFileOffloadResponseError{Code: pb.RpcFileOffloadResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
PanicHandler(r)
}
}
}()
in := new(pb.RpcFileOffloadRequest)
if err := in.Unmarshal(b); err != nil {
resp, _ = (&pb.RpcFileOffloadResponse{Error: &pb.RpcFileOffloadResponseError{Code: pb.RpcFileOffloadResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
return resp
}
resp, _ = clientCommandsHandler.FileOffload(context.Background(), in).Marshal()
return resp
}
func FileSpaceOffload(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
@ -2762,26 +2827,6 @@ func FileSpaceOffload(b []byte) (resp []byte) {
return resp
}
func FileReconcile(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
if r := recover(); r != nil {
resp, _ = (&pb.RpcFileReconcileResponse{Error: &pb.RpcFileReconcileResponseError{Code: pb.RpcFileReconcileResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
PanicHandler(r)
}
}
}()
in := new(pb.RpcFileReconcileRequest)
if err := in.Unmarshal(b); err != nil {
resp, _ = (&pb.RpcFileReconcileResponse{Error: &pb.RpcFileReconcileResponseError{Code: pb.RpcFileReconcileResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
return resp
}
resp, _ = clientCommandsHandler.FileReconcile(context.Background(), in).Marshal()
return resp
}
func FileListOffload(b []byte) (resp []byte) {
defer func() {
if PanicHandler != nil {
@ -5596,6 +5641,8 @@ func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
cd = ObjectGraph(data)
case "ObjectSearch":
cd = ObjectSearch(data)
case "ObjectSearchWithMeta":
cd = ObjectSearchWithMeta(data)
case "ObjectSearchSubscribe":
cd = ObjectSearchSubscribe(data)
case "ObjectSubscribeIds":
@ -5698,10 +5745,12 @@ func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
cd = HistoryGetVersions(data)
case "HistorySetVersion":
cd = HistorySetVersion(data)
case "HistoryDiffVersions":
cd = HistoryDiffVersions(data)
case "FileOffload":
cd = FileOffload(data)
case "FileSpaceOffload":
cd = FileSpaceOffload(data)
case "FileReconcile":
cd = FileReconcile(data)
case "FileListOffload":
cd = FileListOffload(data)
case "FileUpload":
@ -6750,6 +6799,20 @@ func (h *ClientCommandsHandlerProxy) ObjectSearch(ctx context.Context, req *pb.R
call, _ := actualCall(ctx, req)
return call.(*pb.RpcObjectSearchResponse)
}
func (h *ClientCommandsHandlerProxy) ObjectSearchWithMeta(ctx context.Context, req *pb.RpcObjectSearchWithMetaRequest) *pb.RpcObjectSearchWithMetaResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.ObjectSearchWithMeta(ctx, req.(*pb.RpcObjectSearchWithMetaRequest)), nil
}
for _, interceptor := range h.interceptors {
toCall := actualCall
currentInterceptor := interceptor
actualCall = func(ctx context.Context, req any) (any, error) {
return currentInterceptor(ctx, req, "ObjectSearchWithMeta", toCall)
}
}
call, _ := actualCall(ctx, req)
return call.(*pb.RpcObjectSearchWithMetaResponse)
}
func (h *ClientCommandsHandlerProxy) ObjectSearchSubscribe(ctx context.Context, req *pb.RpcObjectSearchSubscribeRequest) *pb.RpcObjectSearchSubscribeResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.ObjectSearchSubscribe(ctx, req.(*pb.RpcObjectSearchSubscribeRequest)), nil
@ -7464,6 +7527,34 @@ func (h *ClientCommandsHandlerProxy) HistorySetVersion(ctx context.Context, req
call, _ := actualCall(ctx, req)
return call.(*pb.RpcHistorySetVersionResponse)
}
func (h *ClientCommandsHandlerProxy) HistoryDiffVersions(ctx context.Context, req *pb.RpcHistoryDiffVersionsRequest) *pb.RpcHistoryDiffVersionsResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.HistoryDiffVersions(ctx, req.(*pb.RpcHistoryDiffVersionsRequest)), nil
}
for _, interceptor := range h.interceptors {
toCall := actualCall
currentInterceptor := interceptor
actualCall = func(ctx context.Context, req any) (any, error) {
return currentInterceptor(ctx, req, "HistoryDiffVersions", toCall)
}
}
call, _ := actualCall(ctx, req)
return call.(*pb.RpcHistoryDiffVersionsResponse)
}
func (h *ClientCommandsHandlerProxy) FileOffload(ctx context.Context, req *pb.RpcFileOffloadRequest) *pb.RpcFileOffloadResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.FileOffload(ctx, req.(*pb.RpcFileOffloadRequest)), nil
}
for _, interceptor := range h.interceptors {
toCall := actualCall
currentInterceptor := interceptor
actualCall = func(ctx context.Context, req any) (any, error) {
return currentInterceptor(ctx, req, "FileOffload", toCall)
}
}
call, _ := actualCall(ctx, req)
return call.(*pb.RpcFileOffloadResponse)
}
func (h *ClientCommandsHandlerProxy) FileSpaceOffload(ctx context.Context, req *pb.RpcFileSpaceOffloadRequest) *pb.RpcFileSpaceOffloadResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.FileSpaceOffload(ctx, req.(*pb.RpcFileSpaceOffloadRequest)), nil
@ -7478,20 +7569,6 @@ func (h *ClientCommandsHandlerProxy) FileSpaceOffload(ctx context.Context, req *
call, _ := actualCall(ctx, req)
return call.(*pb.RpcFileSpaceOffloadResponse)
}
func (h *ClientCommandsHandlerProxy) FileReconcile(ctx context.Context, req *pb.RpcFileReconcileRequest) *pb.RpcFileReconcileResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.FileReconcile(ctx, req.(*pb.RpcFileReconcileRequest)), nil
}
for _, interceptor := range h.interceptors {
toCall := actualCall
currentInterceptor := interceptor
actualCall = func(ctx context.Context, req any) (any, error) {
return currentInterceptor(ctx, req, "FileReconcile", toCall)
}
}
call, _ := actualCall(ctx, req)
return call.(*pb.RpcFileReconcileResponse)
}
func (h *ClientCommandsHandlerProxy) FileListOffload(ctx context.Context, req *pb.RpcFileListOffloadRequest) *pb.RpcFileListOffloadResponse {
actualCall := func(ctx context.Context, req any) (any, error) {
return h.client.FileListOffload(ctx, req.(*pb.RpcFileListOffloadRequest)), nil

View file

@ -139,40 +139,6 @@ func handleZip(input, output string) {
}
}
func handleDirectory(input, output string) {
err := filepath.Walk(input, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
// Get relative path
rel, err := filepath.Rel(input, path)
if err != nil {
return err
}
outputFile := filepath.Join(output, rel)
f, err := os.Open(path)
if err != nil {
return err
}
processFile(File{
Name: f.Name(),
RC: f,
}, outputFile)
}
return nil
})
if err != nil {
log.Fatalf("Failed to process directory: %v", err)
}
}
func createZipFromDirectory(input, output string) {
// create a new zip file
newZipFile, err := os.Create(output)

View file

@ -7,7 +7,6 @@ import (
"strings"
"github.com/hashicorp/go-multierror"
"github.com/ipfs/go-cid"
"github.com/samber/lo"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
@ -214,39 +213,6 @@ func validateBlockLinks(s *pb.SnapshotWithType, info *useCaseInfo) (err error) {
return err
}
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
}
func validateDeleted(s *pb.SnapshotWithType, _ *useCaseInfo) error {
id := pbtypes.GetString(s.Snapshot.Data.Details, bundle.RelationKeyId.String())
@ -294,15 +260,6 @@ func getRelationLinkByKey(links []*model.RelationLink, key string) *model.Relati
return nil
}
func snapshotHasKeyForHash(s *pb.SnapshotWithType, hash string) bool {
for _, k := range s.Snapshot.FileKeys {
if k.Hash == hash && len(k.Keys) > 0 {
return true
}
}
return false
}
func isLinkRelation(k string) bool {
return k == bundle.RelationKeyLinks.String() || k == bundle.RelationKeySourceObject.String() || k == bundle.RelationKeyBacklinks.String()
}

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"path/filepath"
"sync"
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app"
@ -59,7 +58,6 @@ type service struct {
coordClient coordinatorclient.CoordinatorClient
picker cache.ObjectGetter
once sync.Once
personalSpaceId string
}

View file

@ -194,8 +194,6 @@ func Bootstrap(a *app.App, components ...app.Component) {
a.Register(c)
}
const fileWatcherUpdateInterval = 5 * time.Second
a.
// Data storages
Register(clientds.New()).
@ -258,7 +256,7 @@ func Bootstrap(a *app.App, components ...app.Component) {
Register(treemanager.New()).
Register(block.New()).
Register(indexer.New()).
Register(syncstatus.New(fileWatcherUpdateInterval)).
Register(syncstatus.New()).
Register(history.New()).
Register(gateway.New()).
Register(export.New()).

View file

@ -82,6 +82,10 @@ type Config struct {
nodeConf nodeconf.Configuration
}
func (c *Config) IsLocalOnlyMode() bool {
return c.NetworkMode == pb.RpcAccount_LocalOnly
}
type FSConfig struct {
IPFSStorageAddr string
}

View file

@ -82,15 +82,14 @@ func (mw *Middleware) ObjectOpen(cctx context.Context, req *pb.RpcObjectOpenRequ
return m
}
id := domain.FullID{
SpaceID: req.SpaceId,
ObjectID: req.ObjectId,
}
err := mw.doBlockService(func(bs *block.Service) (err error) {
id := domain.FullID{
SpaceID: req.SpaceId,
ObjectID: req.ObjectId,
}
obj, err = bs.OpenBlock(ctx, id, req.IncludeRelationsAsDependentObjects)
return err
})
code := mapErrorCode(err,
errToCode(spacestorage.ErrTreeStorageAlreadyDeleted, pb.RpcObjectOpenResponseError_OBJECT_DELETED),
errToCode(source.ErrUnknownDataFormat, pb.RpcObjectOpenResponseError_ANYTYPE_NEEDS_UPGRADE),
@ -111,15 +110,14 @@ func (mw *Middleware) ObjectShow(cctx context.Context, req *pb.RpcObjectShowRequ
return m
}
id := domain.FullID{
SpaceID: req.SpaceId,
ObjectID: req.ObjectId,
}
err := mw.doBlockService(func(bs *block.Service) (err error) {
id := domain.FullID{
SpaceID: req.SpaceId,
ObjectID: req.ObjectId,
}
obj, err = bs.ShowBlock(id, req.IncludeRelationsAsDependentObjects)
return err
})
code := mapErrorCode(err,
errToCode(spacestorage.ErrTreeStorageAlreadyDeleted, pb.RpcObjectShowResponseError_OBJECT_DELETED),
errToCode(source.ErrUnknownDataFormat, pb.RpcObjectShowResponseError_ANYTYPE_NEEDS_UPGRADE),

View file

@ -13,6 +13,7 @@ import (
"time"
"github.com/anyproto/any-sync/app"
"github.com/globalsign/mgo/bson"
"github.com/gogo/protobuf/types"
"github.com/anyproto/anytype-heart/core/block/editor/state"
@ -240,7 +241,7 @@ func (s *service) ContentUpdaters(spaceID string, url string, parseBlock bool) (
updaters := make(chan func(contentBookmark *bookmark.ObjectContent), 1)
data, body, err := s.linkPreview.Fetch(ctx, url)
data, body, isFile, err := s.linkPreview.Fetch(ctx, url)
if err != nil {
updaters <- func(c *bookmark.ObjectContent) {
if c.BookmarkContent == nil {
@ -312,6 +313,10 @@ func (s *service) ContentUpdaters(spaceID string, url string, parseBlock bool) (
go func() {
defer wg.Done()
updaters <- func(c *bookmark.ObjectContent) {
if isFile {
s.handleFileBlock(c, url)
return
}
blocks, _, err := anymark.HTMLToBlocks(body, url)
if err != nil {
log.Errorf("parse blocks: %s", err)
@ -328,6 +333,19 @@ func (s *service) ContentUpdaters(spaceID string, url string, parseBlock bool) (
return updaters, nil
}
func (s *service) handleFileBlock(c *bookmark.ObjectContent, url string) {
c.Blocks = append(
c.Blocks,
&model.Block{
Id: bson.NewObjectId().Hex(),
Content: &model.BlockContentOfFile{
File: &model.BlockContentFile{
Name: url,
}},
},
)
}
func (s *service) fetcher(spaceID string, blockID string, params bookmark.FetchParams) error {
updaters, err := s.ContentUpdaters(spaceID, params.Url, false)
if err != nil {

View file

@ -0,0 +1,67 @@
package bookmark
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/linkpreview/mock_linkpreview"
)
func TestService_FetchBookmarkContent(t *testing.T) {
t.Run("link to html page - create blocks", func(t *testing.T) {
// given
preview := mock_linkpreview.NewMockLinkPreview(t)
preview.EXPECT().Fetch(mock.Anything, "http://test.com").Return(model.LinkPreview{}, []byte(testHtml), false, nil)
s := &service{linkPreview: preview}
// when
updaters := s.FetchBookmarkContent("space", "http://test.com", true)
// then
content := updaters()
assert.Len(t, content.Blocks, 2)
})
t.Run("link to file - create one block with file", func(t *testing.T) {
// given
preview := mock_linkpreview.NewMockLinkPreview(t)
preview.EXPECT().Fetch(mock.Anything, "http://test.com").Return(model.LinkPreview{}, nil, true, nil)
s := &service{linkPreview: preview}
// when
updaters := s.FetchBookmarkContent("space", "http://test.com", true)
// then
content := updaters()
assert.Len(t, content.Blocks, 1)
assert.NotNil(t, content.Blocks[0].GetFile())
assert.Equal(t, "http://test.com", content.Blocks[0].GetFile().GetName())
})
t.Run("link to file - create one block with file, image is base64", func(t *testing.T) {
// given
preview := mock_linkpreview.NewMockLinkPreview(t)
preview.EXPECT().Fetch(mock.Anything, "http://test.com").Return(model.LinkPreview{}, []byte(testHtmlBase64), false, nil)
s := &service{linkPreview: preview}
// when
updaters := s.FetchBookmarkContent("space", "http://test.com", true)
// then
content := updaters()
assert.Len(t, content.Blocks, 1)
assert.NotNil(t, content.Blocks[0].GetFile())
})
}
const testHtml = `<html><head>
<title>Title</title>
Test
</head></html>`
const testHtmlBase64 = "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=\">"

View file

@ -239,7 +239,7 @@ func (s *Service) DeleteDataviewView(ctx session.Context, req pb.RpcBlockDatavie
func (s *Service) SetDataviewActiveView(ctx session.Context, req pb.RpcBlockDataviewViewSetActiveRequest) error {
return cache.Do(s, req.ContextId, func(b dataview.Dataview) error {
return b.SetActiveView(ctx, req.BlockId, req.ViewId, int(req.Limit), int(req.Offset))
return b.SetActiveView(ctx, req.BlockId, req.ViewId)
})
}
@ -493,6 +493,7 @@ func (s *Service) UploadBlockFile(ctx session.Context, req UploadRequest, groupI
_, err = b.Upload(ctx, req.BlockId, file.FileSource{
Path: req.FilePath,
Url: req.Url,
Bytes: req.Bytes,
GroupID: groupID,
Origin: req.ObjectOrigin,
}, false)
@ -505,6 +506,7 @@ func (s *Service) UploadBlockFileSync(ctx session.Context, req UploadRequest) (e
_, err = b.Upload(ctx, req.BlockId, file.FileSource{
Path: req.FilePath,
Url: req.Url,
Bytes: req.Bytes,
Origin: req.ObjectOrigin,
}, true)
return err
@ -534,8 +536,6 @@ func (s *Service) UploadFile(ctx context.Context, spaceId string, req FileUpload
upl.SetAdditionalDetails(req.Details)
if req.Type != model.BlockContentFile_None {
upl.SetType(req.Type)
} else {
upl.AutoType(true)
}
if req.LocalPath != "" {
upl.SetFile(req.LocalPath)
@ -576,6 +576,7 @@ func (s *Service) UploadFileBlock(
fileObjectId, err = b.Upload(nil, req.BlockId, file.FileSource{
Path: req.FilePath,
Url: req.Url,
Bytes: req.Bytes,
GroupID: "",
Origin: req.ObjectOrigin,
}, true)

View file

@ -15,7 +15,6 @@ import (
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/uri"
@ -28,11 +27,10 @@ type CreateAndFetchRequest struct {
Origin objectorigin.ObjectOrigin
}
func NewBookmark(sb smartblock.SmartBlock, bookmarkSvc BookmarkService, objectStore objectstore.ObjectStore) Bookmark {
func NewBookmark(sb smartblock.SmartBlock, bookmarkSvc BookmarkService) Bookmark {
return &sbookmark{
SmartBlock: sb,
bookmarkSvc: bookmarkSvc,
objectStore: objectStore,
}
}
@ -50,7 +48,6 @@ type BookmarkService interface {
type sbookmark struct {
smartblock.SmartBlock
bookmarkSvc BookmarkService
objectStore objectstore.ObjectStore
}
type BlockService interface {

View file

@ -66,8 +66,14 @@ type clipboard struct {
fileObjectService fileobject.Service
}
func (cb *clipboard) Paste(ctx session.Context, req *pb.RpcBlockPasteRequest, groupId string) (blockIds []string, uploadArr []pb.RpcBlockUploadRequest, caretPosition int32, isSameBlockCaret bool, err error) {
func (cb *clipboard) Paste(ctx session.Context, req *pb.RpcBlockPasteRequest, groupId string) (
blockIds []string, uploadArr []pb.RpcBlockUploadRequest, caretPosition int32, isSameBlockCaret bool, err error,
) {
caretPosition = -1
if err = cb.Restrictions().Object.Check(model.Restrictions_Blocks); err != nil {
return nil, nil, caretPosition, false, err
}
if len(req.FileSlot) > 0 {
blockIds, err = cb.pasteFiles(ctx, req)
return
@ -251,21 +257,22 @@ func unlinkAndClearBlocks(
}
func assertBlocks(stateBlocks []*model.Block, requestBlocks []*model.Block) (map[string]*model.Block, error) {
if len(requestBlocks) == 0 || requestBlocks[0].Id == "" {
if len(requestBlocks) == 0 || requestBlocks[0].GetId() == "" {
return nil, errors.New("nothing to cut")
}
idToBlockMap := make(map[string]*model.Block)
for _, stateBlock := range stateBlocks {
idToBlockMap[stateBlock.Id] = stateBlock
idToBlockMap[stateBlock.GetId()] = stateBlock
}
for _, requestBlock := range requestBlocks {
if requestBlock.Id == "" {
reqId := requestBlock.GetId()
if reqId == "" {
return nil, errors.New("empty requestBlock id")
}
if stateBlock, ok := idToBlockMap[requestBlock.Id]; !ok {
return nil, fmt.Errorf("requestBlock with id %s not found", stateBlock.Id)
if _, ok := idToBlockMap[reqId]; !ok {
return nil, fmt.Errorf("requestBlock with id %s not found", reqId)
}
}
return idToBlockMap, nil
@ -575,7 +582,7 @@ func (cb *clipboard) addRelationLinksToDataview(d *model.BlockContentDataview) (
}
func (cb *clipboard) newHTMLConverter(s *state.State) *html.HTML {
return html.NewHTMLConverter(cb.SpaceID(), cb.fileService, s, cb.fileObjectService)
return html.NewHTMLConverter(cb.fileService, s, cb.fileObjectService)
}
func renderText(s *state.State, ignoreStyle bool) string {

View file

@ -22,8 +22,6 @@ import (
var emptyMarks [][]*model.BlockContentTextMark
var bold = model.BlockContentTextMark_Bold
var italic = model.BlockContentTextMark_Italic
var fontRed = model.BlockContentTextMark_TextColor
func page(blocks ...*model.Block) (sb *smarttest.SmartTest) {
sb = smarttest.New("test")
@ -116,23 +114,6 @@ func shouldBe(sb *smarttest.SmartTest, t *testing.T, shouldBeBLocks ...*model.Bl
}
}
func shouldBeDebug(sb *smarttest.SmartTest, t *testing.T, shouldBeBLocks ...*model.Block) {
realBlocks := []*model.Block{}
cIds := sb.Pick("test").Model().ChildrenIds
for _, cId := range cIds {
realBlocks = append(realBlocks, sb.Pick(cId).Model())
}
fmt.Println(len(realBlocks), len(shouldBeBLocks))
for i, realBlock := range realBlocks {
fmt.Println("Real ", i, realBlock)
}
for i, b := range shouldBeBLocks {
fmt.Println("Should ", i, b)
}
}
func createBlocks(idsArr []string, textArr []string, marksArr [][]*model.BlockContentTextMark) []*model.Block {
blocks := []*model.Block{}
for i := 0; i < len(textArr); i++ {
@ -199,18 +180,6 @@ func checkBlockText(t *testing.T, sb *smarttest.SmartTest, textArr []string) {
assert.Equal(t, textArr, textArr2)
}
func checkBlockTextDebug(t *testing.T, sb *smarttest.SmartTest, textArr []string) {
for i, _ := range textArr {
fmt.Println(textArr[i])
}
fmt.Println("--------")
cIds := sb.Pick("test").Model().ChildrenIds
for _, c := range cIds {
fmt.Println("ID:", sb.Pick(c).Model().Id, "cId:", c, "Text:", sb.Pick(c).Model().GetText())
}
}
func checkBlockMarks(t *testing.T, sb *smarttest.SmartTest, marksArr [][]*model.BlockContentTextMark) {
cIds := sb.Pick("test").Model().ChildrenIds
require.Equal(t, len(cIds), len(marksArr))

View file

@ -1,6 +1,7 @@
package clipboard
import (
"errors"
"strconv"
"testing"
@ -13,6 +14,7 @@ import (
"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/editor/template"
"github.com/anyproto/anytype-heart/core/block/restriction"
"github.com/anyproto/anytype-heart/core/block/simple"
_ "github.com/anyproto/anytype-heart/core/block/simple/base"
"github.com/anyproto/anytype-heart/core/block/simple/text"
@ -617,6 +619,25 @@ func TestClipboard_TitleOps(t *testing.T) {
},
}
t.Run("paste - when base64 file", func(t *testing.T) {
// given
sb := smarttest.New("text")
require.NoError(t, smartblock.ObjectApplyTemplate(sb, nil, template.WithTitle))
sb.Doc = testutil.BuildStateFromAST(blockbuilder.Root(
blockbuilder.ID("root"),
))
// when
cb := newFixture(t, sb)
_, _, _, _, err := cb.Paste(nil, &pb.RpcBlockPasteRequest{
HtmlSlot: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=">`,
}, "")
// then
assert.Equal(t, "image", sb.Doc.Blocks()[len(sb.Doc.Blocks())-1].GetFile().Name)
require.NoError(t, err)
})
t.Run("single to empty title", func(t *testing.T) {
st := withTitle(t, "")
cb := newFixture(t, st)
@ -1058,6 +1079,20 @@ func TestClipboard_TitleOps(t *testing.T) {
require.Len(t, anySlot, 1)
assert.Equal(t, "it", anySlot[0].GetText().Text)
})
t.Run("do not paste if Blocks restriction is set to smartblock", func(t *testing.T) {
// given
sb := smarttest.New("test")
sb.SetRestrictions(restriction.Restrictions{Object: restriction.ObjectRestrictions{model.Restrictions_Blocks}})
cb := newFixture(t, sb)
// when
_, _, _, _, err := cb.Paste(nil, nil, "")
// then
assert.Error(t, err)
assert.True(t, errors.Is(err, restriction.ErrRestricted))
})
}
func addDescription(st *smarttest.SmartTest, description string) {

View file

@ -1,6 +1,8 @@
package clipboard
import (
"encoding/base64"
"errors"
"strings"
"github.com/samber/lo"
@ -15,6 +17,9 @@ import (
textutil "github.com/anyproto/anytype-heart/util/text"
)
const base64ImagePrefix = "data:image"
const base64Prefix = ";base64,"
type pasteCtrl struct {
// doc state
s *state.State
@ -310,16 +315,42 @@ func (p *pasteCtrl) removeSelection() {
}
}
func (p *pasteCtrl) processFiles() {
func (p *pasteCtrl) processFiles() (err error) {
p.ps.Iterate(func(b simple.Block) (isContinue bool) {
if file := b.Model().GetFile(); file != nil && file.State == model.BlockContentFile_Empty {
p.uploadArr = append(p.uploadArr, pb.RpcBlockUploadRequest{
BlockId: b.Model().Id,
Url: file.Name,
})
if strings.HasPrefix(file.Name, base64ImagePrefix) {
err = p.handleBase64(b, file)
if err != nil {
log.Errorf("error handling base64 image: %v", err)
}
} else {
p.uploadArr = append(p.uploadArr, pb.RpcBlockUploadRequest{
BlockId: b.Model().Id,
Url: file.Name,
})
}
}
return true
})
return
}
func (p *pasteCtrl) handleBase64(b simple.Block, file *model.BlockContentFile) error {
index := strings.Index(file.Name, base64Prefix)
if index > 0 {
file.Name = file.Name[index+len(base64Prefix):]
fileContent, err := base64.StdEncoding.DecodeString(file.Name)
if err != nil {
return err
}
file.Name = "image"
p.uploadArr = append(p.uploadArr, pb.RpcBlockUploadRequest{
BlockId: b.Model().Id,
Bytes: fileContent,
})
return nil
}
return errors.New("invalid base64 image")
}
func (p *pasteCtrl) normalize() {

View file

@ -5,7 +5,6 @@ import (
"fmt"
"github.com/globalsign/mgo/bson"
"github.com/gogo/protobuf/types"
"github.com/google/uuid"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -22,14 +21,12 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/badgerhelper"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/slice"
)
const DefaultDetailsFieldName = "_defaultRecordFields"
var log = logging.Logger("anytype-mw-editor-dataview")
var ErrMultiupdateWasNotAllowed = fmt.Errorf("multiupdate was not allowed")
type Dataview interface {
SetSource(ctx session.Context, blockId string, source []string) (err error)
@ -39,7 +36,7 @@ type Dataview interface {
GetDataview(blockID string) (*model.BlockContentDataview, error)
DeleteView(ctx session.Context, blockId string, viewId string, showEvent bool) error
SetActiveView(ctx session.Context, blockId string, activeViewId string, limit int, offset int) error
SetActiveView(ctx session.Context, blockId string, activeViewId string) error
CreateView(ctx session.Context, blockID string,
view model.BlockContentDataviewView, source []string) (*model.BlockContentDataviewView, error)
SetViewPosition(ctx session.Context, blockId string, viewId string, position uint32) error
@ -59,6 +56,7 @@ func NewDataview(sb smartblock.SmartBlock, objectStore objectstore.ObjectStore)
objectStore: objectStore,
}
sb.AddHook(dv.checkDVBlocks, smartblock.HookBeforeApply)
sb.AddHook(dv.injectActiveViews, smartblock.HookBeforeApply)
return dv
}
@ -205,7 +203,7 @@ func (d *sdataview) UpdateView(ctx session.Context, blockID string, viewID strin
return d.Apply(s, smartblock.NoEvent)
}
func (d *sdataview) SetActiveView(ctx session.Context, id string, activeViewId string, limit int, offset int) error {
func (d *sdataview) SetActiveView(ctx session.Context, id string, activeViewId string) error {
s := d.NewStateCtx(ctx)
dvBlock, err := getDataviewBlock(s, id)
@ -218,8 +216,12 @@ func (d *sdataview) SetActiveView(ctx session.Context, id string, activeViewId s
}
dvBlock.SetActiveView(activeViewId)
if err = d.objectStore.SetActiveView(d.Id(), id, activeViewId); err != nil {
return err
}
d.SmartBlock.CheckSubscriptions()
return d.Apply(s)
return d.Apply(s, smartblock.NoHooks)
}
func (d *sdataview) SetViewPosition(ctx session.Context, blockId string, viewId string, position uint32) (err error) {
@ -423,6 +425,34 @@ func (d *sdataview) checkDVBlocks(info smartblock.ApplyInfo) (err error) {
return
}
func (d *sdataview) injectActiveViews(info smartblock.ApplyInfo) (err error) {
s := info.State
views, err := d.objectStore.GetActiveViews(d.Id())
if badgerhelper.IsNotFound(err) {
return nil
}
if err != nil {
log.With("objectId", s.RootId()).Warnf("failed to get list of active views from store: %v", err)
return
}
for blockId, viewId := range views {
b := s.Pick(blockId)
if b == nil {
log.With("objectId", s.RootId()).Warnf("failed to get block '%s' to inject active view", blockId)
continue
}
dv := b.Model().GetDataview()
if dv == nil {
log.With("objectId", s.RootId()).Warnf("block '%s' is not dataview, so cannot inject active view", blockId)
continue
}
dv.ActiveView = viewId
}
return nil
}
func getDataviewBlock(s *state.State, id string) (dataview.Block, error) {
b := s.Get(id)
if b == nil {
@ -434,83 +464,6 @@ func getDataviewBlock(s *state.State, id string) (dataview.Block, error) {
return nil, fmt.Errorf("not a dataview block")
}
func getEntryID(entry database.Record) string {
if entry.Details == nil {
return ""
}
return pbtypes.GetString(entry.Details, bundle.RelationKeyId.String())
}
type recordInsertedAtPosition struct {
position int
entry *types.Struct
}
type recordsInsertedAtPosition struct {
position int
entries []*types.Struct
}
func calculateEntriesDiff(a, b []database.Record) (updated []*types.Struct, removed []string, insertedGroupedByPosition []recordsInsertedAtPosition) {
var inserted []recordInsertedAtPosition
var existing = make(map[string]*types.Struct, len(a))
for _, record := range a {
existing[getEntryID(record)] = record.Details
}
var existingInNew = make(map[string]struct{}, len(b))
for i, entry := range b {
id := getEntryID(entry)
if prev, exists := existing[id]; exists {
if len(a) <= i || getEntryID(a[i]) != id {
// todo: return as moved?
removed = append(removed, id)
inserted = append(inserted, recordInsertedAtPosition{i, entry.Details})
} else {
if !prev.Equal(entry.Details) {
updated = append(updated, entry.Details)
}
}
} else {
inserted = append(inserted, recordInsertedAtPosition{i, entry.Details})
}
existingInNew[id] = struct{}{}
}
for id := range existing {
if _, exists := existingInNew[id]; !exists {
removed = append(removed, id)
}
}
var insertedToTheLastPosition = recordsInsertedAtPosition{position: -1}
var lastPos = -1
if len(inserted) > 0 {
insertedToTheLastPosition.position = inserted[0].position
lastPos = inserted[0].position - 1
}
for _, entry := range inserted {
if entry.position > lastPos+1 {
// split the insert portion
insertedGroupedByPosition = append(insertedGroupedByPosition, insertedToTheLastPosition)
insertedToTheLastPosition = recordsInsertedAtPosition{position: entry.position}
}
lastPos = entry.position
insertedToTheLastPosition.entries = append(insertedToTheLastPosition.entries, entry.entry)
}
if len(insertedToTheLastPosition.entries) > 0 {
insertedGroupedByPosition = append(insertedGroupedByPosition, insertedToTheLastPosition)
}
return
}
func BlockBySource(objectStore objectstore.ObjectStore, source []string) (*model.BlockContentOfDataview, error) {
schema, err := SchemaBySources(source, objectStore)
if err != nil {

View file

@ -1,82 +1,52 @@
package dataview
import (
"errors"
"fmt"
"testing"
"github.com/gogo/protobuf/types"
"github.com/dgraph-io/badger/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"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/block/simple/dataview"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/mock_objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
func Test_calculateEntriesDiff(t *testing.T) {
a := []database.Record{
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id1"),
"name": pbtypes.String("name1"),
},
}},
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id2"),
"name": pbtypes.String("name2"),
},
}},
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id3"),
"name": pbtypes.String("name3"),
},
}},
const objId = "root"
type fixture struct {
store *mock_objectstore.MockObjectStore
sb *smarttest.SmartTest
*sdataview
}
func newFixture(t *testing.T) *fixture {
store := mock_objectstore.NewMockObjectStore(t)
sb := smarttest.New(objId)
dv := NewDataview(sb, store).(*sdataview)
return &fixture{
store: store,
sb: sb,
sdataview: dv,
}
b := []database.Record{
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id1"),
"name": pbtypes.String("name1_change"),
},
}},
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id2"),
"name": pbtypes.String("name2"),
},
}},
{Details: &types.Struct{
Fields: map[string]*types.Value{
"id": pbtypes.String("id4"),
"name": pbtypes.String("name4"),
},
}},
}
updated, removed, inserted := calculateEntriesDiff(a, b)
require.Len(t, updated, 1)
require.Len(t, removed, 1)
require.Len(t, inserted, 1)
require.Equal(t, b[0].Details, updated[0])
require.Equal(t, "id3", removed[0])
require.Equal(t, 2, inserted[0].position)
require.Equal(t, []*types.Struct{b[2].Details}, inserted[0].entries)
}
func TestDataviewCollectionImpl_SetViewPosition(t *testing.T) {
newTestDv := func() (Dataview, *smarttest.SmartTest) {
sb := smarttest.New("root")
sbs := sb.Doc.(*state.State)
sbs.Add(simple.New(&model.Block{Id: "root", ChildrenIds: []string{"dv"}}))
fx := newFixture(t)
sbs := fx.sb.Doc.(*state.State)
sbs.Add(simple.New(&model.Block{Id: objId, ChildrenIds: []string{"dv"}}))
sbs.Add(simple.New(&model.Block{Id: "dv", Content: &model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Views: []*model.BlockContentDataviewView{
@ -86,8 +56,8 @@ func TestDataviewCollectionImpl_SetViewPosition(t *testing.T) {
},
},
}}))
return NewDataview(sb, nil), sb
fx.store.EXPECT().GetActiveViews(mock.Anything).Return(nil, nil).Maybe()
return fx.sdataview, fx.sb
}
assertViewPositions := func(viewId string, pos uint32, exp []string) {
dv, sb := newTestDv()
@ -107,3 +77,81 @@ func TestDataviewCollectionImpl_SetViewPosition(t *testing.T) {
assertViewPositions("1", 0, []string{"1", "2", "3"})
assertViewPositions("1", 42, []string{"2", "3", "1"})
}
func TestInjectActiveView(t *testing.T) {
dv1 := "dataview1"
dv2 := "dataview2"
dv3 := "dataview3"
getInfo := func() smartblock.ApplyInfo {
st := state.NewDoc(objId, map[string]simple.Block{
objId: simple.New(&model.Block{Id: objId, ChildrenIds: []string{dv1, dv2, dv3}}),
dv1: dataview.NewDataview(&model.Block{
Id: dv1,
Content: &model.BlockContentOfDataview{Dataview: &model.BlockContentDataview{}},
}),
dv2: dataview.NewDataview(&model.Block{
Id: dv2,
Content: &model.BlockContentOfDataview{Dataview: &model.BlockContentDataview{}},
}),
dv3: dataview.NewDataview(&model.Block{
Id: dv3,
Content: &model.BlockContentOfDataview{Dataview: &model.BlockContentDataview{}},
}),
}).(*state.State)
return smartblock.ApplyInfo{State: st}
}
t.Run("inject active views to dataview blocks", func(t *testing.T) {
// given
blocksToView := map[string]string{dv1: "view1", dv2: "view2"}
fx := newFixture(t)
fx.store.EXPECT().GetActiveViews(mock.Anything).RunAndReturn(func(id string) (map[string]string, error) {
assert.Equal(t, objId, id)
return blocksToView, nil
})
info := getInfo()
// when
err := fx.injectActiveViews(info)
st := info.State
// then
assert.NoError(t, err)
assert.Equal(t, blocksToView[dv1], st.Pick(dv1).Model().GetDataview().ActiveView)
assert.Equal(t, blocksToView[dv2], st.Pick(dv2).Model().GetDataview().ActiveView)
assert.Empty(t, st.Pick(dv3).Model().GetDataview().ActiveView)
})
t.Run("do nothing if active views are not found in DB", func(t *testing.T) {
// given
fx := newFixture(t)
fx.store.EXPECT().GetActiveViews(mock.Anything).RunAndReturn(func(id string) (map[string]string, error) {
assert.Equal(t, objId, id)
return nil, badger.ErrKeyNotFound
})
info := getInfo()
// when
err := fx.injectActiveViews(info)
// then
assert.NoError(t, err)
})
t.Run("fail on other DB error", func(t *testing.T) {
// given
fx := newFixture(t)
fx.store.EXPECT().GetActiveViews(mock.Anything).RunAndReturn(func(id string) (map[string]string, error) {
assert.Equal(t, objId, id)
return nil, errors.New("badger was stolen by UFO")
})
info := getInfo()
// when
err := fx.injectActiveViews(info)
// then
assert.Error(t, err)
})
}

View file

@ -123,8 +123,10 @@ func (f *ObjectFactory) InitObject(space smartblock.Space, id string, initCtx *s
sb.SetLocker(ot)
}
// we probably don't need any locks here, because the object is initialized synchronously
initCtx.Source = sc
// adding locks as a temporary measure to find the place where we have races in our code
sb.Lock()
defer sb.Unlock()
err = sb.Init(initCtx)
if err != nil {
return nil, fmt.Errorf("init smartblock: %w", err)
@ -176,7 +178,7 @@ func (f *ObjectFactory) New(space smartblock.Space, sbType coresb.SmartBlockType
case coresb.SmartBlockTypeMissingObject:
return NewMissingObject(sb), nil
case coresb.SmartBlockTypeWidget:
return NewWidgetObject(sb, f.objectStore, f.layoutConverter, f.accountService), nil
return NewWidgetObject(sb, f.objectStore, f.layoutConverter), nil
case coresb.SmartBlockTypeNotificationObject:
return NewNotificationObject(sb), nil
case coresb.SmartBlockTypeSubObject:

View file

@ -220,6 +220,9 @@ func (sf *sfile) updateFile(ctx session.Context, id, groupId string, apply func(
}
func (sf *sfile) DropFiles(req pb.RpcFileDropRequest) (err error) {
if err = sf.Restrictions().Object.Check(model.Restrictions_Blocks); err != nil {
return err
}
proc := &dropFilesProcess{
spaceID: sf.SpaceID(),
processService: sf.processService,
@ -561,7 +564,6 @@ func (dp *dropFilesProcess) addFile(f *dropFileInfo) {
upl := dp.fileUploaderFactory.NewUploader(dp.spaceID, objectorigin.DragAndDrop())
res := upl.
SetName(f.name).
AutoType(true).
SetFile(f.path).
Upload(context.Background())

View file

@ -1,13 +1,17 @@
package file
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/anyproto/anytype-heart/core/block/cache/mock_cache"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock/smarttest"
"github.com/anyproto/anytype-heart/core/block/restriction"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/tests/blockbuilder"
@ -101,3 +105,18 @@ func TestFile(t *testing.T) {
})
}
}
func TestDropFiles(t *testing.T) {
t.Run("do not drop files to object with Blocks restriction", func(t *testing.T) {
// given
fx := newFixture(t)
fx.sb.SetRestrictions(restriction.Restrictions{Object: restriction.ObjectRestrictions{model.Restrictions_Blocks}})
// when
err := fx.sfile.DropFiles(pb.RpcFileDropRequest{})
// then
assert.Error(t, err)
assert.True(t, errors.Is(err, restriction.ErrRestricted))
})
}

View file

@ -62,7 +62,7 @@ func (f *ObjectFactory) newPage(sb smartblock.SmartBlock) *Page {
f.fileService,
f.fileObjectService,
),
Bookmark: bookmark.NewBookmark(sb, f.bookmarkService, f.objectStore),
Bookmark: bookmark.NewBookmark(sb, f.bookmarkService),
Dataview: dataview.NewDataview(sb, f.objectStore),
TableEditor: table.NewEditor(sb),
objectStore: f.objectStore,

View file

@ -56,7 +56,7 @@ func (f *ObjectFactory) newProfile(sb smartblock.SmartBlock) *Profile {
f.fileService,
f.fileObjectService,
),
Bookmark: bookmark.NewBookmark(sb, f.bookmarkService, f.objectStore),
Bookmark: bookmark.NewBookmark(sb, f.bookmarkService),
TableEditor: table.NewEditor(sb),
eventSender: f.eventSender,
fileObjectService: f.fileObjectService,

View file

@ -347,7 +347,7 @@ func (sb *smartBlock) Init(ctx *InitContext) (err error) {
}
ctx.State.AddBundledRelations(relKeys...)
if ctx.IsNewObject && ctx.State != nil {
source.NewSubObjectsAndProfileLinksMigration(sb.Type(), sb.space, sb.currentParticipantId, "", sb.objectStore).Migrate(ctx.State)
source.NewSubObjectsAndProfileLinksMigration(sb.Type(), sb.space, sb.currentParticipantId, sb.objectStore).Migrate(ctx.State)
}
if err = sb.injectLocalDetails(ctx.State); err != nil {
@ -753,7 +753,7 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
}
func (sb *smartBlock) ResetToVersion(s *state.State) (err error) {
source.NewSubObjectsAndProfileLinksMigration(sb.Type(), sb.space, sb.currentParticipantId, "", sb.objectStore).Migrate(s)
source.NewSubObjectsAndProfileLinksMigration(sb.Type(), sb.space, sb.currentParticipantId, sb.objectStore).Migrate(s)
s.SetParent(sb.Doc.(*state.State))
sb.storeFileKeys(s)
sb.injectLocalDetails(s)
@ -1195,14 +1195,6 @@ func (sb *smartBlock) AddHookOnce(id string, f HookCallback, events ...Hook) {
}
}
func (sb *smartBlock) baseRelations() []*model.Relation {
rels := []*model.Relation{bundle.MustGetRelation(bundle.RelationKeyId), bundle.MustGetRelation(bundle.RelationKeyLayout), bundle.MustGetRelation(bundle.RelationKeyIconEmoji), bundle.MustGetRelation(bundle.RelationKeyName)}
for _, rel := range rels {
rel.Scope = model.Relation_object
}
return rels
}
// deprecated, use RelationLinks instead
func (sb *smartBlock) Relations(s *state.State) relationutils.Relations {
var links []*model.RelationLink

View file

@ -14,8 +14,8 @@ import (
var (
maxChildrenThreshold = 40
blockSizeLimit = 1 * 1024 * 1024
detailSizeLimit = 65 * 1024
detailSizeLimit = 65 * 1024
)
func (s *State) Normalize(withLayouts bool) (err error) {

View file

@ -29,6 +29,9 @@ func (s *State) InsertTo(targetId string, reqPos model.BlockPosition, ids ...str
if targetId == "" {
reqPos = model.Block_Inner
target = s.Get(s.RootId())
if target == nil {
return fmt.Errorf("target (root) block not found")
}
} else {
target = s.Get(targetId)
if target == nil {

View file

@ -2596,12 +2596,3 @@ func TestState_RootId(t *testing.T) {
// assert.True(t, assertAllDetailsLessThenLimit(s.CombinedDetails()))
// })
// }
func assertAllDetailsLessThenLimit(details *types.Struct) bool {
for _, v := range details.Fields {
if v.Size() > detailSizeLimit {
return false
}
}
return true
}

View file

@ -25,8 +25,6 @@ import (
"github.com/anyproto/anytype-heart/util/slice"
)
const textSizeLimit = 64 * 1024
var setTextApplyInterval = time.Second * 3
type Text interface {

View file

@ -20,8 +20,7 @@ func init() {
func NewBlock(b *model.Block) simple.Block {
if c := b.GetTable(); c != nil {
return &block{
Base: base.NewBase(b).(*base.Base),
content: c,
Base: base.NewBase(b).(*base.Base),
}
}
return nil
@ -35,7 +34,6 @@ type Block interface {
type block struct {
*base.Base
content *model.BlockContentTable
}
func (b *block) Copy() simple.Block {

View file

@ -29,7 +29,6 @@ func NewWidgetObject(
sb smartblock.SmartBlock,
objectStore objectstore.ObjectStore,
layoutConverter converter.LayoutConverter,
accountService accountService,
) *WidgetObject {
bs := basic.NewBasic(sb, objectStore, layoutConverter)
return &WidgetObject{
@ -37,7 +36,7 @@ func NewWidgetObject(
Movable: bs,
Updatable: bs,
IHistory: basic.NewHistory(sb),
Widget: widget.NewWidget(sb, accountService),
Widget: widget.NewWidget(sb),
}
}

View file

@ -21,14 +21,8 @@ type Widget interface {
CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest) (string, error)
}
type accountService interface {
PersonalSpaceID() string
MyParticipantId(string) string
}
type widget struct {
smartblock.SmartBlock
accountService accountService
}
type ImportWidgetFlags struct {
@ -62,10 +56,9 @@ func IsPredefinedWidgetTargetId(targetID string) bool {
}
}
func NewWidget(sb smartblock.SmartBlock, accountService accountService) Widget {
func NewWidget(sb smartblock.SmartBlock) Widget {
return &widget{
SmartBlock: sb,
accountService: accountService,
SmartBlock: sb,
}
}

View file

@ -12,7 +12,6 @@ import (
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/metrics"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -25,7 +24,6 @@ type Workspaces struct {
stext.Text
spaceService spaceService
objectStore objectstore.ObjectStore
config *config.Config
migrator subObjectsMigrator
}
@ -41,7 +39,6 @@ func (f *ObjectFactory) newWorkspace(sb smartblock.SmartBlock) *Workspaces {
f.eventSender,
),
Dataview: dataview.NewDataview(sb, f.objectStore),
objectStore: f.objectStore,
spaceService: f.spaceService,
config: f.config,
}

View file

@ -11,7 +11,6 @@ import (
"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/migration"
"github.com/anyproto/anytype-heart/util/testMock"
)
func TestWorkspaces_FileInfo(t *testing.T) {
@ -47,15 +46,11 @@ func (m migratorStub) migrateSubObjects(st *state.State) {
func NewWorkspacesTest(ctrl *gomock.Controller) (*Workspaces, error) {
sb := smarttest.New("root")
objectStore := testMock.NewMockObjectStore(ctrl)
objectStore.EXPECT().GetDetails(gomock.Any()).AnyTimes()
objectStore.EXPECT().Query(gomock.Any()).AnyTimes()
a := &Workspaces{
SmartBlock: sb,
spaceService: &spaceServiceStub{},
migrator: migratorStub{},
config: &config.Config{},
objectStore: objectStore,
}
initCtx := &smartblock.InitContext{
IsNewObject: true,

View file

@ -24,7 +24,6 @@ import (
"github.com/anyproto/anytype-heart/core/block/cache"
sb "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/object/idresolver"
"github.com/anyproto/anytype-heart/core/block/process"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/converter"
@ -73,7 +72,6 @@ type export struct {
objectStore objectstore.ObjectStore
sbtProvider typeprovider.SmartBlockTypeProvider
fileService files.Service
resolver idresolver.Resolver
spaceService space.Service
accountService account.Service
notificationService notifications.Notifications
@ -88,7 +86,6 @@ func (e *export) Init(a *app.App) (err error) {
e.objectStore = a.MustComponent(objectstore.CName).(objectstore.ObjectStore)
e.fileService = app.MustComponent[files.Service](a)
e.picker = app.MustComponent[cache.ObjectGetter](a)
e.resolver = a.MustComponent(idresolver.CName).(idresolver.Resolver)
e.sbtProvider = app.MustComponent[typeprovider.SmartBlockTypeProvider](a)
e.spaceService = app.MustComponent[space.Service](a)
e.accountService = app.MustComponent[account.Service](a)
@ -665,7 +662,6 @@ func validType(sbType smartblock.SmartBlockType) bool {
sbType == smartblock.SmartBlockTypePage ||
sbType == smartblock.SmartBlockTypeSubObject ||
sbType == smartblock.SmartBlockTypeTemplate ||
sbType == smartblock.SmartBlockTypeDate ||
sbType == smartblock.SmartBlockTypeWorkspace ||
sbType == smartblock.SmartBlockTypeWidget ||
sbType == smartblock.SmartBlockTypeObjectType ||
@ -692,32 +688,33 @@ func (e *export) cleanupFile(wr writer) {
}
func (e *export) getRelatedDerivedObjects(objects map[string]*types.Struct) ([]database.Record, error) {
derivedObjects, err := e.iterateObjects(objects)
derivedObjects, typesAndTemplates, err := e.iterateObjects(objects)
if err != nil {
return nil, err
}
if len(derivedObjects) > 0 {
// get derived objects only from types and templates,
// because relations currently have only system relations and object type
if len(typesAndTemplates) > 0 {
derivedObjectsMap := make(map[string]*types.Struct, 0)
for _, object := range derivedObjects {
for _, object := range typesAndTemplates {
id := object.Get(bundle.RelationKeyId.String()).GetStringValue()
derivedObjectsMap[id] = object.Details
}
iteratedObjects, err := e.iterateObjects(derivedObjectsMap)
iteratedObjects, typesAndTemplates, err := e.iterateObjects(derivedObjectsMap)
if err != nil {
return nil, err
}
derivedObjects = append(derivedObjects, iteratedObjects...)
derivedObjects = append(derivedObjects, typesAndTemplates...)
}
return derivedObjects, nil
}
func (e *export) iterateObjects(objects map[string]*types.Struct) ([]database.Record, error) {
var (
derivedObjects []database.Record
relations []string
)
func (e *export) iterateObjects(objects map[string]*types.Struct,
) (allObjects []database.Record, typesAndTemplates []database.Record, err error) {
var relations []string
for id, object := range objects {
err := cache.Do(e.picker, id, func(b sb.SmartBlock) error {
err = cache.Do(e.picker, id, func(b sb.SmartBlock) error {
state := b.NewState()
relations = e.getObjectRelations(state, relations)
details := state.Details()
@ -731,14 +728,14 @@ func (e *export) iterateObjects(objects map[string]*types.Struct) ([]database.Re
return nil
})
if err != nil {
return nil, err
return nil, nil, err
}
derivedObjects, err = e.processObject(object, derivedObjects, relations)
allObjects, typesAndTemplates, err = e.processObject(object, allObjects, typesAndTemplates, relations)
if err != nil {
return nil, err
return nil, nil, err
}
}
return derivedObjects, nil
return allObjects, typesAndTemplates, nil
}
func (e *export) getDataviewRelations(state *state.State) ([]string, error) {
@ -771,60 +768,69 @@ func (e *export) isObjectWithDataview(details *types.Struct) bool {
func (e *export) processObject(object *types.Struct,
derivedObjects []database.Record,
typesAndTemplates []database.Record,
relations []string,
) ([]database.Record, error) {
) ([]database.Record, []database.Record, error) {
for _, relation := range relations {
storeRelation, err := e.getRelation(relation)
if err != nil {
return nil, err
return nil, nil, err
}
if storeRelation != nil {
derivedObjects, err = e.addRelationAndOptions(storeRelation, object, derivedObjects, relation)
derivedObjects, err = e.addRelationAndOptions(storeRelation, derivedObjects, relation)
if err != nil {
return nil, err
return nil, nil, err
}
}
}
objectTypeId := pbtypes.GetString(object, bundle.RelationKeyType.String())
derivedObjects, err := e.addObjectType(objectTypeId, derivedObjects)
var err error
derivedObjects, typesAndTemplates, err = e.addObjectType(objectTypeId, derivedObjects, typesAndTemplates)
if err != nil {
return nil, err
return nil, nil, err
}
derivedObjects, err = e.addTemplates(objectTypeId, derivedObjects)
derivedObjects, typesAndTemplates, err = e.addTemplates(objectTypeId, derivedObjects, typesAndTemplates)
if err != nil {
return nil, err
return nil, nil, err
}
return e.handleSetOfRelation(object, derivedObjects)
derivedObjects, err = e.handleSetOfRelation(object, derivedObjects)
if err != nil {
return nil, nil, err
}
return derivedObjects, typesAndTemplates, nil
}
func (e *export) addObjectType(objectTypeId string, derivedObjects []database.Record) ([]database.Record, error) {
func (e *export) addObjectType(objectTypeId string, derivedObjects []database.Record, typesAndTemplates []database.Record) ([]database.Record, []database.Record, error) {
objectTypeDetails, err := e.objectStore.GetDetails(objectTypeId)
if err != nil {
return nil, err
return nil, nil, err
}
if objectTypeDetails == nil || objectTypeDetails.Details == nil || len(objectTypeDetails.Details.Fields) == 0 {
return derivedObjects, nil
return derivedObjects, typesAndTemplates, nil
}
uniqueKey := pbtypes.GetString(objectTypeDetails.Details, bundle.RelationKeyUniqueKey.String())
key, err := domain.GetTypeKeyFromRawUniqueKey(uniqueKey)
if err != nil {
return nil, err
return nil, nil, err
}
if bundle.IsInternalType(key) {
return derivedObjects, nil
return derivedObjects, typesAndTemplates, nil
}
recommendedRelations := pbtypes.GetStringList(objectTypeDetails.Details, bundle.RelationKeyRecommendedRelations.String())
for _, relation := range recommendedRelations {
if relation == addr.MissingObject {
continue
}
details, err := e.objectStore.GetDetails(relation)
if err != nil {
return nil, err
return nil, nil, err
}
relationKey := pbtypes.GetString(details.Details, bundle.RelationKeyUniqueKey.String())
uniqueKey, err := domain.UnmarshalUniqueKey(relationKey)
if err != nil {
return nil, err
return nil, nil, err
}
if bundle.IsSystemRelation(domain.RelationKey(uniqueKey.InternalKey())) {
continue
@ -832,7 +838,8 @@ func (e *export) addObjectType(objectTypeId string, derivedObjects []database.Re
derivedObjects = append(derivedObjects, database.Record{Details: details.Details})
}
derivedObjects = append(derivedObjects, database.Record{Details: objectTypeDetails.Details})
return derivedObjects, nil
typesAndTemplates = append(typesAndTemplates, database.Record{Details: objectTypeDetails.Details})
return derivedObjects, typesAndTemplates, nil
}
func (e *export) getRelation(key string) (*database.Record, error) {
@ -868,17 +875,15 @@ func (e *export) getRelation(key string) (*database.Record, error) {
return &relation[0], nil
}
func (e *export) addRelationAndOptions(relation *database.Record, object *types.Struct, derivedObjects []database.Record, relationKey string) ([]database.Record, error) {
func (e *export) addRelationAndOptions(relation *database.Record, derivedObjects []database.Record, relationKey string) ([]database.Record, error) {
derivedObjects = e.addRelation(*relation, derivedObjects)
format := pbtypes.GetInt64(relation.Details, bundle.RelationKeyRelationFormat.String())
if format == int64(model.RelationFormat_tag) || format == int64(model.RelationFormat_status) {
if value := pbtypes.Get(object, relationKey); value != nil {
relationOptions, err := e.getRelationOptions(value)
if err != nil {
return nil, err
}
derivedObjects = append(derivedObjects, relationOptions...)
relationOptions, err := e.getRelationOptions(relationKey)
if err != nil {
return nil, err
}
derivedObjects = append(derivedObjects, relationOptions...)
}
return derivedObjects, nil
@ -893,20 +898,19 @@ func (e *export) addRelation(relation database.Record, derivedObjects []database
return derivedObjects
}
func (e *export) getRelationOptions(relationOptions *types.Value) ([]database.Record, error) {
var filter *model.BlockContentDataviewFilter
if relationOptions.GetStringValue() != "" {
filter = e.getFilterForStringOption(relationOptions, filter)
}
if relationOptions.GetListValue() != nil && len(relationOptions.GetListValue().Values) != 0 {
filter = e.getFilterForOptionsList(relationOptions, filter)
}
if filter == nil {
return nil, nil
}
func (e *export) getRelationOptions(relationKey string) ([]database.Record, error) {
relationOptionsDetails, err := e.objectStore.Query(database.Query{
Filters: []*model.BlockContentDataviewFilter{
filter,
{
RelationKey: bundle.RelationKeyLayout.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Int64(int64(model.ObjectType_relationOption)),
},
{
RelationKey: bundle.RelationKeyRelationKey.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.String(relationKey),
},
{
RelationKey: bundle.RelationKeyIsArchived.String(),
Condition: model.BlockContentDataviewFilter_Equal,
@ -925,30 +929,7 @@ func (e *export) getRelationOptions(relationOptions *types.Value) ([]database.Re
return relationOptionsDetails, nil
}
func (e *export) getFilterForOptionsList(relationOptions *types.Value, filter *model.BlockContentDataviewFilter) *model.BlockContentDataviewFilter {
ids := make([]string, 0, len(relationOptions.GetListValue().Values))
for _, id := range relationOptions.GetListValue().Values {
ids = append(ids, id.GetStringValue())
}
filter = &model.BlockContentDataviewFilter{
RelationKey: bundle.RelationKeyId.String(),
Condition: model.BlockContentDataviewFilter_In,
Value: pbtypes.StringList(ids),
}
return filter
}
func (e *export) getFilterForStringOption(value *types.Value, filter *model.BlockContentDataviewFilter) *model.BlockContentDataviewFilter {
id := value.GetStringValue()
filter = &model.BlockContentDataviewFilter{
RelationKey: bundle.RelationKeyId.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.String(id),
}
return filter
}
func (e *export) addTemplates(id string, derivedObjects []database.Record) ([]database.Record, error) {
func (e *export) addTemplates(id string, derivedObjects []database.Record, typesAndTemplates []database.Record) ([]database.Record, []database.Record, error) {
templates, err := e.objectStore.Query(database.Query{
Filters: []*model.BlockContentDataviewFilter{
{
@ -969,10 +950,11 @@ func (e *export) addTemplates(id string, derivedObjects []database.Record) ([]da
},
})
if err != nil {
return nil, err
return nil, nil, err
}
derivedObjects = append(derivedObjects, templates...)
return derivedObjects, nil
typesAndTemplates = append(typesAndTemplates, templates...)
return derivedObjects, typesAndTemplates, nil
}
func (e *export) handleSetOfRelation(object *types.Struct, derivedObjects []database.Record) ([]database.Record, error) {

View file

@ -15,6 +15,7 @@ import (
"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/addr"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space/spacecore/typeprovider/mock_typeprovider"
@ -184,7 +185,6 @@ func Test_docsForExport(t *testing.T) {
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New("id")
smartBlockRelation := smarttest.New("key")
doc := smartBlockTest.NewState().SetDetails(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyId.String(): pbtypes.String("id"),
@ -201,7 +201,279 @@ func Test_docsForExport(t *testing.T) {
smartBlockTest.Doc = doc
objectGetter.EXPECT().GetObject(context.Background(), "id").Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), "key").Return(smartBlockRelation, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
}
// when
docsForExport, err := e.docsForExport("spaceId", pb.RpcObjectListExportRequest{
SpaceId: "spaceId",
ObjectIds: []string{"id"},
Format: model.Export_Protobuf,
})
// then
assert.Nil(t, err)
assert.Equal(t, 2, len(docsForExport))
})
t.Run("get relation options - no relation options", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
relationKey := "key"
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relationKey)
assert.Nil(t, err)
storeFixture.AddObjects(t, []objectstore.TestObject{
{
bundle.RelationKeyId: pbtypes.String("id"),
domain.RelationKey(relationKey): pbtypes.String("value"),
bundle.RelationKeyType: pbtypes.String("objectType"),
},
{
bundle.RelationKeyId: pbtypes.String(relationKey),
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
bundle.RelationKeyUniqueKey: pbtypes.String(uniqueKey.Marshal()),
bundle.RelationKeyRelationFormat: pbtypes.Int64(int64(model.RelationFormat_status)),
},
})
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New("id")
doc := smartBlockTest.NewState().SetDetails(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyId.String(): pbtypes.String("id"),
relationKey: pbtypes.String("value"),
bundle.RelationKeyType.String(): pbtypes.String("objectType"),
}})
doc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: relationKey,
Format: model.RelationFormat_tag,
})
smartBlockTest.Doc = doc
objectGetter.EXPECT().GetObject(context.Background(), "id").Return(smartBlockTest, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
}
// when
docsForExport, err := e.docsForExport("spaceId", pb.RpcObjectListExportRequest{
SpaceId: "spaceId",
ObjectIds: []string{"id"},
Format: model.Export_Protobuf,
})
// then
assert.Nil(t, err)
assert.Equal(t, 2, len(docsForExport))
})
t.Run("get relation options - 1 relation option", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
relationKey := "key"
optionId := "optionId"
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relationKey)
assert.Nil(t, err)
optionUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelationOption, optionId)
assert.Nil(t, err)
storeFixture.AddObjects(t, []objectstore.TestObject{
{
bundle.RelationKeyId: pbtypes.String("id"),
domain.RelationKey(relationKey): pbtypes.String(optionId),
bundle.RelationKeyType: pbtypes.String("objectType"),
},
{
bundle.RelationKeyId: pbtypes.String(relationKey),
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
bundle.RelationKeyUniqueKey: pbtypes.String(uniqueKey.Marshal()),
bundle.RelationKeyRelationFormat: pbtypes.Int64(int64(model.RelationFormat_tag)),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relation)),
},
{
bundle.RelationKeyId: pbtypes.String(optionId),
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
bundle.RelationKeyUniqueKey: pbtypes.String(optionUniqueKey.Marshal()),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relationOption)),
},
})
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New("id")
doc := smartBlockTest.NewState().SetDetails(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyId.String(): pbtypes.String("id"),
relationKey: pbtypes.String("value"),
bundle.RelationKeyType.String(): pbtypes.String("objectType"),
}})
doc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: relationKey,
Format: model.RelationFormat_tag,
})
smartBlockTest.Doc = doc
objectGetter.EXPECT().GetObject(context.Background(), "id").Return(smartBlockTest, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
}
// when
docsForExport, err := e.docsForExport("spaceId", pb.RpcObjectListExportRequest{
SpaceId: "spaceId",
ObjectIds: []string{"id"},
Format: model.Export_Protobuf,
})
// then
assert.Nil(t, err)
assert.Equal(t, 3, len(docsForExport))
var objectIds []string
for objectId := range docsForExport {
objectIds = append(objectIds, objectId)
}
assert.Contains(t, objectIds, optionId)
})
t.Run("get derived objects - relation, object type with recommended relations, template with link", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
relationKey := "key"
objectTypeKey := "customObjectType"
objectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, objectTypeKey)
assert.Nil(t, err)
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relationKey)
assert.Nil(t, err)
recommendedRelationKey := "recommendedRelationKey"
recommendedRelationUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, recommendedRelationKey)
assert.Nil(t, err)
templateId := "templateId"
linkedObjectId := "linkedObjectId"
storeFixture.AddObjects(t, []objectstore.TestObject{
{
bundle.RelationKeyId: pbtypes.String("id"),
domain.RelationKey(relationKey): pbtypes.String("test"),
bundle.RelationKeyType: pbtypes.String(objectTypeKey),
},
{
bundle.RelationKeyId: pbtypes.String(relationKey),
bundle.RelationKeyRelationKey: pbtypes.String(relationKey),
bundle.RelationKeyUniqueKey: pbtypes.String(uniqueKey.Marshal()),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relation)),
},
{
bundle.RelationKeyId: pbtypes.String(objectTypeKey),
bundle.RelationKeyUniqueKey: pbtypes.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeyRecommendedRelations: pbtypes.StringList([]string{recommendedRelationKey}),
},
{
bundle.RelationKeyId: pbtypes.String(recommendedRelationKey),
bundle.RelationKeyRelationKey: pbtypes.String(recommendedRelationKey),
bundle.RelationKeyUniqueKey: pbtypes.String(recommendedRelationUniqueKey.Marshal()),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_relation)),
},
{
bundle.RelationKeyId: pbtypes.String(templateId),
bundle.RelationKeyTargetObjectType: pbtypes.String(objectTypeKey),
},
{
bundle.RelationKeyId: pbtypes.String(linkedObjectId),
bundle.RelationKeyType: pbtypes.String(objectTypeKey),
},
})
err = storeFixture.UpdateObjectLinks(templateId, []string{linkedObjectId})
assert.Nil(t, err)
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New("id")
smartBlockTemplate := smarttest.New(templateId)
smartBlockObjectType := smarttest.New(objectTypeKey)
doc := smartBlockTest.NewState().SetDetails(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyId.String(): pbtypes.String("id"),
relationKey: pbtypes.String("value"),
bundle.RelationKeyType.String(): pbtypes.String("objectType"),
}})
doc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: relationKey,
Format: model.RelationFormat_tag,
})
smartBlockTest.Doc = doc
objectGetter.EXPECT().GetObject(context.Background(), "id").Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), templateId).Return(smartBlockTemplate, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeKey).Return(smartBlockObjectType, nil)
provider := mock_typeprovider.NewMockSmartBlockTypeProvider(t)
provider.EXPECT().Type("spaceId", linkedObjectId).Return(smartblock.SmartBlockTypePage, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
sbtProvider: provider,
}
// when
docsForExport, err := e.docsForExport("spaceId", pb.RpcObjectListExportRequest{
SpaceId: "spaceId",
ObjectIds: []string{"id"},
Format: model.Export_Protobuf,
IncludeNested: true,
})
// then
assert.Nil(t, err)
assert.Equal(t, 6, len(docsForExport))
})
t.Run("get derived objects, object type have missing relations - return only object and its type", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
objectTypeKey := "customObjectType"
objectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, objectTypeKey)
assert.Nil(t, err)
storeFixture.AddObjects(t, []objectstore.TestObject{
{
bundle.RelationKeyId: pbtypes.String("id"),
bundle.RelationKeyType: pbtypes.String(objectTypeKey),
},
{
bundle.RelationKeyId: pbtypes.String(objectTypeKey),
bundle.RelationKeyUniqueKey: pbtypes.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: pbtypes.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeyRecommendedRelations: pbtypes.StringList([]string{addr.MissingObject}),
},
})
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New("id")
smartBlockObjectType := smarttest.New(objectTypeKey)
objectGetter.EXPECT().GetObject(context.Background(), "id").Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeKey).Return(smartBlockObjectType, nil)
e := &export{
objectStore: storeFixture,

View file

@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"sync"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/gogo/protobuf/types"
@ -29,7 +28,6 @@ import (
"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/addr"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
@ -59,16 +57,13 @@ type ObjectCreator struct {
objectStore objectstore.ObjectStore
relationSyncer *syncer.FileRelationSyncer
syncFactory *syncer.Factory
fileStore filestore.FileStore
objectCreator objectcreator.Service
mu sync.Mutex
}
func New(service BlockService,
syncFactory *syncer.Factory,
objectStore objectstore.ObjectStore,
relationSyncer *syncer.FileRelationSyncer,
fileStore filestore.FileStore,
spaceService space.Service,
objectCreator objectcreator.Service,
) Service {
@ -77,7 +72,6 @@ func New(service BlockService,
syncFactory: syncFactory,
objectStore: objectStore,
relationSyncer: relationSyncer,
fileStore: fileStore,
spaceService: spaceService,
objectCreator: objectCreator,
}

View file

@ -31,7 +31,7 @@ func TestObjectCreator_Create(t *testing.T) {
mockSpace := mock_clientspace.NewMockSpace(t)
mockSpace.EXPECT().IsReadOnly().Return(true)
mockService.EXPECT().Get(context.Background(), spaceID).Return(mockSpace, nil)
service := New(blockService, nil, nil, nil, nil, mockService, objectcreator.NewCreator())
service := New(blockService, nil, nil, nil, mockService, objectcreator.NewCreator())
importedSpaceId := "importedSpaceID"
identity := "identity"

View file

@ -50,13 +50,12 @@ func NewDataObject(ctx context.Context,
}
type Task struct {
spaceID string
sn *common.Snapshot
oc Service
sn *common.Snapshot
oc Service
}
func NewTask(spaceID string, sn *common.Snapshot, oc Service) *Task {
return &Task{sn: sn, oc: oc, spaceID: spaceID}
func NewTask(sn *common.Snapshot, oc Service) *Task {
return &Task{sn: sn, oc: oc}
}
func (t *Task) Execute(data interface{}) interface{} {

View file

@ -10,8 +10,6 @@ import (
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/import/common"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/core/files/fileobject"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -19,9 +17,7 @@ import (
type fileObject struct {
treeObject *treeObject
blockService *block.Service
fileService files.Service
fileObjectService fileobject.Service
blockService *block.Service
}
func (o *fileObject) GetIDAndPayload(ctx context.Context, spaceId string, sn *common.Snapshot, timestamp time.Time, getExisting bool, origin objectorigin.ObjectOrigin) (string, treestorage.TreeStorageCreatePayload, error) {

View file

@ -10,7 +10,6 @@ import (
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/import/common"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/core/files/fileobject"
sb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
@ -26,7 +25,6 @@ type IDProvider interface {
}
type Provider struct {
objectStore objectstore.ObjectStore
idProviderBySmartBlockType map[sb.SmartBlockType]IDProvider
}
@ -36,20 +34,16 @@ func NewIDProvider(
blockService *block.Service,
fileStore filestore.FileStore,
fileObjectService fileobject.Service,
fileService files.Service,
) IDProvider {
p := &Provider{
objectStore: objectStore,
idProviderBySmartBlockType: make(map[sb.SmartBlockType]IDProvider, 0),
}
existingObject := newExistingObject(objectStore)
treeObject := newTreeObject(existingObject, spaceService)
derivedObject := newDerivedObject(existingObject, spaceService)
fileObject := &fileObject{
treeObject: treeObject,
blockService: blockService,
fileService: fileService,
fileObjectService: fileObjectService,
treeObject: treeObject,
blockService: blockService,
}
oldFile := &oldFile{
blockService: blockService,

View file

@ -14,30 +14,22 @@ import (
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/files/fileobject"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
oserror "github.com/anyproto/anytype-heart/util/os"
)
type FileSyncer struct {
service *block.Service
objectStore objectstore.ObjectStore
fileStore filestore.FileStore
fileObjectService fileobject.Service
}
func NewFileSyncer(
service *block.Service,
fileStore filestore.FileStore,
fileObjectService fileobject.Service,
objectStore objectstore.ObjectStore,
) *FileSyncer {
return &FileSyncer{
service: service,
fileStore: fileStore,
fileObjectService: fileObjectService,
objectStore: objectStore,
}
}

View file

@ -34,7 +34,6 @@ import (
"github.com/anyproto/anytype-heart/core/block/process"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/core/files/fileobject"
"github.com/anyproto/anytype-heart/core/filestorage/filesync"
"github.com/anyproto/anytype-heart/metrics"
@ -95,12 +94,11 @@ func (i *Import) Init(a *app.App) (err error) {
store := app.MustComponent[objectstore.ObjectStore](a)
i.fileStore = app.MustComponent[filestore.FileStore](a)
fileObjectService := app.MustComponent[fileobject.Service](a)
fileService := app.MustComponent[files.Service](a)
i.idProvider = objectid.NewIDProvider(store, spaceService, i.s, i.fileStore, fileObjectService, fileService)
factory := syncer.New(syncer.NewFileSyncer(i.s, i.fileStore, fileObjectService, store), syncer.NewBookmarkSyncer(i.s), syncer.NewIconSyncer(i.s, fileObjectService))
i.idProvider = objectid.NewIDProvider(store, spaceService, i.s, i.fileStore, fileObjectService)
factory := syncer.New(syncer.NewFileSyncer(i.s, fileObjectService), syncer.NewBookmarkSyncer(i.s), syncer.NewIconSyncer(i.s, fileObjectService))
relationSyncer := syncer.NewFileRelationSyncer(i.s, fileObjectService)
objectCreator := app.MustComponent[objectcreator.Service](a)
i.oc = creator.New(i.s, factory, store, relationSyncer, i.fileStore, spaceService, objectCreator)
i.oc = creator.New(i.s, factory, store, relationSyncer, spaceService, objectCreator)
i.fileSync = app.MustComponent[filesync.FileSync](a)
return nil
}
@ -322,7 +320,7 @@ func (i *Import) createObjects(ctx context.Context,
do := creator.NewDataObject(ctx, oldIDToNew, createPayloads, filesIDs, origin, req.SpaceId)
pool := workerpool.NewPool(numWorkers)
progress.SetProgressMessage("Create objects")
go i.addWork(req.SpaceId, res, pool)
go i.addWork(res, pool)
go pool.Start(do)
details := i.readResultFromPool(pool, req.Mode, allErrors, progress)
return details, oldIDToNew[res.RootCollectionID]
@ -436,9 +434,9 @@ func (i *Import) getObjectID(
return nil
}
func (i *Import) addWork(spaceID string, res *common.Response, pool *workerpool.WorkerPool) {
func (i *Import) addWork(res *common.Response, pool *workerpool.WorkerPool) {
for _, snapshot := range res.Snapshots {
t := creator.NewTask(spaceID, snapshot, i.oc)
t := creator.NewTask(snapshot, i.oc)
stop := pool.AddWork(t)
if stop {
break

View file

@ -41,14 +41,12 @@ type blocksRenderer struct {
inTable bool
listParentID string
listNestIsNum []bool
listNestLevel uint
}
func newBlocksRenderer(baseFilepath string, allFileShortPaths []string, inTable bool) *blocksRenderer {
return &blocksRenderer{
baseFilepath: baseFilepath,
allFileShortPaths: allFileShortPaths,
listNestLevel: 0,
inTable: inTable,
}
}
@ -159,10 +157,8 @@ func (r *blocksRenderer) addChildID(cID string) {
func (r *blocksRenderer) SetListState(entering bool, isNumbered bool) {
if entering {
r.listNestIsNum = append(r.listNestIsNum, isNumbered)
r.listNestLevel++
} else if len(r.listNestIsNum) > 0 {
r.listNestIsNum = r.listNestIsNum[:len(r.listNestIsNum)-1]
r.listNestLevel--
}
if len(r.listNestIsNum) > 1 {

View file

@ -7,6 +7,8 @@ import (
"github.com/globalsign/mgo/bson"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/assert"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
@ -235,3 +237,54 @@ func TestCloseTextBlock(t *testing.T) {
assert.Equal(t, "id2", renderer.blocks[1].ChildrenIds[0])
})
}
func Test_renderCodeBloc(t *testing.T) {
t.Run("simple case", func(t *testing.T) {
// given
r := NewRenderer(newBlocksRenderer("", nil, false))
node := ast.NewCodeBlock()
segments := text.NewSegments()
segments.Append(text.Segment{
Start: 0,
Stop: 4,
})
node.SetLines(segments)
// when
_, err := r.renderCodeBlock(nil, []byte("test"), node, true)
assert.Nil(t, err)
_, err = r.renderCodeBlock(nil, []byte("test"), node, false)
// then
assert.Nil(t, err)
assert.Len(t, r.blocks, 1)
assert.Equal(t, "test", r.blocks[0].GetText().GetText())
assert.Equal(t, r.blocks[0].GetText().GetStyle(), model.BlockContentText_Code)
})
t.Run("2 lines", func(t *testing.T) {
// given
r := NewRenderer(newBlocksRenderer("", nil, false))
node := ast.NewCodeBlock()
segments := text.NewSegments()
segments.Append(text.Segment{
Start: 0,
Stop: 5,
})
segments.Append(text.Segment{
Start: 5,
Stop: 8,
})
node.SetLines(segments)
// when
_, err := r.renderCodeBlock(nil, []byte("testtest"), node, true)
assert.Nil(t, err)
_, err = r.renderCodeBlock(nil, []byte("testtest"), node, false)
// then
assert.Nil(t, err)
assert.Len(t, r.blocks, 1)
assert.Equal(t, "testtest", r.blocks[0].GetText().GetText())
assert.Equal(t, model.BlockContentText_Code, r.blocks[0].GetText().GetStyle())
})
}

View file

@ -14,11 +14,6 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
var (
pasteCmdArgs = "pbpaste"
copyCmdArgs = "pbcopy"
)
type TestCase struct {
Blocks []map[string]interface{} `json:"blocks"`
HTML string `json:"html"`

View file

@ -192,11 +192,8 @@ func getCustomHTMLRules() []html2md.Rule {
img := html2md.Rule{
Filter: []string{"img"},
Replacement: func(content string, selec *goquery.Selection, options *html2md.Options) *string {
var (
src, title string
ok bool
)
if src, ok = selec.Attr("src"); !ok {
var src, title string
if src = extractImageSource(selec); src == "" {
return nil
}
@ -233,6 +230,19 @@ func getCustomHTMLRules() []html2md.Rule {
simpleText, blockquote, italic, code, bdo, div, img, table}
}
func extractImageSource(selec *goquery.Selection) string {
var (
src string
ok bool
)
if src, ok = selec.Attr("src"); !ok || src == "" {
if src, ok = selec.Attr("data-src"); !ok || src == "" {
return ""
}
}
return src
}
func addHeaderRow(content string, numberOfCells int, numberOfRows int) string {
numberOfColumns := numberOfCells / numberOfRows

View file

@ -121,6 +121,9 @@ func (r *Renderer) renderCodeBlock(_ util.BufWriter,
n ast.Node,
entering bool) (ast.WalkStatus, error) {
r.openTextBlockWithStyle(entering, model.BlockContentText_Code, nil)
if entering {
r.writeLines(source, n)
}
return ast.WalkContinue, nil
}

View file

@ -217,8 +217,23 @@
"blocks": [{"id":"1","Content":{"file":{"name":"http://static.com/image.png","type":2}}}]
},
{
"desc": "section linm",
"desc": "section link",
"html": "<img class=\"logo-icon\" src=\"#description\" alt=\"\">",
"blocks": [{"id":"1","Content":{"file":{"name":"http://test.com/test#description","type":2}}}]
},
{
"desc": "image with data-src atr",
"html": "<img class=\"logo-icon\" data-src=\"/static/image.png\" alt=\"\">",
"blocks": [{"id":"1","Content":{"file":{"name":"http://test.com/static/image.png","type":2}}}]
},
{
"desc": "image without src",
"html": "<img class=\"logo-icon\" alt=\"\">",
"blocks": null
},
{
"desc": "image with empty src",
"html": "<img class=\"logo-icon\" src=\"\" data-src=\"\" alt=\"\">",
"blocks": null
}
]

View file

@ -36,31 +36,6 @@ var (
'\u205F': struct{}{}, // Medium mathemtical space.
'\u3000': struct{}{}, // Ideographic space.
}
// Whitespace line replacement character map.
unicodeWhitespaceLineRepl = map[rune]struct{}{
'\u0009': struct{}{}, // Character tabulation (HT.)
'\u000A': struct{}{}, // Line feed.
'\u0020': struct{}{}, // Space.
'\u00A0': struct{}{}, // No-break space.
'\u180E': struct{}{}, // Mongolian vowel separator.
'\u2000': struct{}{}, // En quad.
'\u2001': struct{}{}, // Em quad.
'\u2002': struct{}{}, // En space.
'\u2003': struct{}{}, // Em space.
'\u2004': struct{}{}, // Three-per-em space.
'\u2005': struct{}{}, // Four-per-em space.
'\u2006': struct{}{}, // Six-per-em space.
'\u2007': struct{}{}, // Figure space.
'\u2008': struct{}{}, // Punctuation space.
'\u2009': struct{}{}, // Thin space.
'\u200A': struct{}{}, // Hair space.
'\u2028': struct{}{}, // Line separator.
'\u2029': struct{}{}, // Paragraph separator.
'\u202F': struct{}{}, // Narrow no-break space.
'\u205F': struct{}{}, // Medium mathemtical space.
'\u3000': struct{}{}, // Ideographic space.
}
)
// Normalize string.

View file

@ -28,7 +28,6 @@ const (
type Service struct {
blockService *block.Service
client *client.Client
propertyService *property.Service
}
@ -36,7 +35,6 @@ type Service struct {
func New(client *client.Client) *Service {
return &Service{
blockService: block.New(client),
client: client,
propertyService: property.New(client),
}
}

View file

@ -57,7 +57,7 @@ func Test_handlePagePropertiesSelect(t *testing.T) {
assert.Equal(t, options[0].Details.Fields[bundle.RelationKeyRelationOptionColor.String()], pbtypes.String("blue"))
}
//Relation already exist
// Relation already exist
selectProperty = property.SelectItem{
Object: "",
ID: "id",
@ -277,7 +277,7 @@ func Test_handlePagePropertiesStatus(t *testing.T) {
assert.Equal(t, options[0].Details.Fields[bundle.RelationKeyRelationOptionColor.String()], pbtypes.String("pink"))
}
//Relation already exist
// Relation already exist
statusProperty = property.StatusItem{
ID: "id",
Type: property.PropertyConfigStatus,
@ -304,6 +304,40 @@ func Test_handlePagePropertiesStatus(t *testing.T) {
}
}
func Test_handlePageProperties(t *testing.T) {
t.Run("empty status property", func(t *testing.T) {
c := client.NewClient()
details := make(map[string]*types.Value, 0)
statusProperty := property.StatusItem{
ID: "id",
Type: property.PropertyConfigStatus,
}
properties := property.Properties{"Status": &statusProperty}
pageTask := Task{
propertyService: property.New(c),
relationOptCreateMutex: &sync.Mutex{},
relationCreateMutex: &sync.Mutex{},
p: Page{Properties: properties},
}
req := &property.PropertiesStore{
PropertyIdsToSnapshots: map[string]*model.SmartBlockSnapshotBase{},
RelationsIdsToOptions: map[string][]*model.SmartBlockSnapshotBase{},
}
do := &DataObject{
request: &api.NotionImportContext{},
relations: req,
}
snapshots, _ := pageTask.handlePageProperties(do, details)
assert.Len(t, snapshots, 1) // 1 relation without option
assert.Len(t, req.PropertyIdsToSnapshots, 1)
assert.NotEmpty(t, req.PropertyIdsToSnapshots["id"])
key := pbtypes.GetString(req.PropertyIdsToSnapshots["id"].Details, bundle.RelationKeyRelationKey.String())
assert.NotEmpty(t, details[key])
})
}
func Test_handlePagePropertiesNumber(t *testing.T) {
c := client.NewClient()
details := make(map[string]*types.Value, 0)
@ -384,7 +418,7 @@ func Test_handlePagePropertiesMultiSelect(t *testing.T) {
assert.Equal(t, options[0].Details.Fields[bundle.RelationKeyRelationOptionColor.String()], pbtypes.String("blue"))
}
//Relation already exist
// Relation already exist
multiSelectProperty = property.MultiSelectItem{
ID: "id",
Type: string(property.PropertyConfigTypeMultiSelect),

View file

@ -437,7 +437,7 @@ func selectItemOptions(property *property.SelectItem, rel string, relation *prop
}
func statusItemOptions(property *property.StatusItem, rel string, relation *property.PropertiesStore) *model.SmartBlockSnapshotBase {
if property.Status.Name == "" {
if property.Status == nil || property.Status.Name == "" {
return nil
}
exist, optionID := isOptionAlreadyExist(property.Status.Name, rel, relation)

View file

@ -586,7 +586,11 @@ type Status struct {
}
func (sp *StatusItem) SetDetail(key string, details map[string]*types.Value) {
details[key] = pbtypes.StringList([]string{sp.Status.ID})
if sp.Status != nil {
details[key] = pbtypes.StringList([]string{sp.Status.ID})
} else {
details[key] = pbtypes.StringList([]string{})
}
}
func (sp *StatusItem) GetPropertyType() ConfigType {

View file

@ -11,7 +11,6 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/restriction"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
@ -58,10 +57,8 @@ type service struct {
objectStore objectstore.ObjectStore
collectionService CollectionService
bookmarkService bookmarkService
app *app.App
spaceService space.Service
templateService TemplateService
fileService files.Service
}
func NewCreator() Service {
@ -74,8 +71,6 @@ func (s *service) Init(a *app.App) (err error) {
s.collectionService = app.MustComponent[CollectionService](a)
s.spaceService = app.MustComponent[space.Service](a)
s.templateService = app.MustComponent[TemplateService](a)
s.fileService = app.MustComponent[files.Service](a)
s.app = a
return nil
}

View file

@ -104,8 +104,6 @@ func (s *service) InstallBundledObjects(
objects = append(objects, newDetails)
}
}
s.reviseSystemObjects(space, existingObjectMap)
return
}

View file

@ -40,7 +40,6 @@ type Service interface {
}
type Builder struct {
graphService Service //nolint:unused
sbtProvider typeprovider.SmartBlockTypeProvider
objectStore objectstore.ObjectStore
subscriptionService subscription.Service

View file

@ -2,7 +2,6 @@ package treemanager
import (
"context"
"sync"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
@ -25,9 +24,6 @@ type treeManager struct {
spaceService space.Service
onDelete func(id domain.FullID) error
syncStarted bool
syncerLock sync.Mutex
}
func New() treemanager.TreeManager {

View file

@ -1,6 +1,10 @@
package restriction
import (
"github.com/samber/lo"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
@ -99,15 +103,33 @@ func (dr DataviewRestrictions) Equal(dr2 DataviewRestrictions) bool {
return true
}
func (s *service) getDataviewRestrictions(rh RestrictionHolder) DataviewRestrictions {
func getDataviewRestrictions(rh RestrictionHolder) DataviewRestrictions {
if dr, ok := dataviewRestrictionsBySBType[rh.Type()]; ok {
return dr
}
uk := rh.UniqueKey()
if uk != nil {
return GetDataviewRestrictionsForUniqueKey(uk)
return getDataviewRestrictionsForUniqueKey(uk)
}
return nil
}
func getDataviewRestrictionsForUniqueKey(uk domain.UniqueKey) DataviewRestrictions {
switch uk.SmartblockType() {
case smartblock.SmartBlockTypeObjectType:
key := uk.InternalKey()
if lo.Contains(bundle.InternalTypes, domain.TypeKey(key)) {
return DataviewRestrictions{
model.RestrictionsDataviewRestrictions{
BlockId: DataviewBlockId,
Restrictions: []model.RestrictionsDataviewRestriction{model.Restrictions_DVCreateObject},
},
}
}
case smartblock.SmartBlockTypeRelation:
// should we handle this?
}
return nil
}

View file

@ -11,7 +11,7 @@ import (
)
func TestService_DataviewRestrictions(t *testing.T) {
s := newFixture(t)
s := service{}
t.Run("internal types have restrictions", func(t *testing.T) {
for _, typeKey := range bundle.InternalTypes {
@ -38,14 +38,12 @@ func TestService_DataviewRestrictions(t *testing.T) {
})
t.Run("ordinary objects don't have restrictions", func(t *testing.T) {
objectTypeID := "derivedFrom(page)"
restrictions := s.GetRestrictions(
newRestrictionHolder(
smartblock.SmartBlockTypePage,
model.ObjectType_basic,
nil,
objectTypeID,
),
&restrictionHolder{
sbType: smartblock.SmartBlockTypePage,
uniqueKey: nil,
layout: model.ObjectType_basic,
},
)
assert.Equal(t, dvRestrictNo, restrictions.Dataview)
})

View file

@ -1,61 +1,58 @@
package restriction
import (
"fmt"
"testing"
"github.com/anyproto/any-sync/app"
"github.com/stretchr/testify/require"
"github.com/anyproto/anytype-heart/core/domain"
"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/mock_objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space/spacecore/typeprovider/mock_typeprovider"
)
type fixture struct {
Service
objectStoreMock *mock_objectstore.MockObjectStore
const noLayout = -1
type restrictionHolder struct {
sbType smartblock.SmartBlockType
uniqueKey domain.UniqueKey
layout model.ObjectTypeLayout
}
func newFixture(t *testing.T) *fixture {
objectStore := mock_objectstore.NewMockObjectStore(t)
objectStore.EXPECT().Name().Return("objectstore")
sbtProvider := mock_typeprovider.NewMockSmartBlockTypeProvider(t)
sbtProvider.EXPECT().Name().Return("sbtProvider")
a := &app.App{}
a.Register(objectStore)
a.Register(sbtProvider)
s := New()
err := s.Init(a)
require.NoError(t, err)
return &fixture{
Service: s,
objectStoreMock: objectStore,
}
func (rh *restrictionHolder) Type() smartblock.SmartBlockType {
return rh.sbType
}
func fakeDerivedID(key string) string {
return fmt.Sprintf("derivedFrom(%s)", key)
func (rh *restrictionHolder) Layout() (model.ObjectTypeLayout, bool) {
return rh.layout, rh.layout != noLayout
}
func (rh *restrictionHolder) UniqueKey() domain.UniqueKey {
return rh.uniqueKey
}
func givenObjectType(typeKey domain.TypeKey) RestrictionHolder {
return newRestrictionHolder(
smartblock.SmartBlockTypeObjectType,
model.ObjectType_objectType,
domain.MustUniqueKey(smartblock.SmartBlockTypeObjectType, typeKey.String()),
fakeDerivedID(typeKey.String()),
)
return &restrictionHolder{
sbType: smartblock.SmartBlockTypeObjectType,
layout: model.ObjectType_objectType,
uniqueKey: domain.MustUniqueKey(smartblock.SmartBlockTypeObjectType, typeKey.String()),
}
}
func givenRelation(relationKey domain.RelationKey) RestrictionHolder {
return newRestrictionHolder(
smartblock.SmartBlockTypeRelation,
model.ObjectType_relation,
domain.MustUniqueKey(smartblock.SmartBlockTypeRelation, relationKey.String()),
fakeDerivedID(relationKey.String()),
)
return &restrictionHolder{
sbType: smartblock.SmartBlockTypeRelation,
layout: model.ObjectType_relation,
uniqueKey: domain.MustUniqueKey(smartblock.SmartBlockTypeRelation, relationKey.String()),
}
}
func givenRestrictionHolder(sbType smartblock.SmartBlockType, typeKey domain.TypeKey) RestrictionHolder {
layout := model.ObjectType_basic
t, err := bundle.GetType(typeKey)
if err == nil {
layout = t.Layout
}
uk, _ := domain.NewUniqueKey(sbType, "")
return &restrictionHolder{
sbType: sbType,
layout: layout,
uniqueKey: uk,
}
}

View file

@ -37,12 +37,6 @@ var (
model.Restrictions_TypeChange,
model.Restrictions_Template,
}
collectionRestrictions = ObjectRestrictions{
model.Restrictions_Blocks,
model.Restrictions_LayoutChange,
model.Restrictions_TypeChange,
model.Restrictions_Template,
}
sysTypesRestrictions = ObjectRestrictions{
model.Restrictions_Blocks,
model.Restrictions_LayoutChange,
@ -65,8 +59,8 @@ var (
model.ObjectType_basic: {},
model.ObjectType_profile: {},
model.ObjectType_todo: {},
model.ObjectType_set: collectionRestrictions,
model.ObjectType_collection: collectionRestrictions,
model.ObjectType_set: objRestrictEdit,
model.ObjectType_collection: objRestrictEdit,
model.ObjectType_objectType: objRestrictEdit,
model.ObjectType_relation: objRestrictEdit,
model.ObjectType_file: objFileRestrictions,
@ -189,10 +183,10 @@ func (or ObjectRestrictions) ToPB() *types.Value {
return pbtypes.IntList(ints...)
}
func (s *service) getObjectRestrictions(rh RestrictionHolder) (r ObjectRestrictions) {
func getObjectRestrictions(rh RestrictionHolder) (r ObjectRestrictions) {
uk := rh.UniqueKey()
if uk != nil {
return GetRestrictionsForUniqueKey(rh.UniqueKey())
if uk != nil && uk.InternalKey() != "" {
return getRestrictionsForUniqueKey(uk)
}
var ok bool
@ -208,7 +202,7 @@ func (s *service) getObjectRestrictions(rh RestrictionHolder) (r ObjectRestricti
return
}
func GetRestrictionsForUniqueKey(uk domain.UniqueKey) (r ObjectRestrictions) {
func getRestrictionsForUniqueKey(uk domain.UniqueKey) (r ObjectRestrictions) {
r = objectRestrictionsBySBType[uk.SmartblockType()]
switch uk.SmartblockType() {
case smartblock.SmartBlockTypeObjectType:
@ -229,21 +223,3 @@ func GetRestrictionsForUniqueKey(uk domain.UniqueKey) (r ObjectRestrictions) {
// we assume that all sb types exist in objectRestrictionsBySBType
return r
}
func GetDataviewRestrictionsForUniqueKey(uk domain.UniqueKey) DataviewRestrictions {
switch uk.SmartblockType() {
case smartblock.SmartBlockTypeObjectType:
key := uk.InternalKey()
if lo.Contains(bundle.InternalTypes, domain.TypeKey(key)) {
return DataviewRestrictions{
model.RestrictionsDataviewRestrictions{
BlockId: DataviewBlockId,
Restrictions: []model.RestrictionsDataviewRestriction{model.Restrictions_DVCreateObject},
},
}
}
case smartblock.SmartBlockTypeRelation:
// should we handle this?
}
return nil
}

View file

@ -10,51 +10,44 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
// TODO Use constructors instead for initializing restrictionHolder structures by hand. See givenObjectType and givenRelation
func TestService_ObjectRestrictionsById(t *testing.T) {
rest := newFixture(t)
rs := service{}
assert.ErrorIs(t, rest.GetRestrictions(&restrictionHolder{
sbType: coresb.SmartBlockTypeAnytypeProfile,
objectTypeID: "",
}).Object.Check(
model.Restrictions_Blocks,
model.Restrictions_LayoutChange,
model.Restrictions_TypeChange,
model.Restrictions_Delete,
model.Restrictions_Duplicate,
),
ErrRestricted,
)
t.Run("anytype profile should have all restrictions", func(t *testing.T) {
assert.ErrorIs(t, rs.GetRestrictions(givenRestrictionHolder(coresb.SmartBlockTypeAnytypeProfile, bundle.TypeKeyProfile)).Object.Check(
objRestrictAll...,
), ErrRestricted)
})
assert.ErrorIs(t, rest.GetRestrictions(&restrictionHolder{
sbType: coresb.SmartBlockTypePage,
layout: model.ObjectType_collection,
objectTypeID: bundle.TypeKeyCollection.URL(),
}).Object.Check(model.Restrictions_Blocks),
ErrRestricted,
)
t.Run("sets and collections should have edit restrictions", func(t *testing.T) {
collection := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeyCollection)
assert.ErrorIs(t, rs.GetRestrictions(collection).Object.Check(objRestrictEdit...), ErrRestricted)
set := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeySet)
assert.ErrorIs(t, rs.GetRestrictions(set).Object.Check(objRestrictEdit...), ErrRestricted)
})
assert.NoError(t, rest.GetRestrictions(&restrictionHolder{
sbType: coresb.SmartBlockTypePage,
objectTypeID: bundle.TypeKeyPage.URL(),
}).Object.Check(model.Restrictions_Blocks))
t.Run("plain pages should not have any restrictions", func(t *testing.T) {
page := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeyPage)
for _, restriction := range objRestrictAll {
assert.NoError(t, rs.GetRestrictions(page).Object.Check(restriction))
}
})
t.Run("system type", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyObjectType)).Object.Check(
assert.ErrorIs(t, rs.GetRestrictions(givenObjectType(bundle.TypeKeyObjectType)).Object.Check(
model.Restrictions_Details,
model.Restrictions_Delete,
), ErrRestricted)
})
t.Run("system type restricted creation", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyParticipant)).Object.Check(
assert.ErrorIs(t, rs.GetRestrictions(givenObjectType(bundle.TypeKeyParticipant)).Object.Check(
model.Restrictions_CreateObjectOfThisType,
), ErrRestricted)
})
t.Run("ordinary type", func(t *testing.T) {
assert.NoError(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
assert.NoError(t, rs.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
model.Restrictions_Details,
model.Restrictions_Delete,
model.Restrictions_CreateObjectOfThisType,
@ -62,29 +55,25 @@ func TestService_ObjectRestrictionsById(t *testing.T) {
})
t.Run("ordinary type has basic restrictions", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
assert.ErrorIs(t, rs.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
model.Restrictions_Blocks,
model.Restrictions_LayoutChange,
), ErrRestricted)
})
t.Run("ordinary relation has basic restrictions", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
assert.ErrorIs(t, rs.GetRestrictions(givenObjectType(bundle.TypeKeyDailyPlan)).Object.Check(
model.Restrictions_TypeChange,
), ErrRestricted)
})
assert.ErrorIs(t, rest.GetRestrictions(&restrictionHolder{
sbType: coresb.SmartBlockTypeBundledObjectType,
layout: model.ObjectType_objectType,
objectTypeID: bundle.TypeKeyObjectType.URL(),
}).Object.Check(
model.Restrictions_Duplicate,
model.Restrictions_Relations,
), ErrRestricted)
t.Run("bundled object types should have all restrictions", func(t *testing.T) {
bundledType := givenRestrictionHolder(coresb.SmartBlockTypeBundledObjectType, bundle.TypeKeyObjectType)
assert.ErrorIs(t, rs.GetRestrictions(bundledType).Object.Check(objRestrictAll...), ErrRestricted)
})
t.Run("ordinary relation", func(t *testing.T) {
assert.NoError(t, rest.GetRestrictions(givenRelation(bundle.RelationKeyImdbRating)).Object.Check(
assert.NoError(t, rs.GetRestrictions(givenRelation(bundle.RelationKeyImdbRating)).Object.Check(
model.Restrictions_Delete,
model.Restrictions_Relations,
model.Restrictions_Details,
@ -92,59 +81,39 @@ func TestService_ObjectRestrictionsById(t *testing.T) {
})
t.Run("system relation", func(t *testing.T) {
assert.ErrorIs(t, rest.GetRestrictions(givenRelation(bundle.RelationKeyId)).Object.Check(
assert.ErrorIs(t, rs.GetRestrictions(givenRelation(bundle.RelationKeyId)).Object.Check(
model.Restrictions_Delete,
model.Restrictions_Relations,
model.Restrictions_Details,
), ErrRestricted)
})
assert.ErrorIs(t, rest.GetRestrictions(&restrictionHolder{
sbType: coresb.SmartBlockTypeBundledRelation,
layout: model.ObjectType_relation,
objectTypeID: bundle.TypeKeyRelation.URL(),
}).Object.Check(
model.Restrictions_Duplicate,
), ErrRestricted)
t.Run("bundled object types should have all restrictions", func(t *testing.T) {
bundledRelation := givenRestrictionHolder(coresb.SmartBlockTypeBundledRelation, bundle.TypeKeyRelation)
assert.ErrorIs(t, rs.GetRestrictions(bundledRelation).Object.Check(objRestrictAll...), ErrRestricted)
})
}
// TODO Use constructors instead for initializing restrictionHolder structures by hand. See givenObjectType and givenRelation
func TestTemplateRestriction(t *testing.T) {
rs := newFixture(t)
rs := service{}
assert.ErrorIs(t, rs.GetRestrictions(&restrictionHolder{
// id: "cannot make template from Template smartblock type",
sbType: coresb.SmartBlockTypeTemplate,
layout: model.ObjectType_basic,
objectTypeID: bundle.TypeKeyTemplate.URL(),
}).Object.Check(
model.Restrictions_Template,
), ErrRestricted)
t.Run("cannot make template from Template smartblock type", func(t *testing.T) {
template := givenRestrictionHolder(coresb.SmartBlockTypeTemplate, bundle.TypeKeyTemplate)
assert.ErrorIs(t, rs.GetRestrictions(template).Object.Check(model.Restrictions_Template), ErrRestricted)
})
assert.ErrorIs(t, rs.GetRestrictions(&restrictionHolder{
// id: "cannot make template from set or collection layout",
sbType: coresb.SmartBlockTypePage,
layout: model.ObjectType_collection,
objectTypeID: bundle.TypeKeyCollection.URL(),
}).Object.Check(
model.Restrictions_Template,
), ErrRestricted)
t.Run("cannot make template from set or collection layout", func(t *testing.T) {
collection := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeyCollection)
assert.ErrorIs(t, rs.GetRestrictions(collection).Object.Check(model.Restrictions_Template), ErrRestricted)
})
assert.ErrorIs(t, rs.GetRestrictions(&restrictionHolder{
// id: "cannot make template from space layout",
sbType: coresb.SmartBlockTypePage,
layout: model.ObjectType_space,
objectTypeID: bundle.TypeKeySpace.URL(),
}).Object.Check(
model.Restrictions_Template,
), ErrRestricted)
t.Run("cannot make template from space layout", func(t *testing.T) {
space := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeySpace)
assert.ErrorIs(t, rs.GetRestrictions(space).Object.Check(model.Restrictions_Template), ErrRestricted)
})
assert.NoError(t, rs.GetRestrictions(&restrictionHolder{
// id: "make template from object with objectType added to space",
sbType: coresb.SmartBlockTypePage,
layout: model.ObjectType_basic,
objectTypeID: bundle.TypeKeyContact.URL(),
}).Object.Check(
model.Restrictions_Template,
))
t.Run("make template from object with objectType added to space", func(t *testing.T) {
book := givenRestrictionHolder(coresb.SmartBlockTypePage, bundle.TypeKeyBook)
assert.NoError(t, rs.GetRestrictions(book).Object.Check(model.Restrictions_Template))
})
}

View file

@ -1,45 +0,0 @@
package restriction
import (
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
type RestrictionHolder interface {
Type() smartblock.SmartBlockType
Layout() (model.ObjectTypeLayout, bool)
UniqueKey() domain.UniqueKey
}
type restrictionHolder struct {
sbType smartblock.SmartBlockType
uniqueKey domain.UniqueKey
layout model.ObjectTypeLayout
objectTypeID string
}
func newRestrictionHolder(sbType smartblock.SmartBlockType, layout model.ObjectTypeLayout, uk domain.UniqueKey, objectTypeID string) RestrictionHolder {
return &restrictionHolder{
sbType: sbType,
layout: layout,
uniqueKey: uk,
objectTypeID: objectTypeID,
}
}
func (rh *restrictionHolder) Type() smartblock.SmartBlockType {
return rh.sbType
}
func (rh *restrictionHolder) Layout() (model.ObjectTypeLayout, bool) {
return rh.layout, rh.layout != noLayout
}
func (rh *restrictionHolder) ObjectTypeID() string {
return rh.objectTypeID
}
func (s *restrictionHolder) UniqueKey() domain.UniqueKey {
return s.uniqueKey
}

View file

@ -5,21 +5,20 @@ import (
"github.com/anyproto/any-sync/app"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const (
CName = "restriction"
noLayout = -1
)
const CName = "restriction"
var (
ErrRestricted = errors.New("restricted")
var ErrRestricted = errors.New("restricted")
log = logging.Logger("anytype-mw-restrictions")
)
type RestrictionHolder interface {
Type() smartblock.SmartBlockType
Layout() (model.ObjectTypeLayout, bool)
UniqueKey() domain.UniqueKey
}
type Service interface {
GetRestrictions(RestrictionHolder) Restrictions
@ -27,16 +26,13 @@ type Service interface {
app.Component
}
type service struct {
objectStore objectstore.ObjectStore
}
type service struct{}
func New() Service {
return &service{}
}
func (s *service) Init(a *app.App) (err error) {
s.objectStore = app.MustComponent[objectstore.ObjectStore](a)
func (s *service) Init(*app.App) (err error) {
return
}
@ -46,13 +42,13 @@ func (s *service) Name() (name string) {
func (s *service) GetRestrictions(rh RestrictionHolder) (r Restrictions) {
return Restrictions{
Object: s.getObjectRestrictions(rh),
Dataview: s.getDataviewRestrictions(rh),
Object: getObjectRestrictions(rh),
Dataview: getDataviewRestrictions(rh),
}
}
func (s *service) CheckRestrictions(rh RestrictionHolder, cr ...model.RestrictionsObjectRestriction) error {
r := s.getObjectRestrictions(rh)
r := getObjectRestrictions(rh)
if err := r.Check(cr...); err != nil {
return err
}

View file

@ -7,7 +7,6 @@ import (
"sync"
"time"
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/globalsign/mgo/bson"
@ -21,7 +20,6 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/collection"
"github.com/anyproto/anytype-heart/core/block/editor/converter"
"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"
@ -32,14 +30,12 @@ import (
"github.com/anyproto/anytype-heart/core/block/restriction"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/block/simple/bookmark"
"github.com/anyproto/anytype-heart/core/block/source"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/files"
"github.com/anyproto/anytype-heart/core/files/fileobject"
"github.com/anyproto/anytype-heart/core/files/fileuploader"
"github.com/anyproto/anytype-heart/core/filestorage/filesync"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/core/syncstatus"
"github.com/anyproto/anytype-heart/metrics"
@ -48,13 +44,11 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/core"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space"
"github.com/anyproto/anytype-heart/util/internalflag"
"github.com/anyproto/anytype-heart/util/linkpreview"
"github.com/anyproto/anytype-heart/util/mutex"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/uri"
@ -66,14 +60,12 @@ import (
)
const (
CName = "block-service"
linkObjectShare = "anytype://object/share?"
CName = "block-service"
)
var (
ErrUnexpectedBlockType = errors.New("unexpected block type")
ErrUnknownObjectType = fmt.Errorf("unknown object type")
ErrObjectNotFoundByOldID = fmt.Errorf("failed to find template by Source Object id")
ErrUnexpectedBlockType = errors.New("unexpected block type")
ErrUnknownObjectType = fmt.Errorf("unknown object type")
)
var log = logging.Logger("anytype-mw-service")
@ -106,10 +98,8 @@ func New() *Service {
type Service struct {
syncStatus syncstatus.Service
eventSender event.Sender
linkPreview linkpreview.LinkPreview
process process.Service
app *app.App
source source.Service
objectStore objectstore.ObjectStore
restriction restriction.Service
bookmark bookmarksvc.Service
@ -117,14 +107,10 @@ type Service struct {
templateService templateService
resolver idresolver.Resolver
spaceService space.Service
commonAccount accountservice.Service
fileStore filestore.FileStore
tempDirProvider core.TempDirProvider
layoutConverter converter.LayoutConverter
builtinObjectService builtinObjects
fileObjectService fileobject.Service
fileSync filesync.FileSync
fileService files.Service
fileUploaderService fileuploader.Service
@ -151,26 +137,20 @@ func (s *Service) Name() string {
func (s *Service) Init(a *app.App) (err error) {
s.syncStatus = a.MustComponent(syncstatus.CName).(syncstatus.Service)
s.linkPreview = a.MustComponent(linkpreview.CName).(linkpreview.LinkPreview)
s.process = a.MustComponent(process.CName).(process.Service)
s.eventSender = a.MustComponent(event.CName).(event.Sender)
s.source = a.MustComponent(source.CName).(source.Service)
s.objectStore = a.MustComponent(objectstore.CName).(objectstore.ObjectStore)
s.restriction = a.MustComponent(restriction.CName).(restriction.Service)
s.bookmark = a.MustComponent("bookmark-importer").(bookmarksvc.Service)
s.objectCreator = app.MustComponent[objectcreator.Service](a)
s.templateService = app.MustComponent[templateService](a)
s.spaceService = a.MustComponent(space.CName).(space.Service)
s.commonAccount = a.MustComponent(accountservice.CName).(accountservice.Service)
s.fileStore = app.MustComponent[filestore.FileStore](a)
s.fileSync = app.MustComponent[filesync.FileSync](a)
s.fileService = app.MustComponent[files.Service](a)
s.resolver = a.MustComponent(idresolver.CName).(idresolver.Resolver)
s.fileObjectService = app.MustComponent[fileobject.Service](a)
s.fileUploaderService = app.MustComponent[fileuploader.Service](a)
s.tempDirProvider = app.MustComponent[core.TempDirProvider](a)
s.layoutConverter = app.MustComponent[converter.LayoutConverter](a)
s.builtinObjectService = app.MustComponent[builtinObjects](a)
s.app = a

View file

@ -154,14 +154,7 @@ func (l *Dataview) ReorderSorts(viewID string, ids []string) error {
}
func (l *Dataview) AddViewRelation(viewID string, relation *model.BlockContentDataviewRelation) error {
view, err := l.GetView(viewID)
if err != nil {
return err
}
l.syncViewRelationWithRelationLinks(view)
view.Relations = append(view.Relations, relation)
return nil
return l.ReplaceViewRelation(viewID, relation.Key, relation)
}
func (l *Dataview) RemoveViewRelations(viewID string, relationKeys []string) error {
@ -188,7 +181,8 @@ func (l *Dataview) ReplaceViewRelation(viewID string, relationKey string, relati
return f.Key == relationKey
})
if idx < 0 {
return l.AddViewRelation(viewID, relation)
view.Relations = append(view.Relations, relation)
return nil
}
view.Relations[idx] = relation

View file

@ -12,7 +12,7 @@ import (
const testViewId = "viewId"
func makeDataviewForReorderTest(relationLinks []*model.RelationLink, relations []*model.BlockContentDataviewRelation) Block {
func makeDataviewForViewRelationsTest(relationLinks []*model.RelationLink, relations []*model.BlockContentDataviewRelation) Block {
return NewDataview(&model.Block{
Content: &model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
@ -30,7 +30,7 @@ func makeDataviewForReorderTest(relationLinks []*model.RelationLink, relations [
func TestReorderViewRelations(t *testing.T) {
t.Run("reorder: add missing relation from relation links", func(t *testing.T) {
dv := makeDataviewForReorderTest(
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object},
@ -44,7 +44,7 @@ func TestReorderViewRelations(t *testing.T) {
err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyCreator.String(), bundle.RelationKeyName.String()})
require.NoError(t, err)
want := makeDataviewForReorderTest(
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object},
@ -61,7 +61,7 @@ func TestReorderViewRelations(t *testing.T) {
})
t.Run("reorder: remove extra relation that don't exist in relation links", func(t *testing.T) {
dv := makeDataviewForReorderTest(
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
@ -76,7 +76,7 @@ func TestReorderViewRelations(t *testing.T) {
err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyName.String(), bundle.RelationKeyCreator.String(), bundle.RelationKeyDescription.String()})
require.NoError(t, err)
want := makeDataviewForReorderTest(
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
@ -89,5 +89,119 @@ func TestReorderViewRelations(t *testing.T) {
assert.Equal(t, want, dv)
})
}
func TestReplaceViewRelation(t *testing.T) {
t.Run("add new relation", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{},
)
err := dv.ReplaceViewRelation(testViewId, bundle.RelationKeyDescription.String(), &model.BlockContentDataviewRelation{
Key: bundle.RelationKeyDescription.String(),
Width: DefaultViewRelationWidth,
IsVisible: true,
})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{
// Added automatically from relation links
{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
IsVisible: false,
},
{
Key: bundle.RelationKeyDescription.String(),
Width: DefaultViewRelationWidth,
IsVisible: true,
},
},
)
assert.Equal(t, want, dv)
})
t.Run("replace existing", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{
// Added automatically from relation links
{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
IsVisible: false,
},
{
Key: bundle.RelationKeyDescription.String(),
Width: DefaultViewRelationWidth,
IsVisible: true,
},
},
)
err := dv.ReplaceViewRelation(testViewId, bundle.RelationKeyDescription.String(), &model.BlockContentDataviewRelation{
Key: bundle.RelationKeyAssignee.String(),
Width: DefaultViewRelationWidth,
IsVisible: true,
})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{
// Added automatically from relation links
{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
IsVisible: false,
},
{
Key: bundle.RelationKeyAssignee.String(),
Width: DefaultViewRelationWidth,
IsVisible: true,
},
},
)
assert.Equal(t, want, dv)
})
t.Run("add relation that exist in relation links, but not in View", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{},
)
err := dv.ReplaceViewRelation(testViewId, bundle.RelationKeyName.String(), &model.BlockContentDataviewRelation{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{
{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
},
},
)
assert.Equal(t, want, dv)
})
}

View file

@ -23,7 +23,6 @@ import (
"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/addr"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/space/spacecore"
"github.com/anyproto/anytype-heart/space/spacecore/storage"
@ -63,7 +62,6 @@ type service struct {
sbtProvider typeprovider.SmartBlockTypeProvider
accountService accountService
accountKeysService accountservice.Service
fileStore filestore.FileStore
spaceCoreService spacecore.SpaceCoreService
storageService storage.ClientStorage
fileService files.Service
@ -80,7 +78,6 @@ func (s *service) Init(a *app.App) (err error) {
s.sbtProvider = a.MustComponent(typeprovider.CName).(typeprovider.SmartBlockTypeProvider)
s.accountService = app.MustComponent[accountService](a)
s.accountKeysService = a.MustComponent(accountservice.CName).(accountservice.Service)
s.fileStore = app.MustComponent[filestore.FileStore](a)
s.spaceCoreService = app.MustComponent[spacecore.SpaceCoreService](a)
s.storageService = a.MustComponent(spacestorage.CName).(storage.ClientStorage)

View file

@ -143,7 +143,7 @@ func (s *service) newTreeSource(ctx context.Context, space Space, id string, bui
return nil, fmt.Errorf("build tree: %w", err)
}
sbt, key, err := typeprovider.GetTypeAndKeyFromRoot(ot.Header())
sbt, _, err := typeprovider.GetTypeAndKeyFromRoot(ot.Header())
if err != nil {
return nil, err
}
@ -151,11 +151,9 @@ func (s *service) newTreeSource(ctx context.Context, space Space, id string, bui
return &source{
ObjectTree: ot,
id: id,
headerKey: key,
space: space,
spaceID: space.Id(),
spaceService: s.spaceCoreService,
openedAt: time.Now(),
smartblockType: sbt,
accountService: s.accountService,
accountKeysService: s.accountKeysService,
@ -181,14 +179,11 @@ type source struct {
space Space
spaceID string
smartblockType smartblock.SmartBlockType
headerKey string // used for header(id) derivation together with smartblockType
lastSnapshotId string
changesSinceSnapshot int
receiver ChangeReceiver
unsubscribe func()
metaOnly bool
closed chan struct{}
openedAt time.Time
fileService files.Service
accountService accountService
@ -211,7 +206,8 @@ func (s *source) Update(ot objecttree.ObjectTree) {
prevSnapshot := s.lastSnapshotId
// todo: check this one
err := s.receiver.StateAppend(func(d state.Doc) (st *state.State, changes []*pb.ChangeContent, err error) {
st, changes, sinceSnapshot, err := BuildState(s.spaceID, d.(*state.State), ot)
// State will be applied later in smartblock.StateAppend
st, changes, sinceSnapshot, err := BuildState(s.spaceID, d.(*state.State), ot, false)
if err != nil {
return
}
@ -277,7 +273,7 @@ func (s *source) readDoc(receiver ChangeReceiver) (doc state.Doc, err error) {
}
func (s *source) buildState() (doc state.Doc, err error) {
st, _, changesAppliedSinceSnapshot, err := BuildState(s.spaceID, nil, s.ObjectTree)
st, _, changesAppliedSinceSnapshot, err := BuildState(s.spaceID, nil, s.ObjectTree, true)
if err != nil {
return
}
@ -295,7 +291,7 @@ func (s *source) buildState() (doc state.Doc, err error) {
// temporary, though the applying change to this Dataview block will persist this migration, breaking backward
// compatibility. But in many cases we expect that users update object not so often as they just view them.
// TODO: we can skip migration for non-personal spaces
migration := NewSubObjectsAndProfileLinksMigration(s.smartblockType, s.space, s.accountService.MyParticipantId(s.spaceID), s.accountService.PersonalSpaceID(), s.objectStore)
migration := NewSubObjectsAndProfileLinksMigration(s.smartblockType, s.space, s.accountService.MyParticipantId(s.spaceID), s.objectStore)
migration.Migrate(st)
if s.Type() == smartblock.SmartBlockTypePage || s.Type() == smartblock.SmartBlockTypeProfilePage {
@ -533,7 +529,7 @@ func (s *source) Close() (err error) {
return s.ObjectTree.Close()
}
func BuildState(spaceId string, initState *state.State, ot objecttree.ReadableObjectTree) (st *state.State, appliedContent []*pb.ChangeContent, changesAppliedSinceSnapshot int, err error) {
func BuildState(spaceId string, initState *state.State, ot objecttree.ReadableObjectTree, applyState bool) (st *state.State, appliedContent []*pb.ChangeContent, changesAppliedSinceSnapshot int, err error) {
var (
startId string
lastChange *objecttree.Change
@ -598,9 +594,11 @@ func BuildState(spaceId string, initState *state.State, ot objecttree.ReadableOb
if err != nil {
return
}
_, _, err = state.ApplyStateFastOne(st)
if err != nil {
return
if applyState {
_, _, err = state.ApplyStateFastOne(st)
if err != nil {
return
}
}
if lastChange != nil && !st.IsTheHeaderChange() {

View file

@ -25,7 +25,6 @@ import (
type subObjectsAndProfileLinksMigration struct {
profileID string
identityObjectID string
personalSpaceId string
sbType smartblock.SmartBlockType
space Space
objectStore objectstore.ObjectStore
@ -35,13 +34,11 @@ func NewSubObjectsAndProfileLinksMigration(
sbType smartblock.SmartBlockType,
space Space,
identityObjectID string,
personalSpaceId string,
objectStore objectstore.ObjectStore,
) *subObjectsAndProfileLinksMigration {
return &subObjectsAndProfileLinksMigration{
space: space,
identityObjectID: identityObjectID,
personalSpaceId: personalSpaceId,
sbType: sbType,
objectStore: objectStore,
}

View file

@ -25,17 +25,15 @@ import (
var log = logging.Logger("html-converter").Desugar()
func NewHTMLConverter(spaceID string, fileService files.Service, s *state.State, fileObjectService fileobject.Service) *HTML {
func NewHTMLConverter(fileService files.Service, s *state.State, fileObjectService fileobject.Service) *HTML {
return &HTML{
s: s,
spaceID: spaceID,
fileService: fileService,
fileObjectService: fileObjectService,
}
}
type HTML struct {
spaceID string
s *state.State
buf *bytes.Buffer
fileService files.Service

View file

@ -18,7 +18,7 @@ func TestHTML_Convert(t *testing.T) {
s := state.NewDoc("root", map[string]simple.Block{
"root": simple.New(&model.Block{}),
}).(*state.State)
assert.Empty(t, NewHTMLConverter("space1", nil, s, nil).Convert())
assert.Empty(t, NewHTMLConverter(nil, s, nil).Convert())
})
t.Run("markup", func(t *testing.T) {
@ -88,7 +88,7 @@ func TestHTML_Convert(t *testing.T) {
}
func convertHtml(s *state.State) string {
return NewHTMLConverter("space1", nil, s, nil).Convert()
return NewHTMLConverter(nil, s, nil).Convert()
}
func givenMarkup() *state.State {

View file

@ -19,7 +19,6 @@ import (
"github.com/go-chi/chi/v5"
"github.com/gogo/protobuf/jsonpb"
"github.com/anyproto/anytype-heart/core/block"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/object/idresolver"
"github.com/anyproto/anytype-heart/core/domain"
@ -46,7 +45,6 @@ type Debug interface {
}
type debug struct {
block *block.Service
store objectstore.ObjectStore
spaceService space.Service
resolver idresolver.Resolver
@ -61,7 +59,6 @@ type Debuggable interface {
func (d *debug) Init(a *app.App) (err error) {
d.store = a.MustComponent(objectstore.CName).(objectstore.ObjectStore)
d.block = a.MustComponent(block.CName).(*block.Service)
d.spaceService = app.MustComponent[space.Service](a)
d.resolver = app.MustComponent[idresolver.Resolver](a)
d.statService, _ = a.Component(debugstat.CName).(debugstat.StatService)

View file

@ -72,7 +72,7 @@ func (t *treeImporter) State() (*state.State, error) {
err error
)
st, _, _, err = source.BuildState("", nil, t.objectTree)
st, _, _, err = source.BuildState("", nil, t.objectTree, true)
if err != nil {
return nil, err
}

View file

@ -13,17 +13,13 @@ import (
type zipAclReadStorage struct {
id string
head string
files map[string]*zip.File
zr *zip.ReadCloser
}
func NewZipAclReadStorage(id string, zr *zip.ReadCloser) (ls liststorage.ListStorage, err error) {
aclStorage := &zipAclReadStorage{
id: id,
head: id,
files: map[string]*zip.File{},
zr: zr,
}
for _, f := range zr.Reader.File {
if len(f.Name) > len(id) && strings.Contains(f.Name, id) {

View file

@ -10,16 +10,14 @@ import (
)
type zipACLWriteStorage struct {
id string
head string
zw *zip.Writer
id string
zw *zip.Writer
}
func NewACLWriteStorage(root *consensusproto.RawRecordWithId, zw *zip.Writer) (ls liststorage.ListStorage, err error) {
ls = &zipACLWriteStorage{
id: root.Id,
head: root.Id,
zw: zw,
id: root.Id,
zw: zw,
}
err = ls.AddRawRecord(context.Background(), root)
return

View file

@ -16,7 +16,6 @@ type zipTreeReadStorage struct {
id string
heads []string
files map[string]*zip.File
zr *zip.ReadCloser
}
func NewZipTreeReadStorage(id string, zr *zip.ReadCloser) (st treestorage.TreeStorage, err error) {
@ -24,7 +23,6 @@ func NewZipTreeReadStorage(id string, zr *zip.ReadCloser) (st treestorage.TreeSt
id: id,
heads: nil,
files: map[string]*zip.File{},
zr: zr,
}
for _, f := range zr.Reader.File {
if len(f.Name) > len(id) && strings.Contains(f.Name, id) {

View file

@ -10,8 +10,6 @@ type FullID struct {
SpaceID string
}
type ObjectTypeKey string
const ParticipantPrefix = "_participant_"
func NewParticipantId(spaceId, identity string) string {

View file

@ -18,12 +18,6 @@ func (s *service) DebugRouter(r chi.Router) {
r.Get("/tree/{rootID}", debug.PlaintextHandler(s.printTree))
}
type fileDebugInfo struct {
Hash string
SyncStatus int
IsIndexed bool
}
func (s *service) printTree(w io.Writer, req *http.Request) error {
rawID := chi.URLParam(req, "rootID")
id, err := cid.Parse(rawID)

View file

@ -34,15 +34,6 @@ type file struct {
node *service
}
func (s *service) newFile(spaceId string, fileId domain.FileId, info *storage.FileInfo) File {
return &file{
spaceID: spaceId,
fileId: fileId,
info: info,
node: s,
}
}
type FileMeta struct {
Media string
Name string

View file

@ -229,16 +229,18 @@ func (ind *indexer) injectMetadataToState(ctx context.Context, st *state.State,
return nil
}
func (ind *indexer) markFileAsNotFound(st *state.State) {
st.SetDetailAndBundledRelation(bundle.RelationKeyFileIndexingStatus, pbtypes.Int64(int64(model.FileIndexingStatus_NotFound)))
}
func (ind *indexer) buildDetails(ctx context.Context, id domain.FullFileId) (details *types.Struct, typeKey domain.TypeKey, err error) {
file, err := ind.fileService.FileByHash(ctx, id)
if err != nil {
return nil, "", err
}
if mill.IsImage(file.Info().Media) {
if file.Info().Mill == mill.BlobId {
details, typeKey, err = file.Details(ctx)
if err != nil {
return nil, "", err
}
} else {
image, err := ind.fileService.ImageByHash(ctx, id)
if err != nil {
return nil, "", err
@ -247,13 +249,15 @@ func (ind *indexer) buildDetails(ctx context.Context, id domain.FullFileId) (det
if err != nil {
return nil, "", err
}
typeKey = bundle.TypeKeyImage
} else {
details, typeKey, err = file.Details(ctx)
if err != nil {
return nil, "", err
}
}
// Overwrite typeKey for images in case that image is uploaded as file.
// That can be possible because some images can't be handled properly and wee fall back to
// handling them as files
if mill.IsImage(file.Info().Media) {
typeKey = bundle.TypeKeyImage
}
details.Fields[bundle.RelationKeyFileIndexingStatus.String()] = pbtypes.Int64(int64(model.FileIndexingStatus_Indexed))
return details, typeKey, nil
}

View file

@ -2,37 +2,147 @@ package fileobject
import (
"context"
"fmt"
"testing"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/files/mock_files"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/mill"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/pkg/lib/pb/storage"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
type indexerFixture struct {
*indexer
fileService *mock_files.MockService
objectStoreFixture *objectstore.StoreFixture
}
func newIndexerFixture(t *testing.T) *indexerFixture {
objectStore := objectstore.NewStoreFixture(t)
fileService := mock_files.NewMockService(t)
svc := &service{
objectStore: objectStore,
fileService: fileService,
}
ind := svc.newIndexer()
return &indexerFixture{
objectStoreFixture: objectStore,
fileService: fileService,
indexer: ind,
}
}
func TestIndexer_buildDetails(t *testing.T) {
t.Run("with file", func(t *testing.T) {
for _, typeKey := range []domain.TypeKey{
bundle.TypeKeyFile,
bundle.TypeKeyAudio,
bundle.TypeKeyVideo,
} {
t.Run(fmt.Sprintf("with type %s", typeKey), func(t *testing.T) {
fx := newIndexerFixture(t)
id := domain.FullFileId{
SpaceId: "space1",
FileId: testFileId,
}
ctx := context.Background()
file := mock_files.NewMockFile(t)
file.EXPECT().Info().Return(&storage.FileInfo{
Mill: mill.BlobId,
Media: "text",
})
file.EXPECT().Details(ctx).Return(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("name"),
},
}, typeKey, nil)
fx.fileService.EXPECT().FileByHash(ctx, id).Return(file, nil)
details, gotTypeKey, err := fx.buildDetails(ctx, id)
require.NoError(t, err)
assert.Equal(t, typeKey, gotTypeKey)
assert.Equal(t, "name", pbtypes.GetString(details, bundle.RelationKeyName.String()))
assert.Equal(t, pbtypes.Int64(int64(model.FileIndexingStatus_Indexed)), details.Fields[bundle.RelationKeyFileIndexingStatus.String()])
})
}
})
t.Run("with image", func(t *testing.T) {
fx := newIndexerFixture(t)
id := domain.FullFileId{
SpaceId: "space1",
FileId: testFileId,
}
ctx := context.Background()
file := mock_files.NewMockFile(t)
file.EXPECT().Info().Return(&storage.FileInfo{
Mill: mill.ImageResizeId,
Media: "image/jpeg",
})
image := mock_files.NewMockImage(t)
image.EXPECT().Details(ctx).Return(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("name"),
},
}, nil)
fx.fileService.EXPECT().FileByHash(ctx, id).Return(file, nil)
fx.fileService.EXPECT().ImageByHash(ctx, id).Return(image, nil)
details, gotTypeKey, err := fx.buildDetails(ctx, id)
require.NoError(t, err)
assert.Equal(t, bundle.TypeKeyImage, gotTypeKey)
assert.Equal(t, "name", pbtypes.GetString(details, bundle.RelationKeyName.String()))
assert.Equal(t, pbtypes.Int64(int64(model.FileIndexingStatus_Indexed)), details.Fields[bundle.RelationKeyFileIndexingStatus.String()])
})
t.Run("with image fell back to file", func(t *testing.T) {
for _, typeKey := range []domain.TypeKey{
bundle.TypeKeyFile,
bundle.TypeKeyAudio,
bundle.TypeKeyVideo,
bundle.TypeKeyImage,
} {
t.Run(fmt.Sprintf("with type %s", typeKey), func(t *testing.T) {
fx := newIndexerFixture(t)
id := domain.FullFileId{
SpaceId: "space1",
FileId: testFileId,
}
ctx := context.Background()
file := mock_files.NewMockFile(t)
file.EXPECT().Info().Return(&storage.FileInfo{
Mill: mill.BlobId,
Media: "image/jpeg",
})
file.EXPECT().Details(ctx).Return(&types.Struct{
Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String("name"),
},
}, typeKey, nil)
fx.fileService.EXPECT().FileByHash(ctx, id).Return(file, nil)
details, gotTypeKey, err := fx.buildDetails(ctx, id)
require.NoError(t, err)
assert.Equal(t, bundle.TypeKeyImage, gotTypeKey)
assert.Equal(t, "name", pbtypes.GetString(details, bundle.RelationKeyName.String()))
assert.Equal(t, pbtypes.Int64(int64(model.FileIndexingStatus_Indexed)), details.Fields[bundle.RelationKeyFileIndexingStatus.String()])
})
}
})
}
func TestIndexer_addFromObjectStore(t *testing.T) {
t.Run("no records in store", func(t *testing.T) {
fx := newIndexerFixture(t)

View file

@ -135,8 +135,7 @@ func (s *service) migrationQueueHandler(ctx context.Context, it *migrationItem)
return persistentqueue.ActionDone, fmt.Errorf("get space: %w", err)
}
// Wait object to load
ctx = peer.CtxWithPeerId(ctx, "*")
ctx = peer.CtxWithPeerId(ctx, peer.CtxResponsiblePeers)
_, err = space.GetObject(ctx, it.FileObjectId)
// Already migrated or deleted file object
if err == nil || errors.Is(err, spacestorage.ErrTreeStorageAlreadyDeleted) {

View file

@ -10,6 +10,7 @@ import (
"github.com/gogo/protobuf/types"
"github.com/ipfs/go-cid"
"github.com/anyproto/anytype-heart/core/anytype/config"
"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/template"
@ -34,6 +35,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space"
"github.com/anyproto/anytype-heart/space/clientspace"
"github.com/anyproto/anytype-heart/space/spacecore/peermanager"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/persistentqueue"
)
@ -103,6 +105,12 @@ func (s *service) Name() string {
return CName
}
type configProvider interface {
IsLocalOnlyMode() bool
}
var _ configProvider = (*config.Config)(nil)
func (s *service) Init(a *app.App) error {
s.spaceService = app.MustComponent[space.Service](a)
s.objectCreator = app.MustComponent[objectCreatorService](a)
@ -112,6 +120,7 @@ func (s *service) Init(a *app.App) error {
s.fileStore = app.MustComponent[filestore.FileStore](a)
s.spaceIdResolver = app.MustComponent[idresolver.Resolver](a)
s.fileStorage = app.MustComponent[filestorage.FileStorage](a)
cfg := app.MustComponent[configProvider](a)
s.indexer = s.newIndexer()
@ -120,7 +129,17 @@ func (s *service) Init(a *app.App) error {
if err != nil {
return fmt.Errorf("get badger: %w", err)
}
s.migrationQueue = persistentqueue.New(persistentqueue.NewBadgerStorage(db, []byte("queue/file_migration/"), makeMigrationItem), log.Desugar(), s.migrationQueueHandler)
migrationQueueCtx := context.Background()
if cfg.IsLocalOnlyMode() {
migrationQueueCtx = context.WithValue(migrationQueueCtx, peermanager.ContextPeerFindDeadlineKey, time.Now().Add(1*time.Minute))
}
s.migrationQueue = persistentqueue.New(
persistentqueue.NewBadgerStorage(db, []byte("queue/file_migration/"), makeMigrationItem),
log.Desugar(),
s.migrationQueueHandler,
persistentqueue.WithContext(migrationQueueCtx),
)
return nil
}

View file

@ -47,6 +47,20 @@ type fixture struct {
*service
}
type dummyConfig struct{}
func (c *dummyConfig) IsLocalOnlyMode() bool {
return false
}
func (c *dummyConfig) Init(_ *app.App) error {
return nil
}
func (c *dummyConfig) Name() string {
return "dummyConfig"
}
const testResolveRetryDelay = 5 * time.Millisecond
func newFixture(t *testing.T) *fixture {
@ -70,6 +84,7 @@ func newFixture(t *testing.T) *fixture {
ctx := context.Background()
a := new(app.App)
a.Register(&dummyConfig{})
a.Register(dataStoreProvider)
a.Register(fileStore)
a.Register(objectStore)

View file

@ -31,7 +31,6 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/crypto/symmetric/gcm"
"github.com/anyproto/anytype-heart/pkg/lib/ipfs/helpers"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/filestore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
m "github.com/anyproto/anytype-heart/pkg/lib/mill"
"github.com/anyproto/anytype-heart/pkg/lib/mill/schema"
@ -66,7 +65,6 @@ type service struct {
fileSync filesync.FileSync
dagService ipld.DAGService
fileStorage filestorage.FileStorage
objectStore objectstore.ObjectStore
lock sync.Mutex
addOperationLocks map[string]*sync.Mutex
@ -85,7 +83,6 @@ func (s *service) Init(a *app.App) (err error) {
s.dagService = s.commonFile.DAGService()
s.fileStorage = app.MustComponent[filestorage.FileStorage](a)
s.objectStore = app.MustComponent[objectstore.ObjectStore](a)
return nil
}
@ -210,21 +207,6 @@ func (s *service) newExistingFileResult(lock *sync.Mutex, fileId domain.FileId)
}
func (s *service) getFileIdAndEncryptionKeysFromInfo(fileInfo *storage.FileInfo) (domain.FileId, *domain.FileEncryptionKeys, error) {
if len(fileInfo.Targets) == 0 {
return "", nil, fmt.Errorf("file exists but has no root")
}
fileId := domain.FileId(fileInfo.Targets[0])
keys, err := s.fileStore.GetFileKeys(fileId)
if err != nil {
return "", nil, fmt.Errorf("can't get encryption keys for existing file: %w", err)
}
return fileId, &domain.FileEncryptionKeys{
FileId: fileId,
EncryptionKeys: keys,
}, nil
}
// addFileRootNode has structure:
/*
- dir (outer)
@ -337,18 +319,6 @@ func (s *service) fileInfoFromPath(ctx context.Context, spaceId string, fileId d
return &file, nil
}
func (s *service) fileContent(ctx context.Context, spaceId string, childId domain.FileContentId) (io.ReadSeeker, *storage.FileInfo, error) {
var err error
var file *storage.FileInfo
var reader io.ReadSeeker
file, err = s.fileStore.GetFileVariant(childId)
if err != nil {
return nil, nil, err
}
reader, err = s.getContentReader(ctx, spaceId, file)
return reader, file, err
}
func (s *service) getContentReader(ctx context.Context, spaceID string, file *storage.FileInfo) (symmetric.ReadSeekCloser, error) {
fileCid, err := cid.Parse(file.Hash)
if err != nil {
@ -420,7 +390,7 @@ func (s *service) addFileNode(ctx context.Context, spaceID string, mill m.Mill,
res, err := mill.Mill(conf.Reader, conf.Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("%w: %w", m.ErrProcessing, err)
}
// count the result size after the applied mill
@ -431,7 +401,12 @@ func (s *service) addFileNode(ctx context.Context, spaceID string, mill m.Mill,
}
if variant, err := s.fileStore.GetFileVariantByChecksum(mill.ID(), variantChecksum); err == nil {
return newExistingFileResult(variant)
if variant.Source == conf.checksum {
// we may have same variant checksum for different files
// e.g. empty image exif with the same resolution
// reuse the whole file only in case the checksum of the original file is the same
return newExistingFileResult(variant)
}
}
_, err = conf.Reader.Seek(0, io.SeekStart)
@ -439,8 +414,7 @@ func (s *service) addFileNode(ctx context.Context, spaceID string, mill m.Mill,
return nil, err
}
// because mill result reader doesn't support seek we need to do the mill again
res, err = mill.Mill(conf.Reader, conf.Name)
_, err = res.File.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
@ -495,6 +469,10 @@ func (s *service) addFileNode(ctx context.Context, spaceID string, mill m.Mill,
fileInfo.MetaHash = metaNode.Cid().String()
pairNode, err := s.addFilePairNode(ctx, spaceID, fileInfo)
err = res.File.Close()
if err != nil {
log.Warnf("failed to close file: %s", err)
}
if err != nil {
return nil, fmt.Errorf("add file pair node: %w", err)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View file

@ -103,7 +103,6 @@ type Uploader interface {
SetGroupId(groupId string) Uploader
SetCustomEncryptionKeys(keys map[string]string) Uploader
AddOptions(options ...files.AddOption) Uploader
AutoType(enable bool) Uploader
AsyncUpdates(smartBlockId string) Uploader
Upload(ctx context.Context) (result UploadResult)
@ -142,20 +141,20 @@ func (ur UploadResult) ToBlock() file.Block {
}
type uploader struct {
spaceId string
fileObjectService fileobject.Service
picker cache.ObjectGetter
block file.Block
getReader func(ctx context.Context) (*fileReader, error)
name string
lastModifiedDate int64
typeDetect bool
forceType bool
smartBlockID string
fileType model.BlockContentFileType
fileStyle model.BlockContentFileStyle
opts []files.AddOption
groupID string
spaceId string
fileObjectService fileobject.Service
picker cache.ObjectGetter
block file.Block
getReader func(ctx context.Context) (*fileReader, error)
name string
lastModifiedDate int64
forceType bool
forceUploadingAsFile bool
smartBlockID string
fileType model.BlockContentFileType
fileStyle model.BlockContentFileStyle
opts []files.AddOption
groupID string
tempDirProvider core.TempDirProvider
fileService files.Service
@ -214,6 +213,11 @@ func (u *uploader) SetType(tp model.BlockContentFileType) Uploader {
return u
}
func (u *uploader) ForceUploadingAsFile() Uploader {
u.forceUploadingAsFile = true
return u
}
func (u *uploader) SetStyle(tp model.BlockContentFileStyle) Uploader {
u.fileStyle = tp
return u
@ -356,11 +360,6 @@ func (u *uploader) setLastModifiedDate(path string) {
}
}
func (u *uploader) AutoType(enable bool) Uploader {
u.typeDetect = enable
return u
}
func (u *uploader) AsyncUpdates(smartBlockId string) Uploader {
u.smartBlockID = smartBlockId
return u
@ -440,10 +439,13 @@ func (u *uploader) Upload(ctx context.Context) (result UploadResult) {
}
var addResult *files.AddResult
if u.fileType == model.BlockContentFile_Image {
if !u.forceUploadingAsFile && u.fileType == model.BlockContentFile_Image {
addResult, err = u.fileService.ImageAdd(ctx, u.spaceId, opts...)
if errors.Is(err, image.ErrFormat) || errors.Is(err, mill.ErrFormatSupportNotEnabled) {
return u.SetType(model.BlockContentFile_File).Upload(ctx)
if errors.Is(err, image.ErrFormat) ||
errors.Is(err, mill.ErrFormatSupportNotEnabled) ||
errors.Is(err, mill.ErrProcessing) {
err = nil
return u.ForceUploadingAsFile().Upload(ctx)
}
if err != nil {
return UploadResult{Err: fmt.Errorf("add image to storage: %w", err)}

View file

@ -34,7 +34,6 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/tests/testutil"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/testMock"
)
func TestUploader_Upload(t *testing.T) {
@ -62,13 +61,27 @@ func TestUploader_Upload(t *testing.T) {
assert.Equal(t, b.Model().GetFile().Name, "unnamed.jpg")
assert.Equal(t, res.MIME, "image/jpeg")
})
t.Run("corrupted image: fall back to file", func(t *testing.T) {
fx := newFixture(t)
defer fx.tearDown()
fileObjectId := fx.expectCreateObject()
b := newBlock(model.BlockContentFile_Image)
res := fx.Uploader.SetBlock(b).SetFile("./testdata/corrupted.jpg").Upload(ctx)
require.NoError(t, res.Err)
assert.Equal(t, res.FileObjectId, fileObjectId)
assert.Equal(t, res.Name, "corrupted.jpg")
assert.Equal(t, b.Model().GetFile().Name, "corrupted.jpg")
assert.Equal(t, res.MIME, "image/jpeg")
})
t.Run("image type detect", func(t *testing.T) {
fx := newFixture(t)
defer fx.tearDown()
fx.expectCreateObject()
res := fx.Uploader.AutoType(true).SetFile("./testdata/unnamed.jpg").Upload(ctx)
res := fx.Uploader.SetFile("./testdata/unnamed.jpg").Upload(ctx)
require.NoError(t, res.Err)
})
t.Run("image to file failover", func(t *testing.T) {
@ -98,7 +111,7 @@ func TestUploader_Upload(t *testing.T) {
fileObjectId := fx.expectCreateObject()
res := fx.Uploader.AutoType(true).SetUrl(serv.URL + "/unnamed.jpg").Upload(ctx)
res := fx.Uploader.SetUrl(serv.URL + "/unnamed.jpg").Upload(ctx)
require.NoError(t, res.Err)
assert.Equal(t, res.FileObjectId, fileObjectId)
assert.Equal(t, res.Name, "unnamed.jpg")
@ -120,7 +133,7 @@ func TestUploader_Upload(t *testing.T) {
fileObjectId := fx.expectCreateObject()
res := fx.Uploader.AutoType(true).SetUrl(serv.URL + "/unnamed.jpg").Upload(ctx)
res := fx.Uploader.SetUrl(serv.URL + "/unnamed.jpg").Upload(ctx)
require.NoError(t, res.Err)
assert.Equal(t, res.FileObjectId, fileObjectId)
assert.Equal(t, res.Name, "filename")
@ -141,7 +154,7 @@ func TestUploader_Upload(t *testing.T) {
fileObjectId := fx.expectCreateObject()
res := fx.Uploader.AutoType(true).SetUrl(serv.URL + "/unnamed.jpg?text=text").Upload(ctx)
res := fx.Uploader.SetUrl(serv.URL + "/unnamed.jpg?text=text").Upload(ctx)
require.NoError(t, res.Err)
assert.Equal(t, res.FileObjectId, fileObjectId)
assert.Equal(t, res.Name, "unnamed.jpg")
@ -236,19 +249,6 @@ type uplFixture struct {
fileObjectService *mock_fileobject.MockService
}
func (fx *uplFixture) newImage(fileId domain.FileId) *testMock.MockImage {
im := testMock.NewMockImage(fx.ctrl)
im.EXPECT().FileId().Return(fileId).AnyTimes()
return im
}
func (fx *uplFixture) newFile(fileId domain.FileId, meta *files.FileMeta) *testMock.MockFile {
f := testMock.NewMockFile(fx.ctrl)
f.EXPECT().FileId().Return(fileId).AnyTimes()
f.EXPECT().Meta().Return(meta).AnyTimes()
return f
}
func (fx *uplFixture) tearDown() {
fx.ctrl.Finish()
}

View file

@ -40,7 +40,7 @@ type image struct {
func selectAndSortResizeVariants(variants []*storage.FileInfo) []*storage.FileInfo {
onlyResizeVariants := variants[:0]
for _, variant := range variants {
if variant.Mill == mill.ImageResizeId {
if variant.Mill == mill.ImageResizeId || variant.Mill == mill.BlobId {
onlyResizeVariants = append(onlyResizeVariants, variant)
}
}

View file

@ -13,6 +13,8 @@ import (
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/mill"
"github.com/anyproto/anytype-heart/pkg/lib/pb/storage"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -157,3 +159,86 @@ func testAddImageWithRichExifData(t *testing.T, fx *fixture) *AddResult {
got.Commit()
return got
}
func TestSelectAndSortResizeVariants(t *testing.T) {
t.Run("with resize variants", func(t *testing.T) {
got := selectAndSortResizeVariants([]*storage.FileInfo{
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(200),
},
},
},
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(100),
},
},
},
{
Mill: mill.ImageExifId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(300),
},
},
},
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(300),
},
},
},
})
want := []*storage.FileInfo{
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(100),
},
},
},
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(200),
},
},
},
{
Mill: mill.ImageResizeId,
Meta: &types.Struct{
Fields: map[string]*types.Value{
"width": pbtypes.Int64(300),
},
},
},
}
assert.Equal(t, want, got)
})
t.Run("with blob variant", func(t *testing.T) {
got := selectAndSortResizeVariants([]*storage.FileInfo{
{
Mill: mill.BlobId,
},
})
want := []*storage.FileInfo{
{
Mill: mill.BlobId,
},
}
assert.Equal(t, want, got)
})
}

View file

@ -1,7 +1,9 @@
package files
import (
"bytes"
"context"
"io"
"os"
"testing"
"time"
@ -102,6 +104,52 @@ func TestImageAddWithCustomEncryptionKeys(t *testing.T) {
assertCustomEncryptionKeys(t, fx, got, customKeys)
}
func TestImageAddReuse(t *testing.T) {
fx := newFixture(t)
f, err := os.Open("../../pkg/lib/mill/testdata/image.jpeg")
require.NoError(t, err)
defer f.Close()
fileName := "myFile"
lastModifiedDate := time.Now()
opts := []AddOption{
WithName(fileName),
WithLastModifiedDate(lastModifiedDate.Unix()),
WithReader(f),
}
got1, err := fx.ImageAdd(context.Background(), spaceId, opts...)
require.NoError(t, err)
got1.Commit()
f.Seek(0, 0)
got2, err := fx.ImageAdd(context.Background(), spaceId, opts...)
require.NoError(t, err)
got2.Commit()
require.True(t, got2.IsExisting)
require.Equal(t, got1.FileId.String(), got2.FileId.String())
require.Equal(t, got1.EncryptionKeys.EncryptionKeys, got2.EncryptionKeys.EncryptionKeys)
b, err := io.ReadAll(f)
require.NoError(t, err)
b[10000] = 0x00
// patch the original image so it will have the different source hash, but the same(empty) exif
patchedReader := bytes.NewReader(b)
opts = []AddOption{
WithName(fileName),
WithLastModifiedDate(lastModifiedDate.Unix()),
WithReader(patchedReader),
}
// exif will be the same but images are different
got3, err := fx.ImageAdd(context.Background(), spaceId, opts...)
require.NoError(t, err)
got3.Commit()
fileId3 := got3.FileId.String()
require.NotEqual(t, got1.FileId.String(), fileId3)
require.False(t, got3.IsExisting)
}
func assertCustomEncryptionKeys(t *testing.T, fx *fixture, got *AddResult, customKeys map[string]string) {
encKeys, err := fx.fileStore.GetFileKeys(got.FileId)
require.NoError(t, err)

View file

@ -23,7 +23,6 @@ import (
"github.com/anyproto/anytype-heart/core/event"
"github.com/anyproto/anytype-heart/core/filestorage/rpcstore"
"github.com/anyproto/anytype-heart/core/wallet"
"github.com/anyproto/anytype-heart/pkg/lib/datastore"
"github.com/anyproto/anytype-heart/space/spacecore/storage"
)
@ -51,7 +50,6 @@ type fileStorage struct {
cfg *config.Config
flatfsPath string
provider datastore.Datastore
rpcStore rpcstore.Service
spaceStorage storage.ClientStorage
eventSender event.Sender

View file

@ -69,8 +69,6 @@ type fileSync struct {
rpcStore rpcstore.RpcStore
loopCtx context.Context
loopCancel context.CancelFunc
uploadPingCh chan struct{}
removePingCh chan struct{}
dagService ipld.DAGService
fileStore filestore.FileStore
eventSender event.Sender
@ -97,8 +95,6 @@ func (s *fileSync) Init(a *app.App) (err error) {
s.dagService = app.MustComponent[fileservice.FileService](a).DAGService()
s.fileStore = app.MustComponent[filestore.FileStore](a)
s.eventSender = app.MustComponent[event.Sender](a)
s.removePingCh = make(chan struct{})
s.uploadPingCh = make(chan struct{})
db, err := s.dbProvider.LocalStorage()
if err != nil {
return

View file

@ -23,12 +23,6 @@ func newFileSyncStore(db *badger.DB) (*fileSyncStore, error) {
return s, nil
}
func (s *fileSyncStore) updateTxn(f func(txn *badger.Txn) error) error {
return badgerhelper.RetryOnConflict(func() error {
return s.db.Update(f)
})
}
func (s *fileSyncStore) setNodeUsage(usage NodeUsage) error {
data, err := json.Marshal(usage)
if err != nil {

View file

@ -9,10 +9,6 @@ import (
"github.com/anyproto/anytype-heart/pb"
)
type limitSetter interface {
SetLimit(limit int)
}
func TestSpaceUsageUpdate(t *testing.T) {
const limit = 1024 * 1024 * 1024
fx := newFixture(t, limit)

View file

@ -125,7 +125,7 @@ func (m *clientManager) onTaskFinished(t *task, c *client, taskErr error) {
}
}
log.Debug("finishing task task", zap.String("cid", t.cid.String()))
t.ready <- result{cid: t.cid, err: taskErr}
t.ready <- result{err: taskErr}
t.release()
}

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