mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-07 21:37:04 +09:00
GO-5297 Merge branch 'main' of github.com:anyproto/anytype-heart into go-5297-push-service-1st-iteration
This commit is contained in:
commit
601c118b3c
95 changed files with 13519 additions and 24273 deletions
|
@ -237,9 +237,11 @@ packages:
|
|||
github.com/anyproto/anytype-heart/core/identity:
|
||||
interfaces:
|
||||
Service:
|
||||
github.com/anyproto/anytype-heart/pb/service:
|
||||
github.com/anyproto/anytype-heart/core/api/apicore:
|
||||
interfaces:
|
||||
ClientCommandsServer:
|
||||
AccountService:
|
||||
ExportService:
|
||||
ClientCommands:
|
||||
github.com/anyproto/anytype-heart/core/block/template:
|
||||
interfaces:
|
||||
Service:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<env name="ANYTYPE_GATEWAY_ADDR" value="127.0.0.1:31006" />
|
||||
<env name="ANYTYPE_GRPC_ADDR" value="127.0.0.1:31007" />
|
||||
<env name="ANYTYPE_GRPCWEB_ADDR" value="127.0.0.1:31008" />
|
||||
<env name="ANYTYPE_API_DEBUG" value="1" />
|
||||
<env name="CC" value="/usr/bin/cc" />
|
||||
<env name="CXX" value="/usr/bin/c++" />
|
||||
<env name="CGO_CFLAGS" value="-Wno-deprecated-declarations -Wno-deprecated-non-prototype -Wno-xor-used-as-pow" />
|
||||
|
|
|
@ -25,366 +25,365 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
|||
func init() { proto.RegisterFile("pb/protos/service/service.proto", fileDescriptor_93a29dc403579097) }
|
||||
|
||||
var fileDescriptor_93a29dc403579097 = []byte{
|
||||
// 5733 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x5f, 0x6f, 0x1d, 0x49,
|
||||
0x56, 0xc0, 0xc7, 0x2f, 0x0c, 0xf4, 0xb2, 0x03, 0xdc, 0x81, 0x61, 0x76, 0xd8, 0x4d, 0x32, 0x99,
|
||||
0xc4, 0x4e, 0xe2, 0xf8, 0x3a, 0x93, 0xcc, 0x3f, 0x76, 0x91, 0xe0, 0xc6, 0x8e, 0x3d, 0x77, 0xc7,
|
||||
0x76, 0x8c, 0xef, 0x75, 0x22, 0x46, 0x42, 0xa2, 0x7d, 0xbb, 0x7c, 0xdd, 0xb8, 0x6f, 0x57, 0x6f,
|
||||
0x77, 0x5d, 0x27, 0x77, 0x11, 0x08, 0x04, 0x02, 0x81, 0x16, 0xb1, 0xe2, 0x9f, 0xe0, 0x09, 0x89,
|
||||
0x4f, 0xc0, 0xc7, 0xe0, 0x71, 0x1f, 0x79, 0x44, 0x33, 0xdf, 0x03, 0xa1, 0xae, 0xaa, 0xae, 0x3f,
|
||||
0xa7, 0xcf, 0xa9, 0x6e, 0xcf, 0x3e, 0x25, 0xf2, 0xf9, 0x9d, 0x73, 0xaa, 0xba, 0x4e, 0x55, 0x9d,
|
||||
0xaa, 0xae, 0xae, 0x1b, 0xdd, 0x2c, 0xce, 0xb6, 0x8b, 0x92, 0x0b, 0x5e, 0x6d, 0x57, 0xac, 0xbc,
|
||||
0x4a, 0x67, 0xac, 0xf9, 0x77, 0x28, 0xff, 0x3c, 0x78, 0x33, 0xce, 0x57, 0x62, 0x55, 0xb0, 0xf7,
|
||||
0xde, 0xb5, 0xe4, 0x8c, 0x2f, 0x16, 0x71, 0x9e, 0x54, 0x0a, 0x79, 0xef, 0x1d, 0x2b, 0x61, 0x57,
|
||||
0x2c, 0x17, 0xfa, 0xef, 0x8f, 0x7f, 0xf2, 0x7f, 0x6b, 0xd1, 0x5b, 0x3b, 0x59, 0xca, 0x72, 0xb1,
|
||||
0xa3, 0x35, 0x06, 0x5f, 0x46, 0xdf, 0x1e, 0x15, 0xc5, 0x3e, 0x13, 0x2f, 0x58, 0x59, 0xa5, 0x3c,
|
||||
0x1f, 0x7c, 0x30, 0xd4, 0x0e, 0x86, 0x27, 0xc5, 0x6c, 0x38, 0x2a, 0x8a, 0xa1, 0x15, 0x0e, 0x4f,
|
||||
0xd8, 0x8f, 0x96, 0xac, 0x12, 0xef, 0xdd, 0x09, 0x43, 0x55, 0xc1, 0xf3, 0x8a, 0x0d, 0xce, 0xa3,
|
||||
0x5f, 0x1b, 0x15, 0xc5, 0x84, 0x89, 0x5d, 0x56, 0x57, 0x60, 0x22, 0x62, 0xc1, 0x06, 0x1b, 0x2d,
|
||||
0x55, 0x1f, 0x30, 0x3e, 0xee, 0x75, 0x83, 0xda, 0xcf, 0x34, 0xfa, 0x56, 0xed, 0xe7, 0x62, 0x29,
|
||||
0x12, 0xfe, 0x2a, 0x1f, 0xbc, 0xdf, 0x56, 0xd4, 0x22, 0x63, 0xfb, 0x76, 0x08, 0xd1, 0x56, 0x5f,
|
||||
0x46, 0xbf, 0xfc, 0x32, 0xce, 0x32, 0x26, 0x76, 0x4a, 0x56, 0x17, 0xdc, 0xd7, 0x51, 0xa2, 0xa1,
|
||||
0x92, 0x19, 0xbb, 0x1f, 0x04, 0x19, 0x6d, 0xf8, 0xcb, 0xe8, 0xdb, 0x4a, 0x72, 0xc2, 0x66, 0xfc,
|
||||
0x8a, 0x95, 0x03, 0x54, 0x4b, 0x0b, 0x89, 0x47, 0xde, 0x82, 0xa0, 0xed, 0x1d, 0x9e, 0x5f, 0xb1,
|
||||
0x52, 0xe0, 0xb6, 0xb5, 0x30, 0x6c, 0xdb, 0x42, 0xda, 0xf6, 0xdf, 0xae, 0x45, 0xdf, 0x1d, 0xcd,
|
||||
0x66, 0x7c, 0x99, 0x8b, 0x03, 0x3e, 0x8b, 0xb3, 0x83, 0x34, 0xbf, 0x3c, 0x62, 0xaf, 0x76, 0x2e,
|
||||
0x6a, 0x3e, 0x9f, 0xb3, 0xc1, 0x13, 0xff, 0xa9, 0x2a, 0x74, 0x68, 0xd8, 0xa1, 0x0b, 0x1b, 0xdf,
|
||||
0x1f, 0x5d, 0x4f, 0x49, 0x97, 0xe5, 0x1f, 0xd6, 0xa2, 0x1b, 0xb0, 0x2c, 0x13, 0x9e, 0x5d, 0x31,
|
||||
0x5b, 0x9a, 0x8f, 0x3b, 0x0c, 0xfb, 0xb8, 0x29, 0xcf, 0x27, 0xd7, 0x55, 0xd3, 0x25, 0xca, 0xa2,
|
||||
0xb7, 0xdd, 0x70, 0x99, 0xb0, 0x4a, 0x76, 0xa7, 0xfb, 0x74, 0x44, 0x68, 0xc4, 0x78, 0x7e, 0xd0,
|
||||
0x07, 0xd5, 0xde, 0xd2, 0x68, 0xa0, 0xbd, 0x65, 0xbc, 0x32, 0xce, 0xee, 0xa1, 0x16, 0x1c, 0xc2,
|
||||
0xf8, 0xba, 0xdf, 0x83, 0xd4, 0xae, 0xfe, 0x28, 0xfa, 0x95, 0x97, 0xbc, 0xbc, 0xac, 0x8a, 0x78,
|
||||
0xc6, 0x74, 0x57, 0xb8, 0xeb, 0x6b, 0x37, 0x52, 0xd8, 0x1b, 0xd6, 0xbb, 0x30, 0x27, 0x68, 0x1b,
|
||||
0xe1, 0xf3, 0x82, 0xc1, 0x31, 0xc8, 0x2a, 0xd6, 0x42, 0x2a, 0x68, 0x21, 0xa4, 0x6d, 0x5f, 0x46,
|
||||
0x03, 0x6b, 0xfb, 0xec, 0x8f, 0xd9, 0x4c, 0x8c, 0x92, 0x04, 0xb6, 0x8a, 0xd5, 0x95, 0xc4, 0x70,
|
||||
0x94, 0x24, 0x54, 0xab, 0xe0, 0xa8, 0x76, 0xf6, 0x2a, 0x7a, 0x07, 0x38, 0x3b, 0x48, 0x2b, 0xe9,
|
||||
0x70, 0x2b, 0x6c, 0x45, 0x63, 0xc6, 0xe9, 0xb0, 0x2f, 0xae, 0x1d, 0xff, 0xf9, 0x5a, 0xf4, 0x1d,
|
||||
0xc4, 0xf3, 0x09, 0x5b, 0xf0, 0x2b, 0x36, 0x78, 0xd4, 0x6d, 0x4d, 0x91, 0xc6, 0xff, 0x87, 0xd7,
|
||||
0xd0, 0x40, 0xc2, 0x64, 0xc2, 0x32, 0x36, 0x13, 0x64, 0x98, 0x28, 0x71, 0x67, 0x98, 0x18, 0xcc,
|
||||
0xe9, 0x61, 0x8d, 0x70, 0x9f, 0x89, 0x9d, 0x65, 0x59, 0xb2, 0x5c, 0x90, 0x6d, 0x69, 0x91, 0xce,
|
||||
0xb6, 0xf4, 0x50, 0xa4, 0x3e, 0xfb, 0x4c, 0x8c, 0xb2, 0x8c, 0xac, 0x8f, 0x12, 0x77, 0xd6, 0xc7,
|
||||
0x60, 0xda, 0xc3, 0x2c, 0xfa, 0x55, 0xe7, 0x89, 0x89, 0x71, 0x7e, 0xce, 0x07, 0xf4, 0xb3, 0x90,
|
||||
0x72, 0xe3, 0x63, 0xa3, 0x93, 0x43, 0xaa, 0xf1, 0xec, 0x75, 0xc1, 0x4b, 0xba, 0x59, 0x94, 0xb8,
|
||||
0xb3, 0x1a, 0x06, 0xd3, 0x1e, 0xfe, 0x30, 0x7a, 0x4b, 0x8f, 0x92, 0xcd, 0x7c, 0x76, 0x07, 0x1d,
|
||||
0x42, 0xe1, 0x84, 0x76, 0xb7, 0x83, 0x6a, 0x99, 0x3f, 0x4c, 0xe7, 0x65, 0x3d, 0xfa, 0xe0, 0xe6,
|
||||
0xb5, 0xb4, 0xc3, 0xbc, 0xa5, 0xb4, 0x79, 0x1e, 0xfd, 0xba, 0x6f, 0x7e, 0x27, 0xce, 0x67, 0x2c,
|
||||
0x1b, 0x3c, 0x08, 0xa9, 0x2b, 0xc6, 0xb8, 0xda, 0xec, 0xc5, 0xda, 0xc1, 0x4e, 0x13, 0x7a, 0x30,
|
||||
0xfd, 0x00, 0xd5, 0x06, 0x43, 0xe9, 0x9d, 0x30, 0xd4, 0xb2, 0xbd, 0xcb, 0x32, 0x46, 0xda, 0x56,
|
||||
0xc2, 0x0e, 0xdb, 0x06, 0xd2, 0xb6, 0xcb, 0xe8, 0x37, 0x4c, 0x33, 0xd7, 0x79, 0x81, 0x94, 0xd7,
|
||||
0x93, 0xce, 0x26, 0xd1, 0x8e, 0x2e, 0x64, 0x7c, 0x3d, 0xec, 0x07, 0xb7, 0xea, 0xa3, 0x47, 0x14,
|
||||
0xbc, 0x3e, 0x60, 0x3c, 0xb9, 0x13, 0x86, 0xb4, 0xed, 0xbf, 0x5b, 0x8b, 0xbe, 0xa7, 0x65, 0xcf,
|
||||
0xf2, 0xf8, 0x2c, 0x63, 0x72, 0x8a, 0x3f, 0x62, 0xe2, 0x15, 0x2f, 0x2f, 0x27, 0xab, 0x7c, 0x46,
|
||||
0xa4, 0x33, 0x38, 0xdc, 0x91, 0xce, 0x90, 0x4a, 0xba, 0x30, 0x7f, 0x12, 0xbd, 0xdb, 0x04, 0xc5,
|
||||
0x45, 0x9c, 0xcf, 0xd9, 0x0f, 0x2b, 0x9e, 0x8f, 0x8a, 0x74, 0x94, 0x24, 0xe5, 0x60, 0x88, 0x37,
|
||||
0x3d, 0xe4, 0x4c, 0x09, 0xb6, 0x7b, 0xf3, 0x4e, 0xfa, 0xac, 0x9f, 0xb2, 0xe0, 0x05, 0x4c, 0x9f,
|
||||
0x9b, 0xc7, 0x27, 0x78, 0x41, 0xa5, 0xcf, 0x3e, 0xd2, 0xb2, 0x7a, 0x58, 0xcf, 0x41, 0xb8, 0xd5,
|
||||
0x43, 0x77, 0xd2, 0xb9, 0x1d, 0x42, 0xec, 0x1c, 0xd0, 0x3c, 0x28, 0x9e, 0x9f, 0xa7, 0xf3, 0xd3,
|
||||
0x22, 0xa9, 0xfb, 0xd0, 0x7d, 0xbc, 0xce, 0x0e, 0x42, 0xcc, 0x01, 0x04, 0xaa, 0xbd, 0xfd, 0xbd,
|
||||
0xcd, 0x32, 0xf5, 0xb8, 0xb4, 0x57, 0xf2, 0xc5, 0x01, 0x9b, 0xc7, 0xb3, 0x95, 0x1e, 0x4c, 0x3f,
|
||||
0x0a, 0x8d, 0x62, 0x90, 0x36, 0x85, 0xf8, 0xf8, 0x9a, 0x5a, 0xba, 0x3c, 0xff, 0xb1, 0x16, 0xdd,
|
||||
0xf1, 0xe2, 0x44, 0x07, 0x93, 0x2a, 0xfd, 0x28, 0x4f, 0x4e, 0x58, 0x25, 0xe2, 0x52, 0x0c, 0xbe,
|
||||
0x1f, 0x88, 0x01, 0x42, 0xc7, 0x94, 0xed, 0x07, 0xdf, 0x48, 0xd7, 0xb6, 0xfa, 0xa4, 0x9e, 0x25,
|
||||
0xf4, 0xf8, 0xe3, 0xb7, 0xba, 0x94, 0xc0, 0xd1, 0xe7, 0x76, 0x08, 0xb1, 0xad, 0x2e, 0x05, 0xe3,
|
||||
0xfc, 0x2a, 0x15, 0x6c, 0x9f, 0xe5, 0xac, 0x6c, 0xb7, 0xba, 0x52, 0xf5, 0x11, 0xa2, 0xd5, 0x09,
|
||||
0xd4, 0x8e, 0x74, 0x9e, 0x37, 0x93, 0x69, 0x6c, 0x06, 0x8c, 0xb4, 0x72, 0x8d, 0x87, 0xfd, 0x60,
|
||||
0xa2, 0x86, 0x62, 0xbf, 0x36, 0x12, 0xac, 0xa1, 0x42, 0x7a, 0xd5, 0xd0, 0xa0, 0x76, 0x61, 0xee,
|
||||
0x78, 0x3b, 0x61, 0x57, 0xfc, 0x12, 0x2e, 0xcc, 0x5d, 0x03, 0x0a, 0x20, 0x16, 0xe6, 0x28, 0x68,
|
||||
0x93, 0x0f, 0xc7, 0xcf, 0x8b, 0x94, 0xbd, 0x02, 0xc9, 0x87, 0xab, 0x5c, 0x8b, 0x89, 0xe4, 0x03,
|
||||
0xc1, 0xb4, 0x87, 0xa3, 0xe8, 0x97, 0xa4, 0xf0, 0x87, 0x3c, 0xcd, 0x07, 0x37, 0x11, 0xa5, 0x5a,
|
||||
0x60, 0xac, 0xde, 0xa2, 0x01, 0x50, 0xe2, 0xfa, 0xaf, 0x3a, 0x13, 0xb8, 0x4b, 0x28, 0x81, 0x24,
|
||||
0x60, 0xbd, 0x0b, 0xb3, 0x59, 0x9f, 0x14, 0xd6, 0xa3, 0xe5, 0xe4, 0x22, 0x2e, 0xd3, 0x7c, 0x3e,
|
||||
0xc0, 0x74, 0x1d, 0x39, 0x91, 0xf5, 0x61, 0x1c, 0x08, 0x27, 0xad, 0x38, 0x2a, 0x8a, 0xb2, 0x1e,
|
||||
0x84, 0xb1, 0x70, 0xf2, 0x91, 0x60, 0x38, 0xb5, 0x50, 0xdc, 0xdb, 0x2e, 0x9b, 0x65, 0x69, 0x1e,
|
||||
0xf4, 0xa6, 0x91, 0x3e, 0xde, 0x2c, 0x0a, 0x82, 0xf7, 0x80, 0xc5, 0x57, 0xac, 0xa9, 0x19, 0xf6,
|
||||
0x64, 0x5c, 0x20, 0x18, 0xbc, 0x00, 0xb4, 0x4b, 0x6c, 0x29, 0x3e, 0x8c, 0x2f, 0x59, 0xfd, 0x80,
|
||||
0x59, 0x3d, 0x85, 0x0f, 0x30, 0x7d, 0x8f, 0x20, 0x96, 0xd8, 0x38, 0xa9, 0x5d, 0x2d, 0xa3, 0x77,
|
||||
0xa4, 0xfc, 0x38, 0x2e, 0x45, 0x3a, 0x4b, 0x8b, 0x38, 0x6f, 0x96, 0x6e, 0xd8, 0x28, 0xd2, 0xa2,
|
||||
0x8c, 0xcb, 0xad, 0x9e, 0xb4, 0x76, 0xfb, 0xaf, 0x6b, 0xd1, 0xfb, 0xd0, 0xef, 0x31, 0x2b, 0x17,
|
||||
0xa9, 0xdc, 0x01, 0xa8, 0xd4, 0x90, 0x3f, 0xf8, 0x34, 0x6c, 0xb4, 0xa5, 0x60, 0x4a, 0xf3, 0xd9,
|
||||
0xf5, 0x15, 0x6d, 0xde, 0x37, 0xd1, 0xab, 0xa2, 0xe7, 0x65, 0xd2, 0xda, 0x21, 0x9b, 0x34, 0x4b,
|
||||
0x1d, 0x29, 0x24, 0xf2, 0xbe, 0x16, 0x04, 0x7a, 0xf8, 0x69, 0x5e, 0x35, 0xd6, 0xb1, 0x1e, 0x6e,
|
||||
0xc5, 0xc1, 0x1e, 0xee, 0x61, 0xb6, 0x87, 0x1f, 0x2f, 0xcf, 0xb2, 0xb4, 0xba, 0x48, 0xf3, 0xb9,
|
||||
0x4e, 0xf2, 0x7d, 0x5d, 0x2b, 0x86, 0x79, 0xfe, 0x46, 0x27, 0x87, 0x39, 0xd1, 0xc1, 0x42, 0x3a,
|
||||
0x01, 0x61, 0xb2, 0xd1, 0xc9, 0xd9, 0xb5, 0x97, 0x95, 0xd6, 0x8b, 0x7e, 0xb0, 0xf6, 0x72, 0x54,
|
||||
0x6b, 0x29, 0xb1, 0xf6, 0x6a, 0x53, 0x76, 0xed, 0xe5, 0xd6, 0xa1, 0xe2, 0xd9, 0x15, 0x3b, 0x2d,
|
||||
0x53, 0xb0, 0xf6, 0xf2, 0xca, 0xd7, 0x30, 0xc4, 0xda, 0x8b, 0x62, 0xed, 0x40, 0x65, 0x89, 0x7d,
|
||||
0x26, 0x26, 0x22, 0x16, 0xcb, 0x0a, 0x0c, 0x54, 0x8e, 0x0d, 0x83, 0x10, 0x03, 0x15, 0x81, 0x6a,
|
||||
0x6f, 0xbf, 0x1f, 0x45, 0x6a, 0xbf, 0x44, 0xee, 0x69, 0xf9, 0x73, 0x8f, 0xde, 0x48, 0xf1, 0x36,
|
||||
0xb4, 0xde, 0x0f, 0x10, 0x36, 0xbd, 0x52, 0x7f, 0x97, 0x5b, 0x75, 0x03, 0x54, 0x43, 0x8a, 0x88,
|
||||
0xf4, 0x0a, 0x20, 0xb0, 0xa0, 0x93, 0x0b, 0xfe, 0x0a, 0x2f, 0x68, 0x2d, 0x09, 0x17, 0x54, 0x13,
|
||||
0x76, 0xf3, 0x5c, 0x17, 0x14, 0xdb, 0x3c, 0x6f, 0x8a, 0x11, 0xda, 0x3c, 0x87, 0x8c, 0x8d, 0x19,
|
||||
0xd7, 0xf0, 0x53, 0xce, 0x2f, 0x17, 0x71, 0x79, 0x09, 0x62, 0xc6, 0x53, 0x6e, 0x18, 0x22, 0x66,
|
||||
0x28, 0xd6, 0xc6, 0x8c, 0xeb, 0xb0, 0x4e, 0xce, 0x4f, 0xcb, 0x0c, 0xc4, 0x8c, 0x67, 0x43, 0x23,
|
||||
0x44, 0xcc, 0x10, 0xa8, 0x1d, 0x9d, 0x5c, 0x6f, 0x13, 0x06, 0xb7, 0x6b, 0x3c, 0xf5, 0x09, 0xa3,
|
||||
0xb6, 0x6b, 0x10, 0x0c, 0x86, 0xd0, 0x7e, 0x19, 0x17, 0x17, 0x78, 0x08, 0x49, 0x51, 0x38, 0x84,
|
||||
0x1a, 0x04, 0xb6, 0xf7, 0x84, 0xc5, 0xe5, 0xec, 0x02, 0x6f, 0x6f, 0x25, 0x0b, 0xb7, 0xb7, 0x61,
|
||||
0x60, 0x7b, 0x2b, 0xc1, 0xcb, 0x54, 0x5c, 0x1c, 0x32, 0x11, 0xe3, 0xed, 0xed, 0x33, 0xe1, 0xf6,
|
||||
0x6e, 0xb1, 0x36, 0xfb, 0x77, 0x1d, 0x4e, 0x96, 0x67, 0xd5, 0xac, 0x4c, 0xcf, 0xd8, 0x20, 0x60,
|
||||
0xc5, 0x40, 0x44, 0xf6, 0x4f, 0xc2, 0xda, 0xe7, 0x4f, 0xd7, 0xa2, 0x9b, 0x4d, 0xb3, 0xf3, 0xaa,
|
||||
0xd2, 0x73, 0x9f, 0xef, 0xfe, 0x63, 0xbc, 0x7d, 0x09, 0x9c, 0x78, 0x9d, 0xd1, 0x43, 0xcd, 0xc9,
|
||||
0x0d, 0xf0, 0x22, 0x9d, 0xe6, 0x95, 0x29, 0xd4, 0xa7, 0x7d, 0xac, 0x3b, 0x0a, 0x44, 0x6e, 0xd0,
|
||||
0x4b, 0xd1, 0xa6, 0x65, 0xba, 0x7d, 0x1a, 0xd9, 0x38, 0xa9, 0x40, 0x5a, 0xd6, 0x3c, 0x6f, 0x87,
|
||||
0x20, 0xd2, 0x32, 0x9c, 0x84, 0xa1, 0xb0, 0x5f, 0xf2, 0x65, 0x51, 0x75, 0x84, 0x02, 0x80, 0xc2,
|
||||
0xa1, 0xd0, 0x86, 0xb5, 0xcf, 0xd7, 0xd1, 0x6f, 0xba, 0xe1, 0xe7, 0x3e, 0xec, 0x2d, 0x3a, 0xa6,
|
||||
0xb0, 0x47, 0x3c, 0xec, 0x8b, 0xdb, 0x8c, 0xa2, 0xf1, 0x2c, 0x76, 0x99, 0x88, 0xd3, 0xac, 0x1a,
|
||||
0xac, 0xe3, 0x36, 0x1a, 0x39, 0x91, 0x51, 0x60, 0x1c, 0x1c, 0xdf, 0x76, 0x97, 0x45, 0x96, 0xce,
|
||||
0xda, 0x2f, 0x93, 0xb4, 0xae, 0x11, 0x87, 0xc7, 0x37, 0x17, 0x83, 0xe3, 0x75, 0x9d, 0xfa, 0xc9,
|
||||
0xff, 0x4c, 0x57, 0x05, 0xc3, 0xc7, 0x6b, 0x0f, 0x09, 0x8f, 0xd7, 0x10, 0x85, 0xf5, 0x99, 0x30,
|
||||
0x71, 0x10, 0xaf, 0xf8, 0x92, 0x18, 0xaf, 0x8d, 0x38, 0x5c, 0x1f, 0x17, 0xb3, 0x6b, 0x03, 0xe3,
|
||||
0x61, 0x9c, 0x0b, 0x56, 0xe6, 0x71, 0xb6, 0x97, 0xc5, 0xf3, 0x6a, 0x40, 0x8c, 0x31, 0x3e, 0x45,
|
||||
0xac, 0x0d, 0x68, 0x1a, 0x79, 0x8c, 0xe3, 0x6a, 0x2f, 0xbe, 0xe2, 0x65, 0x2a, 0xe8, 0xc7, 0x68,
|
||||
0x91, 0xce, 0xc7, 0xe8, 0xa1, 0xa8, 0xb7, 0x51, 0x39, 0xbb, 0x48, 0xaf, 0x58, 0x12, 0xf0, 0xd6,
|
||||
0x20, 0x3d, 0xbc, 0x39, 0x28, 0xd2, 0x68, 0x13, 0xbe, 0x2c, 0x67, 0x8c, 0x6c, 0x34, 0x25, 0xee,
|
||||
0x6c, 0x34, 0x83, 0x69, 0x0f, 0x7f, 0xb5, 0x16, 0xfd, 0x96, 0x92, 0xba, 0x6f, 0x78, 0x76, 0xe3,
|
||||
0xea, 0xe2, 0x8c, 0xc7, 0x65, 0x32, 0xf8, 0x10, 0xb3, 0x83, 0xa2, 0xc6, 0xf5, 0xe3, 0xeb, 0xa8,
|
||||
0xc0, 0xc7, 0x5a, 0xe7, 0xdd, 0xb6, 0xc7, 0xa1, 0x8f, 0xd5, 0x43, 0xc2, 0x8f, 0x15, 0xa2, 0x70,
|
||||
0x00, 0x91, 0x72, 0xb5, 0x01, 0xb8, 0x4e, 0xea, 0xfb, 0xbb, 0x80, 0x1b, 0x9d, 0x1c, 0x1c, 0x1f,
|
||||
0x6b, 0xa1, 0x1f, 0x2d, 0x5b, 0x94, 0x0d, 0x3c, 0x62, 0x86, 0x7d, 0x71, 0xd2, 0xb3, 0xe9, 0x15,
|
||||
0x61, 0xcf, 0xad, 0x9e, 0x31, 0xec, 0x8b, 0x13, 0x9e, 0x9d, 0x61, 0x2d, 0xe4, 0x19, 0x19, 0xda,
|
||||
0x86, 0x7d, 0x71, 0x98, 0x7d, 0x69, 0xa6, 0x99, 0x17, 0x1e, 0x04, 0xec, 0xc0, 0xb9, 0x61, 0xb3,
|
||||
0x17, 0xab, 0x1d, 0xfe, 0xcd, 0x5a, 0xf4, 0x5d, 0xeb, 0xf1, 0x90, 0x27, 0xe9, 0xf9, 0x4a, 0x41,
|
||||
0x2f, 0xe2, 0x6c, 0xc9, 0xaa, 0xc1, 0x63, 0xca, 0x5a, 0x9b, 0x35, 0x25, 0x78, 0x72, 0x2d, 0x1d,
|
||||
0xd8, 0x77, 0x46, 0x45, 0x91, 0xad, 0xa6, 0x6c, 0x51, 0x64, 0x64, 0xdf, 0xf1, 0x90, 0x70, 0xdf,
|
||||
0x81, 0x28, 0xcc, 0xca, 0xa7, 0xbc, 0xce, 0xf9, 0xd1, 0xac, 0x5c, 0x8a, 0xc2, 0x59, 0x79, 0x83,
|
||||
0xc0, 0x5c, 0x69, 0xca, 0x77, 0x78, 0x96, 0xb1, 0x99, 0x68, 0x9f, 0x12, 0x31, 0x9a, 0x96, 0x08,
|
||||
0xe7, 0x4a, 0x80, 0xb4, 0xbb, 0x72, 0xcd, 0x1a, 0x32, 0x2e, 0xd9, 0xd3, 0xd5, 0x41, 0x9a, 0x5f,
|
||||
0x0e, 0xf0, 0xb4, 0xc0, 0x02, 0xc4, 0xae, 0x1c, 0x0a, 0xc2, 0xb5, 0xea, 0x69, 0x9e, 0x70, 0x7c,
|
||||
0xad, 0x5a, 0x4b, 0xc2, 0x6b, 0x55, 0x4d, 0x40, 0x93, 0x27, 0x8c, 0x32, 0x59, 0x4b, 0xc2, 0x26,
|
||||
0x35, 0x81, 0x0d, 0x85, 0xfa, 0x4d, 0x11, 0x39, 0x14, 0x82, 0x77, 0x43, 0x1b, 0x9d, 0x1c, 0x8c,
|
||||
0xd0, 0x66, 0xd1, 0xba, 0xc7, 0xc4, 0xec, 0x02, 0x8f, 0x50, 0x0f, 0x09, 0x47, 0x28, 0x44, 0x61,
|
||||
0x95, 0xa6, 0xdc, 0x2c, 0xba, 0xd7, 0xf1, 0xf8, 0x68, 0x2d, 0xb8, 0x37, 0x3a, 0x39, 0xb8, 0x8c,
|
||||
0x1c, 0x2f, 0xe4, 0x33, 0x43, 0x83, 0x5c, 0xc9, 0xc2, 0xcb, 0x48, 0xc3, 0xc0, 0xd2, 0x2b, 0x81,
|
||||
0xdc, 0xcb, 0x5a, 0xa7, 0x15, 0xbd, 0xdd, 0xac, 0x8d, 0x4e, 0x4e, 0x3b, 0xf9, 0x67, 0xb3, 0x8c,
|
||||
0x53, 0xd2, 0x23, 0x5e, 0xf7, 0x91, 0x17, 0x71, 0x96, 0x26, 0xb1, 0x60, 0x53, 0x7e, 0xc9, 0x72,
|
||||
0x7c, 0xc5, 0xa4, 0x4b, 0xab, 0xf8, 0xa1, 0xa7, 0x10, 0x5e, 0x31, 0x85, 0x15, 0x61, 0x9c, 0x28,
|
||||
0xfa, 0xb4, 0x62, 0x3b, 0x71, 0x45, 0x8c, 0x64, 0x1e, 0x12, 0x8e, 0x13, 0x88, 0xc2, 0x7c, 0x55,
|
||||
0xc9, 0x9f, 0xbd, 0x2e, 0x58, 0x99, 0xb2, 0x7c, 0xc6, 0xf0, 0x7c, 0x15, 0x52, 0xe1, 0x7c, 0x15,
|
||||
0xa1, 0xe1, 0x5a, 0x6d, 0x37, 0x16, 0xec, 0xe9, 0x6a, 0x9a, 0x2e, 0x58, 0x25, 0xe2, 0x45, 0x81,
|
||||
0xaf, 0xd5, 0x00, 0x14, 0x5e, 0xab, 0xb5, 0xe1, 0xd6, 0xd6, 0x90, 0x19, 0x10, 0xdb, 0x87, 0xcb,
|
||||
0x20, 0x11, 0x38, 0x5c, 0x46, 0xa0, 0xf0, 0xc1, 0x5a, 0x00, 0x7d, 0x49, 0xd0, 0xb2, 0x12, 0x7c,
|
||||
0x49, 0x40, 0xd3, 0xad, 0x0d, 0x37, 0xc3, 0x4c, 0xea, 0xae, 0xd9, 0x51, 0xf4, 0x89, 0xdb, 0x45,
|
||||
0x37, 0x7b, 0xb1, 0xf8, 0x0e, 0xdf, 0x09, 0xcb, 0x62, 0x39, 0x6d, 0x05, 0xb6, 0xd1, 0x1a, 0xa6,
|
||||
0xcf, 0x0e, 0x9f, 0xc3, 0x6a, 0x87, 0x7f, 0xb1, 0x16, 0xbd, 0x87, 0x79, 0x7c, 0x5e, 0x48, 0xbf,
|
||||
0x8f, 0xba, 0x6d, 0x29, 0x92, 0x38, 0x3d, 0x17, 0xd6, 0xb0, 0x07, 0x40, 0x1a, 0x91, 0x3d, 0x5c,
|
||||
0xa7, 0x0b, 0xe0, 0x27, 0x6d, 0xa6, 0xfc, 0x90, 0x23, 0x0e, 0x80, 0x84, 0x78, 0xbb, 0x1e, 0xf2,
|
||||
0xcb, 0x55, 0x81, 0xf5, 0x90, 0xb1, 0xa1, 0xc5, 0xc4, 0x7a, 0x08, 0xc1, 0x6c, 0xef, 0x74, 0xab,
|
||||
0xf7, 0x32, 0x15, 0x17, 0x32, 0xdf, 0x02, 0xbd, 0xd3, 0x2b, 0xab, 0x81, 0x88, 0xde, 0x49, 0xc2,
|
||||
0x30, 0x23, 0x69, 0xc0, 0xba, 0x6f, 0x62, 0x63, 0xb9, 0x31, 0xe4, 0xf6, 0xcc, 0x7b, 0xdd, 0x20,
|
||||
0x8c, 0xd7, 0x46, 0xac, 0x97, 0x3e, 0x0f, 0x42, 0x16, 0xc0, 0xf2, 0x67, 0xb3, 0x17, 0xab, 0x1d,
|
||||
0xfe, 0x59, 0xf4, 0x9d, 0x56, 0xc5, 0xf6, 0x58, 0x2c, 0x96, 0x25, 0x4b, 0x06, 0xdb, 0x1d, 0xe5,
|
||||
0x6e, 0x40, 0xe3, 0xfa, 0x51, 0x7f, 0x85, 0x56, 0x8e, 0xde, 0x70, 0x2a, 0xac, 0x4c, 0x19, 0x1e,
|
||||
0x87, 0x4c, 0xfa, 0x6c, 0x30, 0x47, 0xa7, 0x75, 0x5a, 0xcb, 0x6c, 0x37, 0xba, 0x46, 0x57, 0x71,
|
||||
0x9a, 0xc9, 0x97, 0xb5, 0x1f, 0x86, 0x8c, 0x7a, 0x68, 0x70, 0x99, 0x4d, 0xaa, 0xb4, 0x46, 0x66,
|
||||
0xd9, 0xc7, 0x9d, 0xe5, 0xd9, 0x43, 0x7a, 0x24, 0x40, 0x56, 0x67, 0x5b, 0x3d, 0x69, 0xed, 0x56,
|
||||
0x34, 0x53, 0x5e, 0xfd, 0x67, 0x37, 0xc8, 0x31, 0xaf, 0x5a, 0x15, 0x89, 0xf4, 0xad, 0x9e, 0xb4,
|
||||
0xf6, 0xfa, 0xa7, 0xd1, 0xbb, 0x6d, 0xaf, 0x7a, 0x22, 0xda, 0xee, 0x34, 0x05, 0xe6, 0xa2, 0x47,
|
||||
0xfd, 0x15, 0xb4, 0xfb, 0x7f, 0x33, 0xfb, 0xd2, 0xca, 0xff, 0x8c, 0x2f, 0x16, 0x2c, 0x4f, 0x58,
|
||||
0xd2, 0x68, 0x54, 0xf5, 0xfa, 0xe9, 0x33, 0xda, 0xae, 0x51, 0x18, 0xba, 0x1a, 0xa6, 0x44, 0xbf,
|
||||
0xfd, 0x0d, 0x34, 0x75, 0xd1, 0xfe, 0x6b, 0x2d, 0xba, 0x8f, 0x16, 0xad, 0x09, 0x5c, 0xaf, 0x88,
|
||||
0xbf, 0xd7, 0xc7, 0x11, 0xa6, 0x69, 0x8a, 0x3a, 0xfa, 0x39, 0x2c, 0xe8, 0x22, 0xff, 0xfb, 0x5a,
|
||||
0x74, 0xdb, 0x2a, 0xd6, 0xe1, 0xbd, 0xc3, 0xf3, 0xf3, 0x2c, 0x9d, 0x09, 0xf9, 0x46, 0x56, 0xab,
|
||||
0xd0, 0x8f, 0x93, 0xd2, 0xe8, 0x7e, 0x9c, 0x01, 0x4d, 0xbb, 0x78, 0xfd, 0x3c, 0xad, 0x04, 0x2f,
|
||||
0x57, 0x93, 0x0b, 0xfe, 0xaa, 0xf9, 0x3c, 0xc9, 0x1f, 0x97, 0x35, 0x30, 0x74, 0x08, 0x62, 0xf1,
|
||||
0x8a, 0x93, 0x2d, 0x57, 0xf6, 0x33, 0xa6, 0x8a, 0x70, 0xe5, 0x10, 0x1d, 0xae, 0x7c, 0xd2, 0xce,
|
||||
0x4a, 0x4d, 0xad, 0xec, 0x37, 0x57, 0x1b, 0x78, 0x51, 0xdb, 0xdf, 0x5d, 0xdd, 0xeb, 0x06, 0x6d,
|
||||
0x6e, 0xaa, 0xc5, 0xbb, 0xe9, 0xf9, 0xb9, 0xa9, 0x13, 0x5e, 0x52, 0x17, 0x21, 0x72, 0x53, 0x02,
|
||||
0xb5, 0xcb, 0xab, 0xbd, 0x34, 0x63, 0xf2, 0xdd, 0xcd, 0xf3, 0xf3, 0xf3, 0x8c, 0xc7, 0x09, 0x58,
|
||||
0x5e, 0xd5, 0xe2, 0xa1, 0x2b, 0x27, 0x96, 0x57, 0x18, 0x67, 0x4f, 0x85, 0xd4, 0xd2, 0x3a, 0xba,
|
||||
0xf3, 0x59, 0x9a, 0xc1, 0xd3, 0xcd, 0x52, 0xd3, 0x08, 0x89, 0x53, 0x21, 0x2d, 0xc8, 0xa6, 0x40,
|
||||
0xb5, 0xa8, 0x8e, 0xca, 0xa6, 0xfc, 0x77, 0xdb, 0x8a, 0x8e, 0x98, 0x48, 0x81, 0x10, 0xcc, 0xee,
|
||||
0x32, 0xd4, 0xc2, 0xd3, 0x42, 0x1a, 0xbf, 0xd5, 0xd6, 0x52, 0x12, 0x62, 0x97, 0xc1, 0x27, 0xec,
|
||||
0x6a, 0xb9, 0xfe, 0xfb, 0x2e, 0x7f, 0x95, 0x4b, 0xa3, 0xb7, 0xdb, 0x2a, 0x8d, 0x8c, 0x58, 0x2d,
|
||||
0x43, 0x46, 0x1b, 0xfe, 0x22, 0xfa, 0x45, 0x69, 0xb8, 0xe4, 0xc5, 0xe0, 0x06, 0xa2, 0x50, 0x3a,
|
||||
0x67, 0x81, 0x6f, 0x92, 0x72, 0x7b, 0x88, 0xc4, 0xc4, 0xc6, 0x69, 0x15, 0xcf, 0xe1, 0x01, 0x7e,
|
||||
0xdb, 0xe2, 0x52, 0x4a, 0x1c, 0x22, 0x69, 0x53, 0x7e, 0x54, 0x1c, 0xf1, 0x44, 0x5b, 0x47, 0x6a,
|
||||
0x68, 0x84, 0xa1, 0xa8, 0x70, 0x21, 0x9b, 0xb6, 0x1e, 0xc5, 0x57, 0xe9, 0xdc, 0xa4, 0x16, 0x6a,
|
||||
0x00, 0xab, 0x40, 0xda, 0x6a, 0x99, 0xa1, 0x03, 0x11, 0x69, 0x2b, 0x09, 0x6b, 0x9f, 0xff, 0xb4,
|
||||
0x16, 0xdd, 0xb2, 0xcc, 0x7e, 0xb3, 0x2f, 0x3b, 0xce, 0xcf, 0x79, 0x9d, 0xe4, 0x1e, 0xa4, 0xf9,
|
||||
0x65, 0x35, 0xf8, 0x84, 0x32, 0x89, 0xf3, 0xa6, 0x28, 0x9f, 0x5e, 0x5b, 0xcf, 0xae, 0x4f, 0x9a,
|
||||
0x4d, 0x4b, 0x7b, 0x72, 0x41, 0x69, 0x80, 0xf5, 0x89, 0xd9, 0xdb, 0x84, 0x1c, 0xb1, 0x3e, 0x09,
|
||||
0xf1, 0xb6, 0x89, 0x8d, 0xf3, 0x8c, 0xe7, 0xb0, 0x89, 0xad, 0x85, 0x5a, 0x48, 0x34, 0x71, 0x0b,
|
||||
0xb2, 0xe3, 0x71, 0x23, 0x52, 0xfb, 0x6b, 0xa3, 0x2c, 0x03, 0xe3, 0xb1, 0x51, 0x35, 0x00, 0x31,
|
||||
0x1e, 0xa3, 0xa0, 0xf6, 0x73, 0x12, 0x7d, 0xab, 0x7e, 0xa4, 0xc7, 0x25, 0xbb, 0x4a, 0x19, 0x3c,
|
||||
0x64, 0xe3, 0x48, 0x88, 0xfe, 0xef, 0x13, 0xb6, 0x67, 0x9d, 0xe6, 0x55, 0x91, 0xc5, 0xd5, 0x85,
|
||||
0x3e, 0x76, 0xe1, 0xd7, 0xb9, 0x11, 0xc2, 0x83, 0x17, 0x77, 0x3b, 0x28, 0x3b, 0xa8, 0x37, 0x32,
|
||||
0x33, 0xc4, 0xac, 0xe3, 0xaa, 0xad, 0x61, 0x66, 0xa3, 0x93, 0xb3, 0xef, 0x36, 0xf6, 0xe3, 0x2c,
|
||||
0x63, 0xe5, 0xaa, 0x91, 0x1d, 0xc6, 0x79, 0x7a, 0xce, 0x2a, 0x01, 0xde, 0x6d, 0x68, 0x6a, 0x08,
|
||||
0x31, 0xe2, 0xdd, 0x46, 0x00, 0xb7, 0xeb, 0x36, 0xe0, 0x79, 0x9c, 0x27, 0xec, 0x35, 0x58, 0xb7,
|
||||
0x41, 0x3b, 0x92, 0x21, 0xd6, 0x6d, 0x14, 0x6b, 0xf7, 0xf8, 0x9f, 0x66, 0x7c, 0x76, 0xa9, 0xa7,
|
||||
0x00, 0xbf, 0x81, 0xa5, 0x04, 0xce, 0x01, 0xb7, 0x43, 0x88, 0x9d, 0x04, 0xa4, 0xe0, 0x84, 0x15,
|
||||
0x59, 0x3c, 0x83, 0x27, 0xad, 0x94, 0x8e, 0x96, 0x11, 0x93, 0x00, 0x64, 0x40, 0x71, 0xf5, 0x09,
|
||||
0x2e, 0xac, 0xb8, 0xe0, 0x00, 0xd7, 0xed, 0x10, 0x62, 0xa7, 0x41, 0x29, 0x98, 0x14, 0x59, 0x2a,
|
||||
0x40, 0x37, 0x50, 0x1a, 0x52, 0x42, 0x74, 0x03, 0x9f, 0x00, 0x26, 0x0f, 0x59, 0x39, 0x67, 0xa8,
|
||||
0x49, 0x29, 0x09, 0x9a, 0x6c, 0x08, 0x7b, 0xac, 0x5c, 0xd5, 0x9d, 0x17, 0x2b, 0x70, 0xac, 0x5c,
|
||||
0x57, 0x8b, 0x17, 0x2b, 0xe2, 0x58, 0xb9, 0x07, 0x80, 0x22, 0x1e, 0xc7, 0x95, 0xc0, 0x8b, 0x28,
|
||||
0x25, 0xc1, 0x22, 0x36, 0x84, 0x9d, 0xa3, 0x55, 0x11, 0x97, 0x02, 0xcc, 0xd1, 0xba, 0x00, 0xce,
|
||||
0x59, 0x83, 0x9b, 0xa4, 0xdc, 0x8e, 0x24, 0xaa, 0x55, 0x98, 0xd8, 0x4b, 0x59, 0x96, 0x54, 0x60,
|
||||
0x24, 0xd1, 0xcf, 0xbd, 0x91, 0x12, 0x23, 0x49, 0x9b, 0x02, 0xa1, 0xa4, 0xdf, 0x84, 0x60, 0xb5,
|
||||
0x03, 0x2f, 0x41, 0x6e, 0x87, 0x10, 0x3b, 0x3e, 0x35, 0x85, 0xde, 0x89, 0xcb, 0x32, 0xad, 0x27,
|
||||
0xff, 0x75, 0xbc, 0x40, 0x8d, 0x9c, 0x18, 0x9f, 0x30, 0x0e, 0x74, 0xaf, 0x66, 0xe0, 0xc6, 0x0a,
|
||||
0x06, 0x87, 0xee, 0x0f, 0x82, 0x8c, 0xcd, 0x38, 0xa5, 0xc4, 0x79, 0x59, 0x8e, 0x3d, 0x4d, 0xe4,
|
||||
0x5d, 0xf9, 0x7a, 0x17, 0xe6, 0x7c, 0xe1, 0x66, 0x5c, 0x1c, 0xf2, 0x2b, 0x36, 0xe5, 0xcf, 0x5e,
|
||||
0xa7, 0x55, 0xbd, 0xdc, 0xd2, 0x33, 0xf7, 0x13, 0xc2, 0x12, 0x06, 0x13, 0x5f, 0xb8, 0x75, 0x2a,
|
||||
0xd9, 0x04, 0x02, 0x94, 0xe5, 0x88, 0xbd, 0x42, 0x13, 0x08, 0x68, 0xd1, 0x70, 0x44, 0x02, 0x11,
|
||||
0xe2, 0xed, 0x8e, 0x99, 0x71, 0xae, 0xaf, 0x35, 0x98, 0xf2, 0x26, 0x97, 0xa3, 0xac, 0x41, 0x90,
|
||||
0xd8, 0xb4, 0x08, 0x2a, 0xd8, 0xf5, 0xa5, 0xf1, 0x6f, 0xbb, 0xd8, 0x3d, 0xc2, 0x4e, 0xbb, 0x9b,
|
||||
0xdd, 0xef, 0x41, 0x22, 0xae, 0xec, 0x89, 0x0f, 0xca, 0x55, 0xfb, 0xc0, 0xc7, 0xfd, 0x1e, 0xa4,
|
||||
0xb3, 0xfb, 0xe6, 0x56, 0xeb, 0x69, 0x3c, 0xbb, 0x9c, 0x97, 0x7c, 0x99, 0x27, 0x3b, 0x3c, 0xe3,
|
||||
0x25, 0xd8, 0x7d, 0xf3, 0x4a, 0x0d, 0x50, 0x62, 0xf7, 0xad, 0x43, 0xc5, 0x66, 0x70, 0x6e, 0x29,
|
||||
0x46, 0x59, 0x3a, 0x87, 0x2b, 0x6a, 0xcf, 0x90, 0x04, 0x88, 0x0c, 0x0e, 0x05, 0x91, 0x20, 0x52,
|
||||
0x2b, 0x6e, 0x91, 0xce, 0xe2, 0x4c, 0xf9, 0xdb, 0xa6, 0xcd, 0x78, 0x60, 0x67, 0x10, 0x21, 0x0a,
|
||||
0x48, 0x3d, 0xa7, 0xcb, 0x32, 0x1f, 0xe7, 0x82, 0x93, 0xf5, 0x6c, 0x80, 0xce, 0x7a, 0x3a, 0x20,
|
||||
0x18, 0x56, 0xa7, 0xec, 0x75, 0x5d, 0x9a, 0xfa, 0x1f, 0x6c, 0x58, 0xad, 0xff, 0x3e, 0xd4, 0xf2,
|
||||
0xd0, 0xb0, 0x0a, 0x38, 0x50, 0x19, 0xed, 0x44, 0x05, 0x4c, 0x40, 0xdb, 0x0f, 0x93, 0x7b, 0xdd,
|
||||
0x20, 0xee, 0x67, 0x22, 0x56, 0x19, 0x0b, 0xf9, 0x91, 0x40, 0x1f, 0x3f, 0x0d, 0x68, 0xb7, 0x5b,
|
||||
0xbc, 0xfa, 0x5c, 0xb0, 0xd9, 0x65, 0xeb, 0x00, 0x9b, 0x5f, 0x50, 0x85, 0x10, 0xdb, 0x2d, 0x04,
|
||||
0x8a, 0x37, 0xd1, 0x78, 0xc6, 0xf3, 0x50, 0x13, 0xd5, 0xf2, 0x3e, 0x4d, 0xa4, 0x39, 0xbb, 0xf8,
|
||||
0x35, 0x52, 0x1d, 0x99, 0xaa, 0x99, 0x36, 0x09, 0x0b, 0x2e, 0x44, 0x2c, 0x7e, 0x49, 0xd8, 0xe6,
|
||||
0xe4, 0xd0, 0xe7, 0x61, 0xfb, 0x74, 0x7f, 0xcb, 0xca, 0x21, 0x7d, 0xba, 0x9f, 0x62, 0xe9, 0x4a,
|
||||
0xaa, 0x18, 0xe9, 0xb0, 0xe2, 0xc7, 0xc9, 0xc3, 0x7e, 0xb0, 0x5d, 0xf2, 0x78, 0x3e, 0x77, 0x32,
|
||||
0x16, 0x97, 0xca, 0xeb, 0x56, 0xc0, 0x90, 0xc5, 0x88, 0x25, 0x4f, 0x00, 0x07, 0x43, 0x98, 0xe7,
|
||||
0x79, 0x87, 0xe7, 0x82, 0xe5, 0x02, 0x1b, 0xc2, 0x7c, 0x63, 0x1a, 0x0c, 0x0d, 0x61, 0x94, 0x02,
|
||||
0x88, 0x5b, 0xb9, 0x1f, 0xc4, 0xc4, 0x51, 0xbc, 0x40, 0x33, 0x36, 0xb5, 0xd7, 0xa3, 0xe4, 0xa1,
|
||||
0xb8, 0x05, 0x9c, 0xf3, 0x3a, 0xd7, 0xf5, 0x32, 0x8d, 0xcb, 0xb9, 0xd9, 0xdd, 0x48, 0x06, 0x8f,
|
||||
0x68, 0x3b, 0x3e, 0x49, 0xbc, 0xce, 0x0d, 0x6b, 0x80, 0x61, 0x67, 0xbc, 0x88, 0xe7, 0xa6, 0xa6,
|
||||
0x48, 0x0d, 0xa4, 0xbc, 0x55, 0xd5, 0x7b, 0xdd, 0x20, 0xf0, 0xf3, 0x22, 0x4d, 0x18, 0x0f, 0xf8,
|
||||
0x91, 0xf2, 0x3e, 0x7e, 0x20, 0x08, 0xb2, 0xb7, 0xba, 0xde, 0x6a, 0x45, 0x37, 0xca, 0x13, 0xbd,
|
||||
0x8e, 0x1d, 0x12, 0x8f, 0x07, 0x70, 0xa1, 0xec, 0x8d, 0xe0, 0x41, 0x1f, 0x6d, 0x36, 0x68, 0x43,
|
||||
0x7d, 0xd4, 0xec, 0xbf, 0xf6, 0xe9, 0xa3, 0x18, 0xac, 0x7d, 0xfe, 0x58, 0xf7, 0xd1, 0xdd, 0x58,
|
||||
0xc4, 0x75, 0xde, 0xfe, 0x22, 0x65, 0xaf, 0xf4, 0x42, 0x18, 0xa9, 0x6f, 0x43, 0x0d, 0xe5, 0xc7,
|
||||
0xc9, 0x60, 0x55, 0xbc, 0xdd, 0x9b, 0x0f, 0xf8, 0xd6, 0x2b, 0x84, 0x4e, 0xdf, 0x60, 0xa9, 0xb0,
|
||||
0xdd, 0x9b, 0x0f, 0xf8, 0xd6, 0x77, 0x2c, 0x74, 0xfa, 0x06, 0x17, 0x2d, 0x6c, 0xf7, 0xe6, 0xb5,
|
||||
0xef, 0xbf, 0x6c, 0x3a, 0xae, 0xeb, 0xbc, 0xce, 0xc3, 0x66, 0x22, 0xbd, 0x62, 0x58, 0x3a, 0xe9,
|
||||
0xdb, 0x33, 0x68, 0x28, 0x9d, 0xa4, 0x55, 0x9c, 0x5b, 0xce, 0xb0, 0x52, 0x1c, 0xf3, 0x2a, 0x95,
|
||||
0xc7, 0x31, 0x9e, 0xf4, 0x30, 0xda, 0xc0, 0xa1, 0x45, 0x53, 0x48, 0xc9, 0xbe, 0x58, 0xf6, 0x50,
|
||||
0x7b, 0x5e, 0xfd, 0x61, 0xc0, 0x5e, 0xfb, 0xd8, 0xfa, 0x56, 0x4f, 0xda, 0xbe, 0xe2, 0xf5, 0x98,
|
||||
0xe6, 0xe5, 0xdc, 0x84, 0xa1, 0xb3, 0x84, 0x31, 0x65, 0x5e, 0xda, 0xba, 0x6f, 0x29, 0x1f, 0xf5,
|
||||
0x57, 0xe8, 0x70, 0x3f, 0x4a, 0x92, 0x7e, 0xee, 0xdd, 0xb7, 0xdb, 0x8f, 0xfa, 0x2b, 0x68, 0xf7,
|
||||
0x7f, 0xdd, 0x2c, 0x6b, 0xa0, 0x7f, 0xdd, 0x07, 0x1f, 0xf7, 0xb1, 0x08, 0xfa, 0xe1, 0x93, 0x6b,
|
||||
0xe9, 0xe8, 0x82, 0xfc, 0xa4, 0x59, 0xbf, 0x37, 0xa8, 0xfc, 0x68, 0x48, 0x7e, 0x6c, 0xac, 0xbb,
|
||||
0x64, 0x28, 0xaa, 0x2c, 0x0c, 0x3b, 0xe6, 0xc7, 0xd7, 0xd4, 0x72, 0xae, 0xdc, 0xf3, 0x60, 0xfd,
|
||||
0x71, 0xab, 0x53, 0x9e, 0x90, 0x65, 0x87, 0x86, 0x05, 0xfa, 0xe4, 0xba, 0x6a, 0x54, 0x57, 0x75,
|
||||
0x60, 0x79, 0xe9, 0xcc, 0x93, 0x9e, 0x86, 0xbd, 0x6b, 0x68, 0x3e, 0xba, 0x9e, 0x92, 0x2e, 0xcb,
|
||||
0x7f, 0xae, 0x45, 0x77, 0x3d, 0xd6, 0xbe, 0xce, 0x00, 0x9b, 0x2e, 0x3f, 0x08, 0xd8, 0xa7, 0x94,
|
||||
0x4c, 0xe1, 0x7e, 0xe7, 0x9b, 0x29, 0xdb, 0xfb, 0xe9, 0x3c, 0x95, 0xbd, 0x34, 0x13, 0xac, 0x6c,
|
||||
0xdf, 0x4f, 0xe7, 0xdb, 0x55, 0xd4, 0x90, 0xbe, 0x9f, 0x2e, 0x80, 0x3b, 0xf7, 0xd3, 0x21, 0x9e,
|
||||
0xd1, 0xfb, 0xe9, 0x50, 0x6b, 0xc1, 0xfb, 0xe9, 0xc2, 0x1a, 0xd4, 0xec, 0xd2, 0x14, 0x41, 0x6d,
|
||||
0x9b, 0xf7, 0xb2, 0xe8, 0xef, 0xa2, 0x3f, 0xbe, 0x8e, 0x0a, 0x31, 0xbf, 0x2a, 0x4e, 0x1e, 0xa8,
|
||||
0xec, 0xf1, 0x4c, 0xbd, 0x43, 0x95, 0xdb, 0xbd, 0x79, 0xed, 0xfb, 0x47, 0x7a, 0x71, 0x65, 0x66,
|
||||
0x13, 0x5e, 0xca, 0xbb, 0x09, 0x37, 0x43, 0xb3, 0x43, 0x6d, 0xc1, 0x6d, 0xf9, 0x87, 0xfd, 0x60,
|
||||
0xa2, 0xba, 0x35, 0xa1, 0x1b, 0x7d, 0xd8, 0x65, 0x08, 0x34, 0xf9, 0x76, 0x6f, 0x9e, 0x98, 0x46,
|
||||
0x94, 0x6f, 0xd5, 0xda, 0x3d, 0x8c, 0xf9, 0x6d, 0xfd, 0xa8, 0xbf, 0x82, 0x76, 0x7f, 0xa5, 0xb3,
|
||||
0x56, 0xd7, 0xbd, 0x6c, 0xe7, 0xad, 0x2e, 0x53, 0x13, 0xaf, 0x99, 0x87, 0x7d, 0xf1, 0x50, 0xfe,
|
||||
0xe2, 0x4e, 0xa1, 0x5d, 0xf9, 0x0b, 0x3a, 0x8d, 0x7e, 0x74, 0x3d, 0x25, 0x5d, 0x96, 0x7f, 0x5c,
|
||||
0x8b, 0x6e, 0x92, 0x65, 0xd1, 0x71, 0xf0, 0x49, 0x5f, 0xcb, 0x20, 0x1e, 0x3e, 0xbd, 0xb6, 0x9e,
|
||||
0x2e, 0xd4, 0xbf, 0xac, 0x45, 0xb7, 0x02, 0x85, 0x52, 0x01, 0x72, 0x0d, 0xeb, 0x7e, 0xa0, 0x7c,
|
||||
0x76, 0x7d, 0x45, 0x6a, 0xba, 0x77, 0xf1, 0x49, 0xfb, 0xae, 0xb1, 0x80, 0xed, 0x09, 0x7d, 0xd7,
|
||||
0x58, 0xb7, 0x16, 0xdc, 0x63, 0x8a, 0xcf, 0x9a, 0x35, 0x1f, 0xba, 0xc7, 0x24, 0x8f, 0x42, 0x06,
|
||||
0x6f, 0x31, 0xc1, 0x38, 0xcc, 0xc9, 0xb3, 0xd7, 0x45, 0x9c, 0x27, 0xb4, 0x13, 0x25, 0xef, 0x76,
|
||||
0x62, 0x38, 0xb8, 0x37, 0x57, 0x4b, 0x4f, 0x78, 0xb3, 0x8e, 0xbb, 0x4f, 0xe9, 0x1b, 0x24, 0xb8,
|
||||
0x37, 0xd7, 0x42, 0x09, 0x6f, 0x3a, 0x6b, 0x0c, 0x79, 0x03, 0xc9, 0xe2, 0x83, 0x3e, 0x28, 0x58,
|
||||
0x21, 0x18, 0x6f, 0x66, 0xcb, 0xff, 0x61, 0xc8, 0x4a, 0x6b, 0xdb, 0x7f, 0xab, 0x27, 0x4d, 0xb8,
|
||||
0x9d, 0x30, 0xf1, 0x39, 0x8b, 0x13, 0x56, 0x06, 0xdd, 0x1a, 0xaa, 0x97, 0x5b, 0x97, 0xc6, 0xdc,
|
||||
0xee, 0xf0, 0x6c, 0xb9, 0xc8, 0x75, 0x63, 0x92, 0x6e, 0x5d, 0xaa, 0xdb, 0x2d, 0xa0, 0xe1, 0xae,
|
||||
0xa4, 0x75, 0x2b, 0xd3, 0xcb, 0x07, 0x61, 0x33, 0x5e, 0x56, 0xb9, 0xd9, 0x8b, 0xa5, 0xeb, 0xa9,
|
||||
0xc3, 0xa8, 0xa3, 0x9e, 0x20, 0x92, 0xb6, 0x7a, 0xd2, 0x70, 0x7b, 0xd0, 0x71, 0x6b, 0xe2, 0x69,
|
||||
0xbb, 0xc3, 0x56, 0x2b, 0xa4, 0x1e, 0xf5, 0x57, 0x80, 0x9b, 0xb1, 0x3a, 0xaa, 0x0e, 0xd2, 0x4a,
|
||||
0xec, 0xa5, 0x59, 0x36, 0xd8, 0x0c, 0x84, 0x49, 0x03, 0x05, 0x37, 0x63, 0x11, 0x98, 0x88, 0xe4,
|
||||
0x66, 0xf3, 0x32, 0x1f, 0x74, 0xd9, 0x91, 0x54, 0xaf, 0x48, 0x76, 0x69, 0xb0, 0xa1, 0xe6, 0x3c,
|
||||
0x6a, 0x53, 0xdb, 0x61, 0xf8, 0xc1, 0xb5, 0x2a, 0xbc, 0xdd, 0x9b, 0x07, 0x6f, 0xfb, 0x25, 0x25,
|
||||
0x67, 0x96, 0x3b, 0x94, 0x09, 0x6f, 0x26, 0xb9, 0xdb, 0x41, 0x81, 0x4d, 0x49, 0xd5, 0x8d, 0x5e,
|
||||
0xa6, 0xc9, 0x9c, 0x09, 0xf4, 0x45, 0x95, 0x0b, 0x04, 0x5f, 0x54, 0x01, 0x10, 0x34, 0x9d, 0xfa,
|
||||
0xbb, 0xd9, 0x8d, 0x1d, 0x27, 0x58, 0xd3, 0x69, 0x65, 0x87, 0x0a, 0x35, 0x1d, 0x4a, 0x83, 0xd1,
|
||||
0xc0, 0xb8, 0xd5, 0xf7, 0x3e, 0x3c, 0x08, 0x99, 0x01, 0x97, 0x3f, 0x6c, 0xf6, 0x62, 0xc1, 0x8c,
|
||||
0x62, 0x1d, 0xa6, 0x8b, 0x54, 0x60, 0x33, 0x8a, 0x63, 0xa3, 0x46, 0x42, 0x33, 0x4a, 0x1b, 0xa5,
|
||||
0xaa, 0x57, 0xe7, 0x08, 0xe3, 0x24, 0x5c, 0x3d, 0xc5, 0xf4, 0xab, 0x9e, 0x61, 0x5b, 0xef, 0x55,
|
||||
0x73, 0x13, 0x32, 0xe2, 0x42, 0x2f, 0x96, 0x91, 0xd8, 0x96, 0xdf, 0x03, 0x43, 0x30, 0x34, 0xea,
|
||||
0x50, 0x0a, 0xf0, 0x7d, 0x41, 0xcd, 0x35, 0xaf, 0x7e, 0x8b, 0x82, 0xc5, 0x65, 0x9c, 0xcf, 0xd0,
|
||||
0xc5, 0xa9, 0x34, 0xd8, 0x22, 0x43, 0x8b, 0x53, 0x52, 0x03, 0xbc, 0xb5, 0xf7, 0xbf, 0xe4, 0x45,
|
||||
0xba, 0x82, 0xf9, 0x64, 0xd6, 0xff, 0x90, 0xf7, 0x7e, 0x0f, 0x12, 0xbe, 0xb5, 0x6f, 0x00, 0xb3,
|
||||
0xef, 0xae, 0x9c, 0x7e, 0x18, 0x30, 0xe5, 0xa3, 0xa1, 0x85, 0x30, 0xad, 0x02, 0x82, 0xda, 0xd9,
|
||||
0x5b, 0xfc, 0x82, 0xad, 0xb0, 0xa0, 0x76, 0x37, 0x09, 0xbf, 0x60, 0xab, 0x50, 0x50, 0xb7, 0x51,
|
||||
0x90, 0x67, 0xba, 0xeb, 0xa0, 0xf5, 0x80, 0xbe, 0xbb, 0xf4, 0xd9, 0xe8, 0xe4, 0x40, 0xcf, 0xd9,
|
||||
0x4d, 0xaf, 0xbc, 0xd7, 0x14, 0x48, 0x41, 0x77, 0xd3, 0x2b, 0xfc, 0x2d, 0xc5, 0x66, 0x2f, 0x16,
|
||||
0x9e, 0x08, 0x88, 0x05, 0x7b, 0xdd, 0xbc, 0xaa, 0x47, 0x8a, 0x2b, 0xe5, 0xad, 0x77, 0xf5, 0xf7,
|
||||
0xba, 0x41, 0x7b, 0xfe, 0xf6, 0xb8, 0xe4, 0x33, 0x56, 0x55, 0xfa, 0x4a, 0x54, 0xff, 0x80, 0x93,
|
||||
0x96, 0x0d, 0xc1, 0x85, 0xa8, 0x77, 0xc2, 0x90, 0x73, 0x8f, 0xa1, 0x12, 0xd9, 0xeb, 0x95, 0xd6,
|
||||
0x51, 0xcd, 0xf6, 0xcd, 0x4a, 0x1b, 0x9d, 0x9c, 0xed, 0x5e, 0x5a, 0xea, 0xde, 0xa7, 0x74, 0x0f,
|
||||
0x55, 0xc7, 0xae, 0x52, 0xba, 0xdf, 0x83, 0xd4, 0xae, 0x3e, 0x8f, 0xde, 0x3c, 0xe0, 0xf3, 0x09,
|
||||
0xcb, 0x93, 0xc1, 0xf7, 0xfc, 0x13, 0xbc, 0x7c, 0x3e, 0xac, 0xff, 0x6c, 0x8c, 0xde, 0xa0, 0xc4,
|
||||
0xf6, 0x0c, 0xe2, 0x2e, 0x3b, 0x5b, 0xce, 0x27, 0x22, 0x16, 0xe0, 0x0c, 0xa2, 0xfc, 0xfb, 0xb0,
|
||||
0x16, 0x10, 0x67, 0x10, 0x3d, 0x00, 0xd8, 0x9b, 0x96, 0x8c, 0xa1, 0xf6, 0x6a, 0x41, 0xd0, 0x9e,
|
||||
0x06, 0x6c, 0x16, 0x61, 0xec, 0xd5, 0x89, 0x3a, 0x3c, 0x33, 0x68, 0x75, 0xa4, 0x94, 0xc8, 0x22,
|
||||
0xda, 0x94, 0x0d, 0x6e, 0x55, 0x7d, 0x79, 0xbd, 0xcd, 0x72, 0xb1, 0x88, 0xcb, 0x15, 0x08, 0x6e,
|
||||
0x5d, 0x4b, 0x07, 0x20, 0x82, 0x1b, 0x05, 0x6d, 0xaf, 0x6d, 0x1e, 0xf3, 0xec, 0x72, 0x9f, 0x97,
|
||||
0x7c, 0x29, 0xd2, 0x9c, 0xc1, 0x2b, 0x4e, 0xcc, 0x03, 0x75, 0x19, 0xa2, 0xd7, 0x52, 0xac, 0xcd,
|
||||
0x72, 0x25, 0xa1, 0x8e, 0x33, 0xca, 0x3b, 0xe1, 0x2b, 0xc1, 0x4b, 0xf8, 0x3a, 0x53, 0x59, 0x81,
|
||||
0x10, 0x91, 0xe5, 0x92, 0x30, 0x68, 0xfb, 0xe3, 0x34, 0x9f, 0xa3, 0x6d, 0x7f, 0xec, 0x5e, 0x33,
|
||||
0x7c, 0x8b, 0x06, 0x6c, 0x87, 0x52, 0x0f, 0x4d, 0x75, 0x00, 0xfd, 0xd1, 0x30, 0xfa, 0xd0, 0x5d,
|
||||
0x82, 0xe8, 0x50, 0x38, 0x09, 0x5c, 0x3d, 0x2f, 0x58, 0xce, 0x92, 0xe6, 0xd0, 0x1e, 0xe6, 0xca,
|
||||
0x23, 0x82, 0xae, 0x20, 0x69, 0xc7, 0x22, 0x29, 0x3f, 0x59, 0xe6, 0xc7, 0x25, 0x3f, 0x4f, 0x33,
|
||||
0x56, 0x82, 0xb1, 0x48, 0xa9, 0x3b, 0x72, 0x62, 0x2c, 0xc2, 0x38, 0x7b, 0xfa, 0x43, 0x4a, 0xbd,
|
||||
0x1f, 0x36, 0x98, 0x96, 0xf1, 0x0c, 0x9e, 0xfe, 0x50, 0x36, 0xda, 0x18, 0xb1, 0x33, 0x18, 0xc0,
|
||||
0x9d, 0x44, 0x47, 0xb9, 0xce, 0x57, 0x32, 0x3e, 0xf4, 0x47, 0xab, 0xf2, 0xf2, 0xdd, 0x0a, 0x24,
|
||||
0x3a, 0xda, 0x1c, 0x46, 0x12, 0x89, 0x4e, 0x58, 0xc3, 0x4e, 0x25, 0x92, 0x3b, 0xd2, 0xa7, 0x9a,
|
||||
0xc0, 0x54, 0xa2, 0x6c, 0x34, 0x42, 0x62, 0x2a, 0x69, 0x41, 0x60, 0x40, 0x6a, 0xba, 0xc1, 0x1c,
|
||||
0x1d, 0x90, 0x8c, 0x34, 0x38, 0x20, 0xb9, 0x94, 0x1d, 0x28, 0xc6, 0x79, 0x2a, 0xd2, 0x38, 0x9b,
|
||||
0x30, 0x71, 0x1c, 0x97, 0xf1, 0x82, 0x09, 0x56, 0xc2, 0x81, 0x42, 0x23, 0x43, 0x8f, 0x21, 0x06,
|
||||
0x0a, 0x8a, 0xd5, 0x0e, 0x7f, 0x37, 0x7a, 0xbb, 0x9e, 0xf7, 0x59, 0xae, 0x7f, 0x92, 0xe9, 0x99,
|
||||
0xfc, 0x2d, 0xb7, 0xc1, 0x3b, 0xc6, 0xc6, 0x44, 0x94, 0x2c, 0x5e, 0x34, 0xb6, 0xdf, 0x32, 0x7f,
|
||||
0x97, 0xe0, 0xa3, 0xb5, 0x3a, 0x9e, 0x8f, 0xb8, 0x48, 0xcf, 0xeb, 0x65, 0xb6, 0xfe, 0x80, 0x09,
|
||||
0xc4, 0xb3, 0x2b, 0x1e, 0x06, 0x2e, 0x3d, 0xc1, 0x38, 0x3b, 0x4e, 0xbb, 0xd2, 0x13, 0x56, 0x64,
|
||||
0x70, 0x9c, 0xf6, 0xb4, 0x25, 0x40, 0x8c, 0xd3, 0x28, 0x68, 0x3b, 0xa7, 0x2b, 0x9e, 0xb2, 0x70,
|
||||
0x65, 0xa6, 0xac, 0x5f, 0x65, 0xa6, 0xde, 0x37, 0x21, 0x59, 0xf4, 0xf6, 0x21, 0x5b, 0x9c, 0xb1,
|
||||
0xb2, 0xba, 0x48, 0x0b, 0xea, 0x82, 0x60, 0x4b, 0x74, 0x5e, 0x10, 0x4c, 0xa0, 0x76, 0x26, 0xb0,
|
||||
0xc0, 0xb8, 0x3a, 0x8a, 0x17, 0x4c, 0x5e, 0xe1, 0x02, 0x66, 0x02, 0xc7, 0x88, 0x03, 0x11, 0x33,
|
||||
0x01, 0x09, 0x3b, 0x9f, 0x97, 0x59, 0xe6, 0x84, 0xcd, 0xeb, 0x08, 0x2b, 0x8f, 0xe3, 0xd5, 0x82,
|
||||
0xe5, 0x42, 0x9b, 0x04, 0x7b, 0xf2, 0x8e, 0x49, 0x9c, 0x27, 0xf6, 0xe4, 0xfb, 0xe8, 0x39, 0x43,
|
||||
0x93, 0xf7, 0xe0, 0x8f, 0x79, 0x29, 0xd4, 0x0f, 0xae, 0x9d, 0x96, 0x19, 0x18, 0x9a, 0xfc, 0x87,
|
||||
0xea, 0x91, 0xc4, 0xd0, 0x14, 0xd6, 0x70, 0x7e, 0x5c, 0xc3, 0x2b, 0xc3, 0x0b, 0x56, 0x9a, 0x38,
|
||||
0x79, 0xb6, 0x88, 0xd3, 0x4c, 0x47, 0xc3, 0xf7, 0x03, 0xb6, 0x09, 0x1d, 0xe2, 0xc7, 0x35, 0xfa,
|
||||
0xea, 0x3a, 0x3f, 0x47, 0x12, 0x2e, 0x21, 0x78, 0x45, 0xd0, 0x61, 0x9f, 0x78, 0x45, 0xd0, 0xad,
|
||||
0x65, 0x57, 0xee, 0x96, 0x95, 0xdc, 0x4a, 0x12, 0x3b, 0x3c, 0x81, 0xfb, 0x85, 0x8e, 0x4d, 0x00,
|
||||
0x12, 0x2b, 0xf7, 0xa0, 0x82, 0x4d, 0x0d, 0x2c, 0xb6, 0x97, 0xe6, 0x71, 0x96, 0xfe, 0x18, 0xa6,
|
||||
0xf5, 0x8e, 0x9d, 0x86, 0x20, 0x52, 0x03, 0x9c, 0xc4, 0x5c, 0xed, 0x33, 0x31, 0x4d, 0xeb, 0xa1,
|
||||
0xff, 0x5e, 0xe0, 0xb9, 0x49, 0xa2, 0xdb, 0x95, 0x43, 0x3a, 0x97, 0x01, 0xc3, 0xc7, 0x3a, 0x2a,
|
||||
0x8a, 0x49, 0x3d, 0xab, 0x9e, 0xb0, 0x19, 0x4b, 0x0b, 0x31, 0xf8, 0x38, 0xfc, 0xac, 0x00, 0x4e,
|
||||
0x1c, 0xb4, 0xe8, 0xa1, 0xe6, 0xbc, 0xbe, 0xaf, 0xc7, 0x92, 0x89, 0xfa, 0x25, 0xd2, 0xd3, 0x8a,
|
||||
0x95, 0x3a, 0xd1, 0xd8, 0x67, 0x02, 0xf4, 0x4e, 0x87, 0x1b, 0x3a, 0x60, 0x5d, 0x51, 0xa2, 0x77,
|
||||
0x86, 0x35, 0xec, 0x66, 0x9f, 0xc3, 0xe9, 0xcb, 0xdd, 0xe5, 0x71, 0xc7, 0x87, 0xa4, 0x31, 0x87,
|
||||
0x22, 0x36, 0xfb, 0x68, 0xda, 0x66, 0x6b, 0x6d, 0xb7, 0xa3, 0x7c, 0x35, 0x86, 0x47, 0x26, 0x10,
|
||||
0x4b, 0x12, 0x23, 0xb2, 0xb5, 0x00, 0xee, 0x6c, 0x86, 0x97, 0x3c, 0x4e, 0x66, 0x71, 0x25, 0x8e,
|
||||
0xe3, 0x55, 0xc6, 0xe3, 0x44, 0xce, 0xeb, 0x70, 0x33, 0xbc, 0x61, 0x86, 0x2e, 0x44, 0x6d, 0x86,
|
||||
0x53, 0xb0, 0x9b, 0x9d, 0xc9, 0x1f, 0x58, 0xd5, 0x47, 0x49, 0x61, 0x76, 0x26, 0xcb, 0x0b, 0x8f,
|
||||
0x91, 0xde, 0x09, 0x43, 0xf6, 0x13, 0x38, 0x25, 0x92, 0x69, 0xc8, 0x2d, 0x4c, 0xc7, 0x4b, 0x40,
|
||||
0xde, 0x0f, 0x10, 0xf6, 0x02, 0x14, 0xf5, 0xf7, 0xe6, 0x37, 0xb5, 0x84, 0xbe, 0x32, 0xfd, 0x21,
|
||||
0xa6, 0xeb, 0x42, 0xde, 0x09, 0xb5, 0xad, 0x9e, 0xb4, 0x4d, 0x33, 0x77, 0x2e, 0x62, 0x31, 0x4a,
|
||||
0x92, 0x43, 0x56, 0x21, 0xdf, 0xb3, 0xd7, 0xc2, 0xa1, 0x95, 0x12, 0x69, 0x66, 0x9b, 0xb2, 0x81,
|
||||
0x5e, 0xcb, 0x9e, 0x25, 0xa9, 0xd0, 0xb2, 0xe6, 0x80, 0xf6, 0xc3, 0xb6, 0x81, 0x36, 0x45, 0xd4,
|
||||
0x8a, 0xa6, 0xed, 0x58, 0x5e, 0x33, 0x53, 0x3e, 0x9f, 0x67, 0x4c, 0x43, 0x27, 0x2c, 0x56, 0x37,
|
||||
0x46, 0x6e, 0xb7, 0x6d, 0xa1, 0x20, 0x31, 0x96, 0x07, 0x15, 0x6c, 0x1a, 0x59, 0x63, 0xea, 0x95,
|
||||
0x54, 0xf3, 0x60, 0x37, 0xda, 0x66, 0x3c, 0x80, 0x48, 0x23, 0x51, 0xd0, 0x7e, 0x76, 0x57, 0x8b,
|
||||
0xf7, 0x59, 0xf3, 0x24, 0xe0, 0x5d, 0x57, 0x52, 0xd9, 0x11, 0x13, 0x9f, 0xdd, 0x21, 0x98, 0x5d,
|
||||
0x27, 0x00, 0x0f, 0x4f, 0x57, 0xe3, 0x04, 0xae, 0x13, 0xa0, 0xbe, 0x64, 0x88, 0x75, 0x02, 0xc5,
|
||||
0xfa, 0x4d, 0x67, 0xf6, 0xbd, 0x0e, 0xe2, 0xca, 0x56, 0x0e, 0x69, 0x3a, 0x14, 0x0c, 0x35, 0x1d,
|
||||
0xa5, 0xe0, 0x3f, 0x52, 0x77, 0x6b, 0x0d, 0x79, 0xa4, 0xd8, 0xbe, 0xda, 0x7a, 0x17, 0x66, 0x73,
|
||||
0xff, 0x5a, 0x78, 0xc2, 0xe2, 0xc4, 0x54, 0x0c, 0xd1, 0x75, 0xe5, 0x44, 0xee, 0x8f, 0x71, 0xda,
|
||||
0xc9, 0x1f, 0x44, 0x03, 0x55, 0x8d, 0xd2, 0x75, 0x73, 0x0b, 0x2b, 0x62, 0x4d, 0x10, 0x03, 0x95,
|
||||
0x4f, 0x38, 0x89, 0x9b, 0xd7, 0x44, 0x53, 0xae, 0x1d, 0xe8, 0xcf, 0x42, 0x2b, 0x90, 0xb8, 0xf9,
|
||||
0x8f, 0xbd, 0x45, 0x13, 0x89, 0x5b, 0xb7, 0x96, 0x73, 0xed, 0x0f, 0x68, 0xb2, 0xbd, 0x92, 0x2f,
|
||||
0x60, 0x99, 0x3e, 0x0b, 0x36, 0x0f, 0xa2, 0x41, 0x5c, 0xfb, 0xd3, 0x4f, 0xd3, 0xce, 0x41, 0x66,
|
||||
0xef, 0x40, 0x1e, 0x4f, 0xc3, 0x7f, 0x16, 0x44, 0x09, 0x89, 0x39, 0xa8, 0x05, 0x39, 0xbf, 0x25,
|
||||
0x3a, 0x7e, 0x59, 0xa6, 0x22, 0xcd, 0xe7, 0x53, 0xce, 0x33, 0xb8, 0x65, 0x39, 0x1a, 0x0f, 0x5d,
|
||||
0x29, 0xf5, 0x5b, 0xa2, 0x2d, 0xca, 0x4e, 0x71, 0xa3, 0xf1, 0x68, 0x29, 0xf8, 0x79, 0x9a, 0x65,
|
||||
0x20, 0x72, 0x46, 0xe3, 0x61, 0x23, 0x21, 0x22, 0xc7, 0x27, 0x9c, 0x5f, 0xc0, 0x1c, 0xcb, 0xdd,
|
||||
0x7f, 0xbd, 0x03, 0xfa, 0x01, 0xd4, 0x71, 0x84, 0xd4, 0x2f, 0x60, 0x42, 0xc8, 0xf9, 0x45, 0xcf,
|
||||
0x31, 0xf6, 0xdb, 0x26, 0x9b, 0x50, 0x1d, 0x81, 0xa8, 0x5f, 0xf4, 0xa4, 0x60, 0xe7, 0x9b, 0xe4,
|
||||
0xe3, 0x65, 0x75, 0xe1, 0x6f, 0x19, 0xa8, 0xc5, 0xa1, 0xba, 0x20, 0xf5, 0x09, 0xf8, 0x85, 0x1d,
|
||||
0x9f, 0x1d, 0x7a, 0x30, 0x71, 0x3c, 0xad, 0x53, 0x49, 0x15, 0xe6, 0xe9, 0xfb, 0xff, 0xfd, 0xd5,
|
||||
0x8d, 0xb5, 0x9f, 0x7d, 0x75, 0x63, 0xed, 0x7f, 0xbf, 0xba, 0xb1, 0xf6, 0xd3, 0xaf, 0x6f, 0xbc,
|
||||
0xf1, 0xb3, 0xaf, 0x6f, 0xbc, 0xf1, 0x3f, 0x5f, 0xdf, 0x78, 0xe3, 0xcb, 0x37, 0xf5, 0x6f, 0xee,
|
||||
0x9f, 0xfd, 0x82, 0xfc, 0xe5, 0xfc, 0x27, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x89, 0x4b, 0xe1,
|
||||
0x93, 0x97, 0x7f, 0x00, 0x00,
|
||||
// 5713 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0xdd, 0x6f, 0x24, 0x49,
|
||||
0x52, 0xc0, 0xd7, 0x2f, 0x2c, 0xd4, 0x71, 0x0b, 0xf4, 0xc2, 0xb2, 0xb7, 0xdc, 0xcd, 0xcc, 0xce,
|
||||
0x87, 0x3d, 0x33, 0x1e, 0xb7, 0x67, 0x67, 0xf6, 0x8b, 0x3b, 0x24, 0xe8, 0xb1, 0xc7, 0xde, 0xbe,
|
||||
0xb5, 0xbd, 0xc6, 0xdd, 0x9e, 0x11, 0x2b, 0x21, 0x51, 0xee, 0x4a, 0xb7, 0x0b, 0x57, 0x57, 0xd6,
|
||||
0x55, 0x65, 0x7b, 0xa6, 0x0f, 0x81, 0x40, 0x20, 0x10, 0x08, 0xc4, 0x89, 0x2f, 0xc1, 0x13, 0x12,
|
||||
0x7f, 0x01, 0x7f, 0x06, 0x8f, 0xf7, 0x78, 0x8f, 0x68, 0xf7, 0xef, 0x40, 0x42, 0x95, 0x95, 0x9f,
|
||||
0x51, 0x11, 0x59, 0xe5, 0xbd, 0xa7, 0x19, 0x39, 0x7e, 0x11, 0x91, 0x1f, 0x91, 0x99, 0x91, 0x59,
|
||||
0x59, 0xd5, 0xd1, 0xcd, 0xe2, 0x6c, 0xbb, 0x28, 0xb9, 0xe0, 0xd5, 0x76, 0xc5, 0xca, 0xab, 0x74,
|
||||
0xc6, 0xf4, 0xbf, 0x43, 0xf9, 0xe7, 0xc1, 0x9b, 0x71, 0xbe, 0x12, 0xab, 0x82, 0xbd, 0xf7, 0xae,
|
||||
0x25, 0x67, 0x7c, 0xb1, 0x88, 0xf3, 0xa4, 0x6a, 0x90, 0xf7, 0xde, 0xb1, 0x12, 0x76, 0xc5, 0x72,
|
||||
0xa1, 0xfe, 0xfe, 0xe4, 0x67, 0xff, 0xb7, 0x16, 0xbd, 0xb5, 0x93, 0xa5, 0x2c, 0x17, 0x3b, 0x4a,
|
||||
0x63, 0xf0, 0x65, 0xf4, 0xed, 0x51, 0x51, 0xec, 0x33, 0xf1, 0x82, 0x95, 0x55, 0xca, 0xf3, 0xc1,
|
||||
0x9d, 0xa1, 0x72, 0x30, 0x3c, 0x29, 0x66, 0xc3, 0x51, 0x51, 0x0c, 0xad, 0x70, 0x78, 0xc2, 0x7e,
|
||||
0xb4, 0x64, 0x95, 0x78, 0xef, 0x6e, 0x18, 0xaa, 0x0a, 0x9e, 0x57, 0x6c, 0x70, 0x1e, 0xfd, 0xda,
|
||||
0xa8, 0x28, 0x26, 0x4c, 0xec, 0xb2, 0xba, 0x02, 0x13, 0x11, 0x0b, 0x36, 0xd8, 0x68, 0xa9, 0xfa,
|
||||
0x80, 0xf1, 0x71, 0xbf, 0x1b, 0x54, 0x7e, 0xa6, 0xd1, 0xb7, 0x6a, 0x3f, 0x17, 0x4b, 0x91, 0xf0,
|
||||
0x57, 0xf9, 0xe0, 0xfd, 0xb6, 0xa2, 0x12, 0x19, 0xdb, 0xb7, 0x43, 0x88, 0xb2, 0xfa, 0x32, 0xfa,
|
||||
0xe5, 0x97, 0x71, 0x96, 0x31, 0xb1, 0x53, 0xb2, 0xba, 0xe0, 0xbe, 0x4e, 0x23, 0x1a, 0x36, 0x32,
|
||||
0x63, 0xf7, 0x4e, 0x90, 0x51, 0x86, 0xbf, 0x8c, 0xbe, 0xdd, 0x48, 0x4e, 0xd8, 0x8c, 0x5f, 0xb1,
|
||||
0x72, 0x80, 0x6a, 0x29, 0x21, 0xd1, 0xe4, 0x2d, 0x08, 0xda, 0xde, 0xe1, 0xf9, 0x15, 0x2b, 0x05,
|
||||
0x6e, 0x5b, 0x09, 0xc3, 0xb6, 0x2d, 0xa4, 0x6c, 0xff, 0xed, 0x5a, 0xf4, 0xdd, 0xd1, 0x6c, 0xc6,
|
||||
0x97, 0xb9, 0x38, 0xe0, 0xb3, 0x38, 0x3b, 0x48, 0xf3, 0xcb, 0x23, 0xf6, 0x6a, 0xe7, 0xa2, 0xe6,
|
||||
0xf3, 0x39, 0x1b, 0x3c, 0xf5, 0x5b, 0xb5, 0x41, 0x87, 0x86, 0x1d, 0xba, 0xb0, 0xf1, 0xfd, 0xe1,
|
||||
0xf5, 0x94, 0x54, 0x59, 0xfe, 0x71, 0x2d, 0xba, 0x01, 0xcb, 0x32, 0xe1, 0xd9, 0x15, 0xb3, 0xa5,
|
||||
0xf9, 0xa8, 0xc3, 0xb0, 0x8f, 0x9b, 0xf2, 0x7c, 0x7c, 0x5d, 0x35, 0x55, 0xa2, 0x2c, 0x7a, 0xdb,
|
||||
0x0d, 0x97, 0x09, 0xab, 0xe4, 0x70, 0x7a, 0x40, 0x47, 0x84, 0x42, 0x8c, 0xe7, 0x87, 0x7d, 0x50,
|
||||
0xe5, 0x2d, 0x8d, 0x06, 0xca, 0x5b, 0xc6, 0x2b, 0xe3, 0xec, 0x3e, 0x6a, 0xc1, 0x21, 0x8c, 0xaf,
|
||||
0x07, 0x3d, 0x48, 0xe5, 0xea, 0x8f, 0xa2, 0x5f, 0x79, 0xc9, 0xcb, 0xcb, 0xaa, 0x88, 0x67, 0x4c,
|
||||
0x0d, 0x85, 0x7b, 0xbe, 0xb6, 0x96, 0xc2, 0xd1, 0xb0, 0xde, 0x85, 0x39, 0x41, 0xab, 0x85, 0x5f,
|
||||
0x14, 0x0c, 0xce, 0x41, 0x56, 0xb1, 0x16, 0x52, 0x41, 0x0b, 0x21, 0x65, 0xfb, 0x32, 0x1a, 0x58,
|
||||
0xdb, 0x67, 0x7f, 0xcc, 0x66, 0x62, 0x94, 0x24, 0xb0, 0x57, 0xac, 0xae, 0x24, 0x86, 0xa3, 0x24,
|
||||
0xa1, 0x7a, 0x05, 0x47, 0x95, 0xb3, 0x57, 0xd1, 0x3b, 0xc0, 0xd9, 0x41, 0x5a, 0x49, 0x87, 0x5b,
|
||||
0x61, 0x2b, 0x0a, 0x33, 0x4e, 0x87, 0x7d, 0x71, 0xe5, 0xf8, 0xcf, 0xd7, 0xa2, 0xef, 0x20, 0x9e,
|
||||
0x4f, 0xd8, 0x82, 0x5f, 0xb1, 0xc1, 0xe3, 0x6e, 0x6b, 0x0d, 0x69, 0xfc, 0x7f, 0x70, 0x0d, 0x0d,
|
||||
0x24, 0x4c, 0x26, 0x2c, 0x63, 0x33, 0x41, 0x86, 0x49, 0x23, 0xee, 0x0c, 0x13, 0x83, 0x39, 0x23,
|
||||
0x4c, 0x0b, 0xf7, 0x99, 0xd8, 0x59, 0x96, 0x25, 0xcb, 0x05, 0xd9, 0x97, 0x16, 0xe9, 0xec, 0x4b,
|
||||
0x0f, 0x45, 0xea, 0xb3, 0xcf, 0xc4, 0x28, 0xcb, 0xc8, 0xfa, 0x34, 0xe2, 0xce, 0xfa, 0x18, 0x4c,
|
||||
0x79, 0x98, 0x45, 0xbf, 0xea, 0xb4, 0x98, 0x18, 0xe7, 0xe7, 0x7c, 0x40, 0xb7, 0x85, 0x94, 0x1b,
|
||||
0x1f, 0x1b, 0x9d, 0x1c, 0x52, 0x8d, 0xe7, 0xaf, 0x0b, 0x5e, 0xd2, 0xdd, 0xd2, 0x88, 0x3b, 0xab,
|
||||
0x61, 0x30, 0xe5, 0xe1, 0x0f, 0xa3, 0xb7, 0xd4, 0x2c, 0xa9, 0xd7, 0xb3, 0xbb, 0xe8, 0x14, 0x0a,
|
||||
0x17, 0xb4, 0x7b, 0x1d, 0x54, 0xcb, 0xfc, 0x61, 0x3a, 0x2f, 0xeb, 0xd9, 0x07, 0x37, 0xaf, 0xa4,
|
||||
0x1d, 0xe6, 0x2d, 0xa5, 0xcc, 0xf3, 0xe8, 0xd7, 0x7d, 0xf3, 0x3b, 0x71, 0x3e, 0x63, 0xd9, 0xe0,
|
||||
0x61, 0x48, 0xbd, 0x61, 0x8c, 0xab, 0xcd, 0x5e, 0xac, 0x9d, 0xec, 0x14, 0xa1, 0x26, 0xd3, 0x3b,
|
||||
0xa8, 0x36, 0x98, 0x4a, 0xef, 0x86, 0xa1, 0x96, 0xed, 0x5d, 0x96, 0x31, 0xd2, 0x76, 0x23, 0xec,
|
||||
0xb0, 0x6d, 0x20, 0x65, 0xbb, 0x8c, 0x7e, 0xc3, 0x74, 0x73, 0x9d, 0x17, 0x48, 0x79, 0xbd, 0xe8,
|
||||
0x6c, 0x12, 0xfd, 0xe8, 0x42, 0xc6, 0xd7, 0xa3, 0x7e, 0x70, 0xab, 0x3e, 0x6a, 0x46, 0xc1, 0xeb,
|
||||
0x03, 0xe6, 0x93, 0xbb, 0x61, 0x48, 0xd9, 0xfe, 0xbb, 0xb5, 0xe8, 0x7b, 0x4a, 0xf6, 0x3c, 0x8f,
|
||||
0xcf, 0x32, 0x26, 0x97, 0xf8, 0x23, 0x26, 0x5e, 0xf1, 0xf2, 0x72, 0xb2, 0xca, 0x67, 0x44, 0x3a,
|
||||
0x83, 0xc3, 0x1d, 0xe9, 0x0c, 0xa9, 0xa4, 0x0a, 0xf3, 0x27, 0xd1, 0xbb, 0x3a, 0x28, 0x2e, 0xe2,
|
||||
0x7c, 0xce, 0x7e, 0x58, 0xf1, 0x7c, 0x54, 0xa4, 0xa3, 0x24, 0x29, 0x07, 0x43, 0xbc, 0xeb, 0x21,
|
||||
0x67, 0x4a, 0xb0, 0xdd, 0x9b, 0x77, 0xd2, 0x67, 0xd5, 0xca, 0x82, 0x17, 0x30, 0x7d, 0xd6, 0xcd,
|
||||
0x27, 0x78, 0x41, 0xa5, 0xcf, 0x3e, 0xd2, 0xb2, 0x7a, 0x58, 0xaf, 0x41, 0xb8, 0xd5, 0x43, 0x77,
|
||||
0xd1, 0xb9, 0x1d, 0x42, 0xec, 0x1a, 0xa0, 0x1b, 0x8a, 0xe7, 0xe7, 0xe9, 0xfc, 0xb4, 0x48, 0xea,
|
||||
0x31, 0xf4, 0x00, 0xaf, 0xb3, 0x83, 0x10, 0x6b, 0x00, 0x81, 0x2a, 0x6f, 0xff, 0x60, 0xb3, 0x4c,
|
||||
0x35, 0x2f, 0xed, 0x95, 0x7c, 0x71, 0xc0, 0xe6, 0xf1, 0x6c, 0xa5, 0x26, 0xd3, 0x0f, 0x43, 0xb3,
|
||||
0x18, 0xa4, 0x4d, 0x21, 0x3e, 0xba, 0xa6, 0x96, 0x2a, 0xcf, 0x7f, 0xae, 0x45, 0x77, 0xbd, 0x38,
|
||||
0x51, 0xc1, 0xd4, 0x94, 0x7e, 0x94, 0x27, 0x27, 0xac, 0x12, 0x71, 0x29, 0x06, 0xdf, 0x0f, 0xc4,
|
||||
0x00, 0xa1, 0x63, 0xca, 0xf6, 0x83, 0x6f, 0xa4, 0x6b, 0x7b, 0x7d, 0x52, 0xaf, 0x12, 0x6a, 0xfe,
|
||||
0xf1, 0x7b, 0x5d, 0x4a, 0xe0, 0xec, 0x73, 0x3b, 0x84, 0xd8, 0x5e, 0x97, 0x82, 0x71, 0x7e, 0x95,
|
||||
0x0a, 0xb6, 0xcf, 0x72, 0x56, 0xb6, 0x7b, 0xbd, 0x51, 0xf5, 0x11, 0xa2, 0xd7, 0x09, 0xd4, 0xce,
|
||||
0x74, 0x9e, 0x37, 0x93, 0x69, 0x6c, 0x06, 0x8c, 0xb4, 0x72, 0x8d, 0x47, 0xfd, 0x60, 0xa2, 0x86,
|
||||
0x62, 0xbf, 0x36, 0x12, 0xac, 0x61, 0x83, 0xf4, 0xaa, 0xa1, 0x41, 0xed, 0xc6, 0xdc, 0xf1, 0x76,
|
||||
0xc2, 0xae, 0xf8, 0x25, 0xdc, 0x98, 0xbb, 0x06, 0x1a, 0x80, 0xd8, 0x98, 0xa3, 0xa0, 0x4d, 0x3e,
|
||||
0x1c, 0x3f, 0x2f, 0x52, 0xf6, 0x0a, 0x24, 0x1f, 0xae, 0x72, 0x2d, 0x26, 0x92, 0x0f, 0x04, 0x53,
|
||||
0x1e, 0x8e, 0xa2, 0x5f, 0x92, 0xc2, 0x1f, 0xf2, 0x34, 0x1f, 0xdc, 0x44, 0x94, 0x6a, 0x81, 0xb1,
|
||||
0x7a, 0x8b, 0x06, 0x40, 0x89, 0xeb, 0xbf, 0xaa, 0x4c, 0xe0, 0x1e, 0xa1, 0x04, 0x92, 0x80, 0xf5,
|
||||
0x2e, 0xcc, 0x66, 0x7d, 0x52, 0x58, 0xcf, 0x96, 0x93, 0x8b, 0xb8, 0x4c, 0xf3, 0xf9, 0x00, 0xd3,
|
||||
0x75, 0xe4, 0x44, 0xd6, 0x87, 0x71, 0x20, 0x9c, 0x94, 0xe2, 0xa8, 0x28, 0xca, 0x7a, 0x12, 0xc6,
|
||||
0xc2, 0xc9, 0x47, 0x82, 0xe1, 0xd4, 0x42, 0x71, 0x6f, 0xbb, 0x6c, 0x96, 0xa5, 0x79, 0xd0, 0x9b,
|
||||
0x42, 0xfa, 0x78, 0xb3, 0x28, 0x08, 0xde, 0x03, 0x16, 0x5f, 0x31, 0x5d, 0x33, 0xac, 0x65, 0x5c,
|
||||
0x20, 0x18, 0xbc, 0x00, 0xb4, 0x5b, 0x6c, 0x29, 0x3e, 0x8c, 0x2f, 0x59, 0xdd, 0xc0, 0xac, 0x5e,
|
||||
0xc2, 0x07, 0x98, 0xbe, 0x47, 0x10, 0x5b, 0x6c, 0x9c, 0x54, 0xae, 0x96, 0xd1, 0x3b, 0x52, 0x7e,
|
||||
0x1c, 0x97, 0x22, 0x9d, 0xa5, 0x45, 0x9c, 0xeb, 0xad, 0x1b, 0x36, 0x8b, 0xb4, 0x28, 0xe3, 0x72,
|
||||
0xab, 0x27, 0xad, 0xdc, 0xfe, 0xdb, 0x5a, 0xf4, 0x3e, 0xf4, 0x7b, 0xcc, 0xca, 0x45, 0x2a, 0x4f,
|
||||
0x00, 0xaa, 0x66, 0xca, 0x1f, 0x7c, 0x12, 0x36, 0xda, 0x52, 0x30, 0xa5, 0xf9, 0xf4, 0xfa, 0x8a,
|
||||
0x36, 0xef, 0x9b, 0xa8, 0x5d, 0xd1, 0x17, 0x65, 0xd2, 0x3a, 0x21, 0x9b, 0xe8, 0xad, 0x8e, 0x14,
|
||||
0x12, 0x79, 0x5f, 0x0b, 0x02, 0x23, 0xfc, 0x34, 0xaf, 0xb4, 0x75, 0x6c, 0x84, 0x5b, 0x71, 0x70,
|
||||
0x84, 0x7b, 0x98, 0x1d, 0xe1, 0xc7, 0xcb, 0xb3, 0x2c, 0xad, 0x2e, 0xd2, 0x7c, 0xae, 0x92, 0x7c,
|
||||
0x5f, 0xd7, 0x8a, 0x61, 0x9e, 0xbf, 0xd1, 0xc9, 0x61, 0x4e, 0x54, 0xb0, 0x90, 0x4e, 0x40, 0x98,
|
||||
0x6c, 0x74, 0x72, 0x76, 0xef, 0x65, 0xa5, 0xf5, 0xa6, 0x1f, 0xec, 0xbd, 0x1c, 0xd5, 0x5a, 0x4a,
|
||||
0xec, 0xbd, 0xda, 0x94, 0xdd, 0x7b, 0xb9, 0x75, 0xa8, 0x78, 0x76, 0xc5, 0x4e, 0xcb, 0x14, 0xec,
|
||||
0xbd, 0xbc, 0xf2, 0x69, 0x86, 0xd8, 0x7b, 0x51, 0xac, 0x9d, 0xa8, 0x2c, 0xb1, 0xcf, 0xc4, 0x44,
|
||||
0xc4, 0x62, 0x59, 0x81, 0x89, 0xca, 0xb1, 0x61, 0x10, 0x62, 0xa2, 0x22, 0x50, 0xe5, 0xed, 0xf7,
|
||||
0xa3, 0xa8, 0x39, 0x2f, 0x91, 0x67, 0x5a, 0xfe, 0xda, 0xa3, 0x0e, 0x52, 0xbc, 0x03, 0xad, 0xf7,
|
||||
0x03, 0x84, 0x4d, 0xaf, 0x9a, 0xbf, 0xcb, 0xa3, 0xba, 0x01, 0xaa, 0x21, 0x45, 0x44, 0x7a, 0x05,
|
||||
0x10, 0x58, 0xd0, 0xc9, 0x05, 0x7f, 0x85, 0x17, 0xb4, 0x96, 0x84, 0x0b, 0xaa, 0x08, 0x7b, 0x78,
|
||||
0xae, 0x0a, 0x8a, 0x1d, 0x9e, 0xeb, 0x62, 0x84, 0x0e, 0xcf, 0x21, 0x63, 0x63, 0xc6, 0x35, 0xfc,
|
||||
0x8c, 0xf3, 0xcb, 0x45, 0x5c, 0x5e, 0x82, 0x98, 0xf1, 0x94, 0x35, 0x43, 0xc4, 0x0c, 0xc5, 0xda,
|
||||
0x98, 0x71, 0x1d, 0xd6, 0xc9, 0xf9, 0x69, 0x99, 0x81, 0x98, 0xf1, 0x6c, 0x28, 0x84, 0x88, 0x19,
|
||||
0x02, 0xb5, 0xb3, 0x93, 0xeb, 0x6d, 0xc2, 0xe0, 0x71, 0x8d, 0xa7, 0x3e, 0x61, 0xd4, 0x71, 0x0d,
|
||||
0x82, 0xc1, 0x10, 0xda, 0x2f, 0xe3, 0xe2, 0x02, 0x0f, 0x21, 0x29, 0x0a, 0x87, 0x90, 0x46, 0x60,
|
||||
0x7f, 0x4f, 0x58, 0x5c, 0xce, 0x2e, 0xf0, 0xfe, 0x6e, 0x64, 0xe1, 0xfe, 0x36, 0x0c, 0xec, 0xef,
|
||||
0x46, 0xf0, 0x32, 0x15, 0x17, 0x87, 0x4c, 0xc4, 0x78, 0x7f, 0xfb, 0x4c, 0xb8, 0xbf, 0x5b, 0xac,
|
||||
0xcd, 0xfe, 0x5d, 0x87, 0x93, 0xe5, 0x59, 0x35, 0x2b, 0xd3, 0x33, 0x36, 0x08, 0x58, 0x31, 0x10,
|
||||
0x91, 0xfd, 0x93, 0xb0, 0xf2, 0xf9, 0x93, 0xb5, 0xe8, 0xa6, 0xee, 0x76, 0x5e, 0x55, 0x6a, 0xed,
|
||||
0xf3, 0xdd, 0x7f, 0x84, 0xf7, 0x2f, 0x81, 0x13, 0x8f, 0x33, 0x7a, 0xa8, 0x39, 0xb9, 0x01, 0x5e,
|
||||
0xa4, 0xd3, 0xbc, 0x32, 0x85, 0xfa, 0xa4, 0x8f, 0x75, 0x47, 0x81, 0xc8, 0x0d, 0x7a, 0x29, 0xda,
|
||||
0xb4, 0x4c, 0xf5, 0x8f, 0x96, 0x8d, 0x93, 0x0a, 0xa4, 0x65, 0xba, 0xbd, 0x1d, 0x82, 0x48, 0xcb,
|
||||
0x70, 0x12, 0x86, 0xc2, 0x7e, 0xc9, 0x97, 0x45, 0xd5, 0x11, 0x0a, 0x00, 0x0a, 0x87, 0x42, 0x1b,
|
||||
0x56, 0x3e, 0x5f, 0x47, 0xbf, 0xe9, 0x86, 0x9f, 0xdb, 0xd8, 0x5b, 0x74, 0x4c, 0x61, 0x4d, 0x3c,
|
||||
0xec, 0x8b, 0xdb, 0x8c, 0x42, 0x7b, 0x16, 0xbb, 0x4c, 0xc4, 0x69, 0x56, 0x0d, 0xd6, 0x71, 0x1b,
|
||||
0x5a, 0x4e, 0x64, 0x14, 0x18, 0x07, 0xe7, 0xb7, 0xdd, 0x65, 0x91, 0xa5, 0xb3, 0xf6, 0xc3, 0x24,
|
||||
0xa5, 0x6b, 0xc4, 0xe1, 0xf9, 0xcd, 0xc5, 0xe0, 0x7c, 0x5d, 0xa7, 0x7e, 0xf2, 0x3f, 0xd3, 0x55,
|
||||
0xc1, 0xf0, 0xf9, 0xda, 0x43, 0xc2, 0xf3, 0x35, 0x44, 0x61, 0x7d, 0x26, 0x4c, 0x1c, 0xc4, 0x2b,
|
||||
0xbe, 0x24, 0xe6, 0x6b, 0x23, 0x0e, 0xd7, 0xc7, 0xc5, 0xec, 0xde, 0xc0, 0x78, 0x18, 0xe7, 0x82,
|
||||
0x95, 0x79, 0x9c, 0xed, 0x65, 0xf1, 0xbc, 0x1a, 0x10, 0x73, 0x8c, 0x4f, 0x11, 0x7b, 0x03, 0x9a,
|
||||
0x46, 0x9a, 0x71, 0x5c, 0xed, 0xc5, 0x57, 0xbc, 0x4c, 0x05, 0xdd, 0x8c, 0x16, 0xe9, 0x6c, 0x46,
|
||||
0x0f, 0x45, 0xbd, 0x8d, 0xca, 0xd9, 0x45, 0x7a, 0xc5, 0x92, 0x80, 0x37, 0x8d, 0xf4, 0xf0, 0xe6,
|
||||
0xa0, 0x48, 0xa7, 0x4d, 0xf8, 0xb2, 0x9c, 0x31, 0xb2, 0xd3, 0x1a, 0x71, 0x67, 0xa7, 0x19, 0x4c,
|
||||
0x79, 0xf8, 0xab, 0xb5, 0xe8, 0xb7, 0x1a, 0xa9, 0xfb, 0x84, 0x67, 0x37, 0xae, 0x2e, 0xce, 0x78,
|
||||
0x5c, 0x26, 0x83, 0x0f, 0x30, 0x3b, 0x28, 0x6a, 0x5c, 0x3f, 0xb9, 0x8e, 0x0a, 0x6c, 0xd6, 0x3a,
|
||||
0xef, 0xb6, 0x23, 0x0e, 0x6d, 0x56, 0x0f, 0x09, 0x37, 0x2b, 0x44, 0xe1, 0x04, 0x22, 0xe5, 0xcd,
|
||||
0x01, 0xe0, 0x3a, 0xa9, 0xef, 0x9f, 0x02, 0x6e, 0x74, 0x72, 0x70, 0x7e, 0xac, 0x85, 0x7e, 0xb4,
|
||||
0x6c, 0x51, 0x36, 0xf0, 0x88, 0x19, 0xf6, 0xc5, 0x49, 0xcf, 0x66, 0x54, 0x84, 0x3d, 0xb7, 0x46,
|
||||
0xc6, 0xb0, 0x2f, 0x4e, 0x78, 0x76, 0xa6, 0xb5, 0x90, 0x67, 0x64, 0x6a, 0x1b, 0xf6, 0xc5, 0x61,
|
||||
0xf6, 0xa5, 0x18, 0xbd, 0x2e, 0x3c, 0x0c, 0xd8, 0x81, 0x6b, 0xc3, 0x66, 0x2f, 0x56, 0x39, 0xfc,
|
||||
0x9b, 0xb5, 0xe8, 0xbb, 0xd6, 0xe3, 0x21, 0x4f, 0xd2, 0xf3, 0x55, 0x03, 0xbd, 0x88, 0xb3, 0x25,
|
||||
0xab, 0x06, 0x4f, 0x28, 0x6b, 0x6d, 0xd6, 0x94, 0xe0, 0xe9, 0xb5, 0x74, 0xe0, 0xd8, 0x19, 0x15,
|
||||
0x45, 0xb6, 0x9a, 0xb2, 0x45, 0x91, 0x91, 0x63, 0xc7, 0x43, 0xc2, 0x63, 0x07, 0xa2, 0x30, 0x2b,
|
||||
0x9f, 0xf2, 0x3a, 0xe7, 0x47, 0xb3, 0x72, 0x29, 0x0a, 0x67, 0xe5, 0x1a, 0x81, 0xb9, 0xd2, 0x94,
|
||||
0xef, 0xf0, 0x2c, 0x63, 0x33, 0xd1, 0xbe, 0x25, 0x62, 0x34, 0x2d, 0x11, 0xce, 0x95, 0x00, 0x69,
|
||||
0x4f, 0xe5, 0xf4, 0x1e, 0x32, 0x2e, 0xd9, 0xb3, 0xd5, 0x41, 0x9a, 0x5f, 0x0e, 0xf0, 0xb4, 0xc0,
|
||||
0x02, 0xc4, 0xa9, 0x1c, 0x0a, 0xc2, 0xbd, 0xea, 0x69, 0x9e, 0x70, 0x7c, 0xaf, 0x5a, 0x4b, 0xc2,
|
||||
0x7b, 0x55, 0x45, 0x40, 0x93, 0x27, 0x8c, 0x32, 0x59, 0x4b, 0xc2, 0x26, 0x15, 0x81, 0x4d, 0x85,
|
||||
0xea, 0x49, 0x11, 0x39, 0x15, 0x82, 0x67, 0x43, 0x1b, 0x9d, 0x1c, 0xdc, 0x73, 0x29, 0x07, 0x68,
|
||||
0x44, 0x00, 0xe3, 0x77, 0x82, 0x0c, 0x0c, 0x7d, 0xbd, 0x1b, 0xde, 0x63, 0x62, 0x76, 0x81, 0x87,
|
||||
0xbe, 0x87, 0x84, 0x43, 0x1f, 0xa2, 0xb0, 0xad, 0xa6, 0xdc, 0xec, 0xe6, 0xd7, 0xf1, 0xc0, 0x6b,
|
||||
0xed, 0xe4, 0x37, 0x3a, 0x39, 0xd8, 0x56, 0xe3, 0x05, 0xdd, 0x56, 0x8d, 0x2c, 0xdc, 0x56, 0x86,
|
||||
0x81, 0xa5, 0x6f, 0x04, 0xf2, 0x90, 0x6c, 0x9d, 0x56, 0xf4, 0x8e, 0xc9, 0x36, 0x3a, 0x39, 0xe5,
|
||||
0xe4, 0x5f, 0xcc, 0xfe, 0xb0, 0x91, 0x1e, 0xf1, 0x7a, 0xf0, 0xbd, 0x88, 0xb3, 0x34, 0x89, 0x05,
|
||||
0x9b, 0xf2, 0x4b, 0x96, 0xe3, 0x5b, 0x31, 0x55, 0xda, 0x86, 0x1f, 0x7a, 0x0a, 0xe1, 0xad, 0x58,
|
||||
0x58, 0x11, 0xc6, 0x49, 0x43, 0x9f, 0x56, 0x6c, 0x27, 0xae, 0x88, 0x29, 0xd2, 0x43, 0xc2, 0x71,
|
||||
0x02, 0x51, 0x98, 0x08, 0x37, 0xf2, 0xe7, 0xaf, 0x0b, 0x56, 0xa6, 0x2c, 0x9f, 0x31, 0x3c, 0x11,
|
||||
0x86, 0x54, 0x38, 0x11, 0x46, 0x68, 0xb8, 0x09, 0xdc, 0x8d, 0x05, 0x7b, 0xb6, 0x9a, 0xa6, 0x0b,
|
||||
0x56, 0x89, 0x78, 0x51, 0xe0, 0x9b, 0x40, 0x00, 0x85, 0x37, 0x81, 0x6d, 0xb8, 0x75, 0xe6, 0x64,
|
||||
0x66, 0xda, 0xf6, 0xad, 0x35, 0x48, 0x04, 0x6e, 0xad, 0x11, 0x28, 0x6c, 0x58, 0x0b, 0xa0, 0x4f,
|
||||
0x1f, 0x5a, 0x56, 0x82, 0x4f, 0x1f, 0x68, 0xba, 0x75, 0x92, 0x67, 0x98, 0x49, 0x3d, 0x34, 0x3b,
|
||||
0x8a, 0x3e, 0x71, 0x87, 0xe8, 0x66, 0x2f, 0x16, 0x3f, 0x3a, 0x3c, 0x61, 0x59, 0x2c, 0xd7, 0xc3,
|
||||
0xc0, 0xf9, 0x9c, 0x66, 0xfa, 0x1c, 0x1d, 0x3a, 0xac, 0x72, 0xf8, 0x17, 0x6b, 0xd1, 0x7b, 0x98,
|
||||
0xc7, 0x2f, 0x0a, 0xe9, 0xf7, 0x71, 0xb7, 0xad, 0x86, 0x24, 0xae, 0xe5, 0x85, 0x35, 0xec, 0xcd,
|
||||
0x12, 0x2d, 0xb2, 0xb7, 0xf6, 0x54, 0x01, 0xfc, 0x6c, 0xd0, 0x94, 0x1f, 0x72, 0xc4, 0xcd, 0x92,
|
||||
0x10, 0x6f, 0x37, 0x5a, 0x7e, 0xb9, 0x2a, 0xb0, 0xd1, 0x32, 0x36, 0x94, 0x98, 0xd8, 0x68, 0x21,
|
||||
0x98, 0x1d, 0x9d, 0x6e, 0xf5, 0x5e, 0xa6, 0xe2, 0x42, 0x26, 0x72, 0x60, 0x74, 0x7a, 0x65, 0x35,
|
||||
0x10, 0x31, 0x3a, 0x49, 0x18, 0xa6, 0x3a, 0x1a, 0xac, 0xc7, 0x26, 0x36, 0x97, 0x1b, 0x43, 0xee,
|
||||
0xc8, 0xbc, 0xdf, 0x0d, 0xc2, 0x78, 0xd5, 0x62, 0xb5, 0xa7, 0x7a, 0x18, 0xb2, 0x00, 0xf6, 0x55,
|
||||
0x9b, 0xbd, 0x58, 0xe5, 0xf0, 0xcf, 0xa2, 0xef, 0xb4, 0x2a, 0xb6, 0xc7, 0x62, 0xb1, 0x2c, 0x59,
|
||||
0x32, 0xd8, 0xee, 0x28, 0xb7, 0x06, 0x8d, 0xeb, 0xc7, 0xfd, 0x15, 0x5a, 0xc9, 0xbf, 0xe6, 0x9a,
|
||||
0xb0, 0x32, 0x65, 0x78, 0x12, 0x32, 0xe9, 0xb3, 0xc1, 0xe4, 0x9f, 0xd6, 0x69, 0xed, 0xdf, 0xdd,
|
||||
0xe8, 0x1a, 0x5d, 0xc5, 0x69, 0x26, 0x9f, 0x02, 0x7f, 0x10, 0x32, 0xea, 0xa1, 0xc1, 0xfd, 0x3b,
|
||||
0xa9, 0xd2, 0x9a, 0x99, 0xe5, 0x18, 0x77, 0xf6, 0x7d, 0x8f, 0xe8, 0x99, 0x00, 0xd9, 0xf6, 0x6d,
|
||||
0xf5, 0xa4, 0x95, 0x5b, 0xa1, 0x97, 0xbc, 0xfa, 0xcf, 0x6e, 0x90, 0x63, 0x5e, 0x95, 0x2a, 0x12,
|
||||
0xe9, 0x5b, 0x3d, 0x69, 0xe5, 0xf5, 0x4f, 0xa3, 0x77, 0xdb, 0x5e, 0xd5, 0x42, 0xb4, 0xdd, 0x69,
|
||||
0x0a, 0xac, 0x45, 0x8f, 0xfb, 0x2b, 0x28, 0xf7, 0xff, 0x6e, 0x0e, 0xbc, 0x1b, 0xff, 0x33, 0xbe,
|
||||
0x58, 0xb0, 0x3c, 0x61, 0x89, 0xd6, 0xa8, 0xea, 0x8d, 0xd9, 0xa7, 0xb4, 0x5d, 0xa3, 0x30, 0x74,
|
||||
0x35, 0x4c, 0x89, 0x7e, 0xfb, 0x1b, 0x68, 0xaa, 0xa2, 0xfd, 0xf7, 0x5a, 0xf4, 0x00, 0x2d, 0x9a,
|
||||
0x0e, 0x5c, 0xaf, 0x88, 0xbf, 0xd7, 0xc7, 0x11, 0xa6, 0x69, 0x8a, 0x3a, 0xfa, 0x39, 0x2c, 0xa8,
|
||||
0x22, 0xff, 0xc7, 0x5a, 0x74, 0xdb, 0x2a, 0xd6, 0xe1, 0xbd, 0xc3, 0xf3, 0xf3, 0x2c, 0x9d, 0x09,
|
||||
0xf9, 0xa8, 0x57, 0xa9, 0xd0, 0xcd, 0x49, 0x69, 0x74, 0x37, 0x67, 0x40, 0xd3, 0xee, 0x8a, 0x3f,
|
||||
0x4b, 0x2b, 0xc1, 0xcb, 0xd5, 0xe4, 0x82, 0xbf, 0xd2, 0xef, 0x3d, 0xf9, 0xf3, 0xb2, 0x02, 0x86,
|
||||
0x0e, 0x41, 0xec, 0x8a, 0x71, 0xb2, 0xe5, 0xca, 0xbe, 0x1f, 0x55, 0x11, 0xae, 0x1c, 0xa2, 0xc3,
|
||||
0x95, 0x4f, 0xda, 0x55, 0x49, 0xd7, 0xca, 0xbe, 0xcc, 0xb5, 0x81, 0x17, 0xb5, 0xfd, 0x42, 0xd7,
|
||||
0xfd, 0x6e, 0xd0, 0xe6, 0xa6, 0x4a, 0xbc, 0x9b, 0x9e, 0x9f, 0x9b, 0x3a, 0xe1, 0x25, 0x75, 0x11,
|
||||
0x22, 0x37, 0x25, 0x50, 0xbb, 0xbd, 0xda, 0x4b, 0x33, 0x26, 0x1f, 0x0a, 0x7d, 0x71, 0x7e, 0x9e,
|
||||
0xf1, 0x38, 0x01, 0xdb, 0xab, 0x5a, 0x3c, 0x74, 0xe5, 0xc4, 0xf6, 0x0a, 0xe3, 0xec, 0x75, 0x93,
|
||||
0x5a, 0x5a, 0x47, 0x77, 0x3e, 0x4b, 0x33, 0x78, 0x6d, 0x5a, 0x6a, 0x1a, 0x21, 0x71, 0xdd, 0xa4,
|
||||
0x05, 0xd9, 0x14, 0xa8, 0x16, 0xd5, 0x51, 0xa9, 0xcb, 0x7f, 0xaf, 0xad, 0xe8, 0x88, 0x89, 0x14,
|
||||
0x08, 0xc1, 0xec, 0xf1, 0x45, 0x2d, 0x3c, 0x2d, 0xa4, 0xf1, 0x5b, 0x6d, 0xad, 0x46, 0x42, 0x1c,
|
||||
0x5f, 0xf8, 0x84, 0xdd, 0x2d, 0xd7, 0x7f, 0xdf, 0xe5, 0xaf, 0x72, 0x69, 0xf4, 0x76, 0x5b, 0x45,
|
||||
0xcb, 0x88, 0xdd, 0x32, 0x64, 0x94, 0xe1, 0xcf, 0xa3, 0x5f, 0x94, 0x86, 0x4b, 0x5e, 0x0c, 0x6e,
|
||||
0x20, 0x0a, 0xa5, 0x73, 0xc9, 0xf8, 0x26, 0x29, 0xb7, 0xb7, 0x53, 0x4c, 0x6c, 0x9c, 0x56, 0xf1,
|
||||
0x1c, 0xbe, 0x19, 0x60, 0x7b, 0x5c, 0x4a, 0x89, 0xdb, 0x29, 0x6d, 0xca, 0x8f, 0x8a, 0x23, 0x9e,
|
||||
0x28, 0xeb, 0x48, 0x0d, 0x8d, 0x30, 0x14, 0x15, 0x2e, 0x64, 0xd3, 0xd6, 0xa3, 0xf8, 0x2a, 0x9d,
|
||||
0x9b, 0xd4, 0xa2, 0x99, 0xc0, 0x2a, 0x90, 0xb6, 0x5a, 0x66, 0xe8, 0x40, 0x44, 0xda, 0x4a, 0xc2,
|
||||
0xca, 0xe7, 0x3f, 0xaf, 0x45, 0xb7, 0x2c, 0xb3, 0xaf, 0x0f, 0x7c, 0xc7, 0xf9, 0x39, 0xaf, 0x93,
|
||||
0xdc, 0x83, 0x34, 0xbf, 0xac, 0x06, 0x1f, 0x53, 0x26, 0x71, 0xde, 0x14, 0xe5, 0x93, 0x6b, 0xeb,
|
||||
0xd9, 0xfd, 0x89, 0x3e, 0x0d, 0xb5, 0x57, 0x22, 0x1a, 0x0d, 0xb0, 0x3f, 0x31, 0x87, 0xa6, 0x90,
|
||||
0x23, 0xf6, 0x27, 0x21, 0xde, 0x76, 0xb1, 0x71, 0x9e, 0xf1, 0x1c, 0x76, 0xb1, 0xb5, 0x50, 0x0b,
|
||||
0x89, 0x2e, 0x6e, 0x41, 0x76, 0x3e, 0xd6, 0xa2, 0xe6, 0x7c, 0x6d, 0x94, 0x65, 0x60, 0x3e, 0x36,
|
||||
0xaa, 0x06, 0x20, 0xe6, 0x63, 0x14, 0x54, 0x7e, 0x4e, 0xa2, 0x6f, 0xd5, 0x4d, 0x7a, 0x5c, 0xb2,
|
||||
0xab, 0x94, 0xc1, 0xdb, 0x3b, 0x8e, 0x84, 0x18, 0xff, 0x3e, 0x61, 0x47, 0xd6, 0x69, 0x5e, 0x15,
|
||||
0x59, 0x5c, 0x5d, 0xa8, 0xfb, 0x1c, 0x7e, 0x9d, 0xb5, 0x10, 0xde, 0xe8, 0xb8, 0xd7, 0x41, 0xd9,
|
||||
0x49, 0x5d, 0xcb, 0xcc, 0x14, 0xb3, 0x8e, 0xab, 0xb6, 0xa6, 0x99, 0x8d, 0x4e, 0xce, 0x3e, 0x34,
|
||||
0xd9, 0x8f, 0xb3, 0x8c, 0x95, 0x2b, 0x2d, 0x3b, 0x8c, 0xf3, 0xf4, 0x9c, 0x55, 0x02, 0x3c, 0x34,
|
||||
0x51, 0xd4, 0x10, 0x62, 0xc4, 0x43, 0x93, 0x00, 0x6e, 0xf7, 0x6d, 0xc0, 0xf3, 0x38, 0x4f, 0xd8,
|
||||
0x6b, 0xb0, 0x6f, 0x83, 0x76, 0x24, 0x43, 0xec, 0xdb, 0x28, 0xd6, 0x3e, 0x3c, 0x78, 0x96, 0xf1,
|
||||
0xd9, 0xa5, 0x5a, 0x02, 0xfc, 0x0e, 0x96, 0x12, 0xb8, 0x06, 0xdc, 0x0e, 0x21, 0x76, 0x11, 0x90,
|
||||
0x82, 0x13, 0x56, 0x64, 0xf1, 0x0c, 0x5e, 0xe1, 0x6a, 0x74, 0x94, 0x8c, 0x58, 0x04, 0x20, 0x03,
|
||||
0x8a, 0xab, 0xae, 0x86, 0x61, 0xc5, 0x05, 0x37, 0xc3, 0x6e, 0x87, 0x10, 0xbb, 0x0c, 0x4a, 0xc1,
|
||||
0xa4, 0xc8, 0x52, 0x01, 0x86, 0x41, 0xa3, 0x21, 0x25, 0xc4, 0x30, 0xf0, 0x09, 0x60, 0xf2, 0x90,
|
||||
0x95, 0x73, 0x86, 0x9a, 0x94, 0x92, 0xa0, 0x49, 0x4d, 0xd8, 0xfb, 0xea, 0x4d, 0xdd, 0x79, 0xb1,
|
||||
0x02, 0xf7, 0xd5, 0x55, 0xb5, 0x78, 0xb1, 0x22, 0xee, 0xab, 0x7b, 0x00, 0x28, 0xe2, 0x71, 0x5c,
|
||||
0x09, 0xbc, 0x88, 0x52, 0x12, 0x2c, 0xa2, 0x26, 0xec, 0x1a, 0xdd, 0x14, 0x71, 0x29, 0xc0, 0x1a,
|
||||
0xad, 0x0a, 0xe0, 0x5c, 0x62, 0xb8, 0x49, 0xca, 0xed, 0x4c, 0xd2, 0xf4, 0x0a, 0x13, 0x7b, 0x29,
|
||||
0xcb, 0x92, 0x0a, 0xcc, 0x24, 0xaa, 0xdd, 0xb5, 0x94, 0x98, 0x49, 0xda, 0x14, 0x08, 0x25, 0xf5,
|
||||
0x04, 0x04, 0xab, 0x1d, 0x78, 0x00, 0x72, 0x3b, 0x84, 0xd8, 0xf9, 0x49, 0x17, 0x7a, 0x27, 0x2e,
|
||||
0xcb, 0xb4, 0x5e, 0xfc, 0xd7, 0xf1, 0x02, 0x69, 0x39, 0x31, 0x3f, 0x61, 0x1c, 0x18, 0x5e, 0x7a,
|
||||
0xe2, 0xc6, 0x0a, 0x06, 0xa7, 0xee, 0x3b, 0x41, 0xc6, 0x66, 0x9c, 0x52, 0xe2, 0x3c, 0x85, 0xc7,
|
||||
0x5a, 0x13, 0x79, 0x08, 0xbf, 0xde, 0x85, 0x39, 0xaf, 0xce, 0x19, 0x17, 0x87, 0xfc, 0x8a, 0x4d,
|
||||
0xf9, 0xf3, 0xd7, 0x69, 0x55, 0x6f, 0xb7, 0xd4, 0xca, 0xfd, 0x94, 0xb0, 0x84, 0xc1, 0xc4, 0xab,
|
||||
0x73, 0x9d, 0x4a, 0x36, 0x81, 0x00, 0x65, 0x39, 0x62, 0xaf, 0xd0, 0x04, 0x02, 0x5a, 0x34, 0x1c,
|
||||
0x91, 0x40, 0x84, 0x78, 0x7b, 0x62, 0x66, 0x9c, 0xab, 0xef, 0x25, 0x4c, 0xb9, 0xce, 0xe5, 0x28,
|
||||
0x6b, 0x10, 0x24, 0x0e, 0x2d, 0x82, 0x0a, 0x76, 0x7f, 0x69, 0xfc, 0xdb, 0x21, 0x76, 0x9f, 0xb0,
|
||||
0xd3, 0x1e, 0x66, 0x0f, 0x7a, 0x90, 0x88, 0x2b, 0x7b, 0x95, 0x84, 0x72, 0xd5, 0xbe, 0x49, 0xf2,
|
||||
0xa0, 0x07, 0xe9, 0x9c, 0xbe, 0xb9, 0xd5, 0x7a, 0x16, 0xcf, 0x2e, 0xe7, 0x25, 0x5f, 0xe6, 0xc9,
|
||||
0x0e, 0xcf, 0x78, 0x09, 0x4e, 0xdf, 0xbc, 0x52, 0x03, 0x94, 0x38, 0x7d, 0xeb, 0x50, 0xb1, 0x19,
|
||||
0x9c, 0x5b, 0x8a, 0x51, 0x96, 0xce, 0xe1, 0x8e, 0xda, 0x33, 0x24, 0x01, 0x22, 0x83, 0x43, 0x41,
|
||||
0x24, 0x88, 0x9a, 0x1d, 0xb7, 0x48, 0x67, 0x71, 0xd6, 0xf8, 0xdb, 0xa6, 0xcd, 0x78, 0x60, 0x67,
|
||||
0x10, 0x21, 0x0a, 0x48, 0x3d, 0xa7, 0xcb, 0x32, 0x1f, 0xe7, 0x82, 0x93, 0xf5, 0xd4, 0x40, 0x67,
|
||||
0x3d, 0x1d, 0x10, 0x4c, 0xab, 0x53, 0xf6, 0xba, 0x2e, 0x4d, 0xfd, 0x0f, 0x36, 0xad, 0xd6, 0x7f,
|
||||
0x1f, 0x2a, 0x79, 0x68, 0x5a, 0x05, 0x1c, 0xa8, 0x8c, 0x72, 0xd2, 0x04, 0x4c, 0x40, 0xdb, 0x0f,
|
||||
0x93, 0xfb, 0xdd, 0x20, 0xee, 0x67, 0x22, 0x56, 0x19, 0x0b, 0xf9, 0x91, 0x40, 0x1f, 0x3f, 0x1a,
|
||||
0xb4, 0xc7, 0x2d, 0x5e, 0x7d, 0x2e, 0xd8, 0xec, 0xb2, 0x75, 0x33, 0xce, 0x2f, 0x68, 0x83, 0x10,
|
||||
0xc7, 0x2d, 0x04, 0x8a, 0x77, 0xd1, 0x78, 0xc6, 0xf3, 0x50, 0x17, 0xd5, 0xf2, 0x3e, 0x5d, 0xa4,
|
||||
0x38, 0xbb, 0xf9, 0x35, 0x52, 0x15, 0x99, 0x4d, 0x37, 0x6d, 0x12, 0x16, 0x5c, 0x88, 0xd8, 0xfc,
|
||||
0x92, 0xb0, 0xcd, 0xc9, 0xa1, 0xcf, 0xc3, 0xf6, 0x6b, 0x03, 0x2d, 0x2b, 0x87, 0xf4, 0x6b, 0x03,
|
||||
0x14, 0x4b, 0x57, 0xb2, 0x89, 0x91, 0x0e, 0x2b, 0x7e, 0x9c, 0x3c, 0xea, 0x07, 0xdb, 0x2d, 0x8f,
|
||||
0xe7, 0x73, 0x27, 0x63, 0x71, 0xd9, 0x78, 0xdd, 0x0a, 0x18, 0xb2, 0x18, 0xb1, 0xe5, 0x09, 0xe0,
|
||||
0x60, 0x0a, 0xf3, 0x3c, 0xef, 0xf0, 0x5c, 0xb0, 0x5c, 0x60, 0x53, 0x98, 0x6f, 0x4c, 0x81, 0xa1,
|
||||
0x29, 0x8c, 0x52, 0x00, 0x71, 0x2b, 0xcf, 0x83, 0x98, 0x38, 0x8a, 0x17, 0x68, 0xc6, 0xd6, 0x9c,
|
||||
0xf5, 0x34, 0xf2, 0x50, 0xdc, 0x02, 0xce, 0x79, 0x9c, 0xeb, 0x7a, 0x99, 0xc6, 0xe5, 0xdc, 0x9c,
|
||||
0x6e, 0x24, 0x83, 0xc7, 0xb4, 0x1d, 0x9f, 0x24, 0x1e, 0xe7, 0x86, 0x35, 0xc0, 0xb4, 0x33, 0x5e,
|
||||
0xc4, 0x73, 0x53, 0x53, 0xa4, 0x06, 0x52, 0xde, 0xaa, 0xea, 0xfd, 0x6e, 0x10, 0xf8, 0x79, 0x91,
|
||||
0x26, 0x8c, 0x07, 0xfc, 0x48, 0x79, 0x1f, 0x3f, 0x10, 0x04, 0xd9, 0x5b, 0x5d, 0xef, 0x66, 0x47,
|
||||
0x37, 0xca, 0x13, 0xb5, 0x8f, 0x1d, 0x12, 0xcd, 0x03, 0xb8, 0x50, 0xf6, 0x46, 0xf0, 0x60, 0x8c,
|
||||
0xea, 0x03, 0xda, 0xd0, 0x18, 0x35, 0xe7, 0xaf, 0x7d, 0xc6, 0x28, 0x06, 0x2b, 0x9f, 0x3f, 0x56,
|
||||
0x63, 0x74, 0x37, 0x16, 0x71, 0x9d, 0xb7, 0xbf, 0x48, 0xd9, 0x2b, 0xb5, 0x11, 0x46, 0xea, 0xab,
|
||||
0xa9, 0xa1, 0x7c, 0xeb, 0x19, 0xec, 0x8a, 0xb7, 0x7b, 0xf3, 0x01, 0xdf, 0x6a, 0x87, 0xd0, 0xe9,
|
||||
0x1b, 0x6c, 0x15, 0xb6, 0x7b, 0xf3, 0x01, 0xdf, 0xea, 0xe3, 0x0d, 0x9d, 0xbe, 0xc1, 0x17, 0x1c,
|
||||
0xb6, 0x7b, 0xf3, 0xca, 0xf7, 0x5f, 0xea, 0x81, 0xeb, 0x3a, 0xaf, 0xf3, 0xb0, 0x99, 0x48, 0xaf,
|
||||
0x18, 0x96, 0x4e, 0xfa, 0xf6, 0x0c, 0x1a, 0x4a, 0x27, 0x69, 0x15, 0xe7, 0xf3, 0x69, 0x58, 0x29,
|
||||
0x8e, 0x79, 0x95, 0xca, 0xeb, 0x18, 0x4f, 0x7b, 0x18, 0xd5, 0x70, 0x68, 0xd3, 0x14, 0x52, 0xb2,
|
||||
0x0f, 0x96, 0x3d, 0xd4, 0x5e, 0x84, 0x7f, 0x14, 0xb0, 0xd7, 0xbe, 0x0f, 0xbf, 0xd5, 0x93, 0xb6,
|
||||
0x8f, 0x78, 0x3d, 0x46, 0x3f, 0x9c, 0x9b, 0x30, 0x74, 0x95, 0x30, 0xa6, 0xcc, 0x43, 0x5b, 0xf7,
|
||||
0x29, 0xe5, 0xe3, 0xfe, 0x0a, 0x1d, 0xee, 0x47, 0x49, 0xd2, 0xcf, 0xbd, 0xfb, 0x74, 0xfb, 0x71,
|
||||
0x7f, 0x05, 0xe5, 0xfe, 0xaf, 0xf5, 0xb6, 0x06, 0xfa, 0x57, 0x63, 0xf0, 0x49, 0x1f, 0x8b, 0x60,
|
||||
0x1c, 0x3e, 0xbd, 0x96, 0x8e, 0x2a, 0xc8, 0xdf, 0xeb, 0xfd, 0xbb, 0x46, 0xe5, 0xdb, 0x48, 0xf2,
|
||||
0x2d, 0x66, 0x35, 0x24, 0x43, 0x51, 0x65, 0x61, 0x38, 0x30, 0x3f, 0xba, 0xa6, 0x96, 0xf3, 0x2d,
|
||||
0x3f, 0x0f, 0x56, 0x6f, 0xcd, 0x3a, 0xe5, 0x09, 0x59, 0x76, 0x68, 0x58, 0xa0, 0x8f, 0xaf, 0xab,
|
||||
0x46, 0x0d, 0x55, 0x07, 0x96, 0x5f, 0xb3, 0x79, 0xda, 0xd3, 0xb0, 0xf7, 0x7d, 0x9b, 0x0f, 0xaf,
|
||||
0xa7, 0xa4, 0xca, 0xf2, 0x5f, 0x6b, 0xd1, 0x3d, 0x8f, 0xb5, 0x8f, 0x33, 0xc0, 0xa1, 0xcb, 0x0f,
|
||||
0x02, 0xf6, 0x29, 0x25, 0x53, 0xb8, 0xdf, 0xf9, 0x66, 0xca, 0xf6, 0xc3, 0x77, 0x9e, 0xca, 0x5e,
|
||||
0x9a, 0x09, 0x56, 0xb6, 0x3f, 0x7c, 0xe7, 0xdb, 0x6d, 0xa8, 0x21, 0xfd, 0xe1, 0xbb, 0x00, 0xee,
|
||||
0x7c, 0xf8, 0x0e, 0xf1, 0x8c, 0x7e, 0xf8, 0x0e, 0xb5, 0x16, 0xfc, 0xf0, 0x5d, 0x58, 0x83, 0x5a,
|
||||
0x5d, 0x74, 0x11, 0x9a, 0x63, 0xf3, 0x5e, 0x16, 0xfd, 0x53, 0xf4, 0x27, 0xd7, 0x51, 0x21, 0xd6,
|
||||
0xd7, 0x86, 0x93, 0x17, 0x2a, 0x7b, 0xb4, 0xa9, 0x77, 0xa9, 0x72, 0xbb, 0x37, 0xaf, 0x7c, 0xff,
|
||||
0x48, 0x6d, 0xae, 0xcc, 0x6a, 0xc2, 0x4b, 0xf9, 0xd1, 0xc3, 0xcd, 0xd0, 0xea, 0x50, 0x5b, 0x70,
|
||||
0x7b, 0xfe, 0x51, 0x3f, 0x98, 0xa8, 0x6e, 0x4d, 0xa8, 0x4e, 0x1f, 0x76, 0x19, 0x02, 0x5d, 0xbe,
|
||||
0xdd, 0x9b, 0x27, 0x96, 0x91, 0xc6, 0x77, 0xd3, 0xdb, 0x3d, 0x8c, 0xf9, 0x7d, 0xfd, 0xb8, 0xbf,
|
||||
0x82, 0x72, 0x7f, 0xa5, 0xb2, 0x56, 0xd7, 0xbd, 0xec, 0xe7, 0xad, 0x2e, 0x53, 0x13, 0xaf, 0x9b,
|
||||
0x87, 0x7d, 0xf1, 0x50, 0xfe, 0xe2, 0x2e, 0xa1, 0x5d, 0xf9, 0x0b, 0xba, 0x8c, 0x7e, 0x78, 0x3d,
|
||||
0x25, 0x55, 0x96, 0x7f, 0x5a, 0x8b, 0x6e, 0x92, 0x65, 0x51, 0x71, 0xf0, 0x71, 0x5f, 0xcb, 0x20,
|
||||
0x1e, 0x3e, 0xb9, 0xb6, 0x9e, 0x2a, 0xd4, 0xbf, 0xae, 0x45, 0xb7, 0x02, 0x85, 0x6a, 0x02, 0xe4,
|
||||
0x1a, 0xd6, 0xfd, 0x40, 0xf9, 0xf4, 0xfa, 0x8a, 0xd4, 0x72, 0xef, 0xe2, 0x93, 0xf6, 0x47, 0xcc,
|
||||
0x02, 0xb6, 0x27, 0xf4, 0x47, 0xcc, 0xba, 0xb5, 0xe0, 0x19, 0x53, 0x7c, 0xa6, 0xf7, 0x7c, 0xe8,
|
||||
0x19, 0x93, 0xbc, 0x0a, 0x19, 0xfc, 0x3c, 0x0a, 0xc6, 0x61, 0x4e, 0x9e, 0xbf, 0x2e, 0xe2, 0x3c,
|
||||
0xa1, 0x9d, 0x34, 0xf2, 0x6e, 0x27, 0x86, 0x83, 0x67, 0x73, 0xb5, 0xf4, 0x84, 0xeb, 0x7d, 0xdc,
|
||||
0x03, 0x4a, 0xdf, 0x20, 0xc1, 0xb3, 0xb9, 0x16, 0x4a, 0x78, 0x53, 0x59, 0x63, 0xc8, 0x1b, 0x48,
|
||||
0x16, 0x1f, 0xf6, 0x41, 0xc1, 0x0e, 0xc1, 0x78, 0x33, 0x47, 0xfe, 0x8f, 0x42, 0x56, 0x5a, 0xc7,
|
||||
0xfe, 0x5b, 0x3d, 0x69, 0xc2, 0xed, 0x84, 0x89, 0xcf, 0x58, 0x9c, 0xb0, 0x32, 0xe8, 0xd6, 0x50,
|
||||
0xbd, 0xdc, 0xba, 0x34, 0xe6, 0x76, 0x87, 0x67, 0xcb, 0x45, 0xae, 0x3a, 0x93, 0x74, 0xeb, 0x52,
|
||||
0xdd, 0x6e, 0x01, 0x0d, 0x4f, 0x25, 0xad, 0x5b, 0x99, 0x5e, 0x3e, 0x0c, 0x9b, 0xf1, 0xb2, 0xca,
|
||||
0xcd, 0x5e, 0x2c, 0x5d, 0x4f, 0x15, 0x46, 0x1d, 0xf5, 0x04, 0x91, 0xb4, 0xd5, 0x93, 0x86, 0xc7,
|
||||
0x83, 0x8e, 0x5b, 0x13, 0x4f, 0xdb, 0x1d, 0xb6, 0x5a, 0x21, 0xf5, 0xb8, 0xbf, 0x02, 0x3c, 0x8c,
|
||||
0x55, 0x51, 0x75, 0x90, 0x56, 0x62, 0x2f, 0xcd, 0xb2, 0xc1, 0x66, 0x20, 0x4c, 0x34, 0x14, 0x3c,
|
||||
0x8c, 0x45, 0x60, 0x22, 0x92, 0xf5, 0xe1, 0x65, 0x3e, 0xe8, 0xb2, 0x23, 0xa9, 0x5e, 0x91, 0xec,
|
||||
0xd2, 0xe0, 0x40, 0xcd, 0x69, 0x6a, 0x53, 0xdb, 0x61, 0xb8, 0xe1, 0x5a, 0x15, 0xde, 0xee, 0xcd,
|
||||
0x83, 0xa7, 0xfd, 0x92, 0x92, 0x2b, 0xcb, 0x5d, 0xca, 0x84, 0xb7, 0x92, 0xdc, 0xeb, 0xa0, 0xc0,
|
||||
0xa1, 0x64, 0x33, 0x8c, 0x5e, 0xa6, 0xc9, 0x9c, 0x09, 0xf4, 0x41, 0x95, 0x0b, 0x04, 0x1f, 0x54,
|
||||
0x01, 0x10, 0x74, 0x5d, 0xf3, 0x77, 0x73, 0x1a, 0x3b, 0x4e, 0xb0, 0xae, 0x53, 0xca, 0x0e, 0x15,
|
||||
0xea, 0x3a, 0x94, 0x06, 0xb3, 0x81, 0x71, 0xab, 0x3e, 0x28, 0xf1, 0x30, 0x64, 0x06, 0x7c, 0x55,
|
||||
0x62, 0xb3, 0x17, 0x0b, 0x56, 0x14, 0xeb, 0x30, 0x5d, 0xa4, 0x02, 0x5b, 0x51, 0x1c, 0x1b, 0x35,
|
||||
0x12, 0x5a, 0x51, 0xda, 0x28, 0x55, 0xbd, 0x3a, 0x47, 0x18, 0x27, 0xe1, 0xea, 0x35, 0x4c, 0xbf,
|
||||
0xea, 0x19, 0xb6, 0xf5, 0x5c, 0x35, 0x37, 0x21, 0x23, 0x2e, 0xd4, 0x66, 0x19, 0x89, 0x6d, 0xf9,
|
||||
0xa2, 0x31, 0x04, 0x43, 0xb3, 0x0e, 0xa5, 0x00, 0x9f, 0x17, 0xd4, 0x9c, 0x7e, 0xf4, 0x5b, 0x14,
|
||||
0x2c, 0x2e, 0xe3, 0x7c, 0x86, 0x6e, 0x4e, 0xa5, 0xc1, 0x16, 0x19, 0xda, 0x9c, 0x92, 0x1a, 0xe0,
|
||||
0xa9, 0xbd, 0xff, 0x26, 0x2f, 0x32, 0x14, 0xcc, 0x2b, 0xb3, 0xfe, 0x8b, 0xbc, 0x0f, 0x7a, 0x90,
|
||||
0xf0, 0xa9, 0xbd, 0x06, 0xcc, 0xb9, 0x7b, 0xe3, 0xf4, 0x83, 0x80, 0x29, 0x1f, 0x0d, 0x6d, 0x84,
|
||||
0x69, 0x15, 0x10, 0xd4, 0xce, 0xd9, 0xe2, 0xe7, 0x6c, 0x85, 0x05, 0xb5, 0x7b, 0x48, 0xf8, 0x39,
|
||||
0x5b, 0x85, 0x82, 0xba, 0x8d, 0x82, 0x3c, 0xd3, 0xdd, 0x07, 0xad, 0x07, 0xf4, 0xdd, 0xad, 0xcf,
|
||||
0x46, 0x27, 0x07, 0x46, 0xce, 0x6e, 0x7a, 0xe5, 0x3d, 0xa6, 0x40, 0x0a, 0xba, 0x9b, 0x5e, 0xe1,
|
||||
0x4f, 0x29, 0x36, 0x7b, 0xb1, 0xf0, 0x46, 0x40, 0x2c, 0xd8, 0x6b, 0xfd, 0xa8, 0x1e, 0x29, 0xae,
|
||||
0x94, 0xb7, 0x9e, 0xd5, 0xdf, 0xef, 0x06, 0xed, 0xfd, 0xdb, 0xe3, 0x92, 0xcf, 0x58, 0x55, 0xa9,
|
||||
0x6f, 0xad, 0xfa, 0x17, 0x9c, 0x94, 0x6c, 0x08, 0xbe, 0xb4, 0x7a, 0x37, 0x0c, 0x39, 0x1f, 0x48,
|
||||
0x6c, 0x44, 0xf6, 0xbb, 0x4d, 0xeb, 0xa8, 0x66, 0xfb, 0x93, 0x4d, 0x1b, 0x9d, 0x9c, 0x1d, 0x5e,
|
||||
0x4a, 0xea, 0x7e, 0xa8, 0xe9, 0x3e, 0xaa, 0x8e, 0x7d, 0xa3, 0xe9, 0x41, 0x0f, 0x52, 0xb9, 0xfa,
|
||||
0x2c, 0x7a, 0xf3, 0x80, 0xcf, 0x27, 0x2c, 0x4f, 0x06, 0xdf, 0xf3, 0x6f, 0xf0, 0xf2, 0xf9, 0xb0,
|
||||
0xfe, 0xb3, 0x31, 0x7a, 0x83, 0x12, 0xdb, 0x3b, 0x88, 0xbb, 0xec, 0x6c, 0x39, 0x9f, 0x88, 0x58,
|
||||
0x80, 0x3b, 0x88, 0xf2, 0xef, 0xc3, 0x5a, 0x40, 0xdc, 0x41, 0xf4, 0x00, 0x60, 0x6f, 0x5a, 0x32,
|
||||
0x86, 0xda, 0xab, 0x05, 0x41, 0x7b, 0x0a, 0xb0, 0x59, 0x84, 0xb1, 0x57, 0x27, 0xea, 0xf0, 0xce,
|
||||
0xa0, 0xd5, 0x91, 0x52, 0x22, 0x8b, 0x68, 0x53, 0x36, 0xb8, 0x9b, 0xea, 0xcb, 0xef, 0xe6, 0x2c,
|
||||
0x17, 0x8b, 0xb8, 0x5c, 0x81, 0xe0, 0x56, 0xb5, 0x74, 0x00, 0x22, 0xb8, 0x51, 0xd0, 0x8e, 0x5a,
|
||||
0xdd, 0xcc, 0xb3, 0xcb, 0x7d, 0x5e, 0xf2, 0xa5, 0x48, 0x73, 0x06, 0xbf, 0x9d, 0x62, 0x1a, 0xd4,
|
||||
0x65, 0x88, 0x51, 0x4b, 0xb1, 0x36, 0xcb, 0x95, 0x44, 0x73, 0x9d, 0x51, 0x7e, 0x6c, 0xbe, 0x12,
|
||||
0xbc, 0x84, 0x8f, 0x33, 0x1b, 0x2b, 0x10, 0x22, 0xb2, 0x5c, 0x12, 0x06, 0x7d, 0x7f, 0x9c, 0xe6,
|
||||
0x73, 0xb4, 0xef, 0x8f, 0xdd, 0xef, 0x17, 0xdf, 0xa2, 0x01, 0x3b, 0xa0, 0x9a, 0x46, 0x6b, 0x06,
|
||||
0x80, 0x7a, 0x69, 0x18, 0x6d, 0x74, 0x97, 0x20, 0x06, 0x14, 0x4e, 0x02, 0x57, 0x5f, 0x14, 0x2c,
|
||||
0x67, 0x89, 0xbe, 0xb4, 0x87, 0xb9, 0xf2, 0x88, 0xa0, 0x2b, 0x48, 0xda, 0xb9, 0x48, 0xca, 0x4f,
|
||||
0x96, 0xf9, 0x71, 0xc9, 0xcf, 0xd3, 0x8c, 0x95, 0x60, 0x2e, 0x6a, 0xd4, 0x1d, 0x39, 0x31, 0x17,
|
||||
0x61, 0x9c, 0xbd, 0xfd, 0x21, 0xa5, 0xde, 0x2f, 0x26, 0x4c, 0xcb, 0x78, 0x06, 0x6f, 0x7f, 0x34,
|
||||
0x36, 0xda, 0x18, 0x71, 0x32, 0x18, 0xc0, 0x9d, 0x44, 0xa7, 0x71, 0x9d, 0xaf, 0x64, 0x7c, 0xa8,
|
||||
0x97, 0x56, 0xe5, 0x57, 0x7d, 0x2b, 0x90, 0xe8, 0x28, 0x73, 0x18, 0x49, 0x24, 0x3a, 0x61, 0x0d,
|
||||
0xbb, 0x94, 0x48, 0xee, 0x48, 0xdd, 0x6a, 0x02, 0x4b, 0x49, 0x63, 0x43, 0x0b, 0x89, 0xa5, 0xa4,
|
||||
0x05, 0x81, 0x09, 0x49, 0x0f, 0x83, 0x39, 0x3a, 0x21, 0x19, 0x69, 0x70, 0x42, 0x72, 0x29, 0x3b,
|
||||
0x51, 0x8c, 0xf3, 0x54, 0xa4, 0x71, 0x36, 0x61, 0xe2, 0x38, 0x2e, 0xe3, 0x05, 0x13, 0xac, 0x84,
|
||||
0x13, 0x85, 0x42, 0x86, 0x1e, 0x43, 0x4c, 0x14, 0x14, 0xab, 0x1c, 0xfe, 0x6e, 0xf4, 0x76, 0xbd,
|
||||
0xee, 0xb3, 0x5c, 0xfd, 0xd6, 0xd3, 0x73, 0xf9, 0x23, 0x71, 0x83, 0x77, 0x8c, 0x8d, 0x89, 0x28,
|
||||
0x59, 0xbc, 0xd0, 0xb6, 0xdf, 0x32, 0x7f, 0x97, 0xe0, 0xe3, 0xb5, 0x3a, 0x9e, 0x8f, 0xb8, 0x48,
|
||||
0xcf, 0xeb, 0x6d, 0xb6, 0x7a, 0x81, 0x09, 0xc4, 0xb3, 0x2b, 0x1e, 0x06, 0x3e, 0x7a, 0x82, 0x71,
|
||||
0x76, 0x9e, 0x76, 0xa5, 0x27, 0xac, 0xc8, 0xe0, 0x3c, 0xed, 0x69, 0x4b, 0x80, 0x98, 0xa7, 0x51,
|
||||
0xd0, 0x0e, 0x4e, 0x57, 0x3c, 0x65, 0xe1, 0xca, 0x4c, 0x59, 0xbf, 0xca, 0x4c, 0xbd, 0x77, 0x42,
|
||||
0xb2, 0xe8, 0xed, 0x43, 0xb6, 0x38, 0x63, 0x65, 0x75, 0x91, 0x16, 0xd4, 0x97, 0x87, 0x2d, 0xd1,
|
||||
0xf9, 0xe5, 0x61, 0x02, 0xb5, 0x2b, 0x81, 0x05, 0xc6, 0xd5, 0x51, 0xbc, 0x60, 0xf2, 0x13, 0x2e,
|
||||
0x60, 0x25, 0x70, 0x8c, 0x38, 0x10, 0xb1, 0x12, 0x90, 0xb0, 0xf3, 0x7a, 0x99, 0x65, 0x4e, 0xd8,
|
||||
0xbc, 0x8e, 0xb0, 0xf2, 0x38, 0x5e, 0x2d, 0x58, 0x2e, 0x94, 0x49, 0x70, 0x26, 0xef, 0x98, 0xc4,
|
||||
0x79, 0xe2, 0x4c, 0xbe, 0x8f, 0x9e, 0x33, 0x35, 0x79, 0x0d, 0x7f, 0xcc, 0x4b, 0xd1, 0xfc, 0x92,
|
||||
0xdb, 0x69, 0x99, 0x81, 0xa9, 0xc9, 0x6f, 0x54, 0x8f, 0x24, 0xa6, 0xa6, 0xb0, 0x86, 0xf3, 0xab,
|
||||
0x1d, 0x5e, 0x19, 0x5e, 0xb0, 0xd2, 0xc4, 0xc9, 0xf3, 0x45, 0x9c, 0x66, 0x2a, 0x1a, 0xbe, 0x1f,
|
||||
0xb0, 0x4d, 0xe8, 0x10, 0xbf, 0xda, 0xd1, 0x57, 0xd7, 0xf9, 0x9d, 0x93, 0x70, 0x09, 0xc1, 0x23,
|
||||
0x82, 0x0e, 0xfb, 0xc4, 0x23, 0x82, 0x6e, 0x2d, 0xbb, 0x73, 0xb7, 0xac, 0xe4, 0x56, 0x92, 0xd8,
|
||||
0xe1, 0x09, 0x3c, 0x2f, 0x74, 0x6c, 0x02, 0x90, 0xd8, 0xb9, 0x07, 0x15, 0x6c, 0x6a, 0x60, 0xb1,
|
||||
0xbd, 0x34, 0x8f, 0xb3, 0xf4, 0xc7, 0x30, 0xad, 0x77, 0xec, 0x68, 0x82, 0x48, 0x0d, 0x70, 0x12,
|
||||
0x73, 0xb5, 0xcf, 0xc4, 0x34, 0xad, 0xa7, 0xfe, 0xfb, 0x81, 0x76, 0x93, 0x44, 0xb7, 0x2b, 0x87,
|
||||
0x74, 0xbe, 0x32, 0x0c, 0x9b, 0x75, 0x54, 0x14, 0x93, 0x7a, 0x55, 0x3d, 0x61, 0x33, 0x96, 0x16,
|
||||
0x62, 0xf0, 0x51, 0xb8, 0xad, 0x00, 0x4e, 0x5c, 0xb4, 0xe8, 0xa1, 0xe6, 0x3c, 0xbe, 0xaf, 0xe7,
|
||||
0x92, 0x49, 0xf3, 0x13, 0xa7, 0xa7, 0x15, 0x2b, 0x55, 0xa2, 0xb1, 0xcf, 0x04, 0x18, 0x9d, 0x0e,
|
||||
0x37, 0x74, 0xc0, 0xba, 0xa2, 0xc4, 0xe8, 0x0c, 0x6b, 0xd8, 0xc3, 0x3e, 0x87, 0x53, 0x5f, 0x8d,
|
||||
0x97, 0xd7, 0x1d, 0x1f, 0x91, 0xc6, 0x1c, 0x8a, 0x38, 0xec, 0xa3, 0x69, 0x9b, 0xad, 0xb5, 0xdd,
|
||||
0x8e, 0xf2, 0xd5, 0x18, 0x5e, 0x99, 0x40, 0x2c, 0x49, 0x8c, 0xc8, 0xd6, 0x02, 0xb8, 0x73, 0x18,
|
||||
0x5e, 0xf2, 0x38, 0x99, 0xc5, 0x95, 0x38, 0x8e, 0x57, 0x19, 0x8f, 0x13, 0xb9, 0xae, 0xc3, 0xc3,
|
||||
0x70, 0xcd, 0x0c, 0x5d, 0x88, 0x3a, 0x0c, 0xa7, 0x60, 0x37, 0x3b, 0x93, 0xbf, 0xdc, 0xaa, 0xae,
|
||||
0x92, 0xc2, 0xec, 0x4c, 0x96, 0x17, 0x5e, 0x23, 0xbd, 0x1b, 0x86, 0xec, 0x2b, 0x70, 0x8d, 0x48,
|
||||
0xa6, 0x21, 0xb7, 0x30, 0x1d, 0x2f, 0x01, 0x79, 0x3f, 0x40, 0xd8, 0x0f, 0xa0, 0x34, 0x7f, 0xd7,
|
||||
0x3f, 0xd6, 0x25, 0xd4, 0xb7, 0xd8, 0x1f, 0x61, 0xba, 0x2e, 0xe4, 0xdd, 0x50, 0xdb, 0xea, 0x49,
|
||||
0xdb, 0x34, 0x73, 0xe7, 0x22, 0x16, 0xa3, 0x24, 0x39, 0x64, 0x15, 0xf2, 0x3e, 0x7b, 0x2d, 0x1c,
|
||||
0x5a, 0x29, 0x91, 0x66, 0xb6, 0x29, 0x1b, 0xe8, 0xb5, 0xec, 0x79, 0x92, 0x0a, 0x25, 0xd3, 0x17,
|
||||
0xb4, 0x1f, 0xb5, 0x0d, 0xb4, 0x29, 0xa2, 0x56, 0x34, 0x6d, 0xe7, 0xf2, 0x9a, 0x99, 0xf2, 0xf9,
|
||||
0x3c, 0x63, 0x0a, 0x3a, 0x61, 0x71, 0xf3, 0x29, 0xca, 0xed, 0xb6, 0x2d, 0x14, 0x24, 0xe6, 0xf2,
|
||||
0xa0, 0x82, 0x4d, 0x23, 0x6b, 0xac, 0x79, 0x24, 0xa5, 0x1b, 0x76, 0xa3, 0x6d, 0xc6, 0x03, 0x88,
|
||||
0x34, 0x12, 0x05, 0xed, 0x6b, 0x77, 0xb5, 0x78, 0x9f, 0xe9, 0x96, 0x80, 0xdf, 0xba, 0x92, 0xca,
|
||||
0x8e, 0x98, 0x78, 0xed, 0x0e, 0xc1, 0xec, 0x3e, 0x01, 0x78, 0x78, 0xb6, 0x1a, 0x27, 0x70, 0x9f,
|
||||
0x00, 0xf5, 0x25, 0x43, 0xec, 0x13, 0x28, 0xd6, 0xef, 0x3a, 0x73, 0xee, 0x75, 0x10, 0x57, 0xb6,
|
||||
0x72, 0x48, 0xd7, 0xa1, 0x60, 0xa8, 0xeb, 0x28, 0x05, 0xbf, 0x49, 0xdd, 0xa3, 0x35, 0xa4, 0x49,
|
||||
0xb1, 0x73, 0xb5, 0xf5, 0x2e, 0xcc, 0xe6, 0xfe, 0xb5, 0xf0, 0x84, 0xc5, 0x89, 0xa9, 0x18, 0xa2,
|
||||
0xeb, 0xca, 0x89, 0xdc, 0x1f, 0xe3, 0x94, 0x93, 0x3f, 0x88, 0x06, 0x4d, 0x35, 0x4a, 0xd7, 0xcd,
|
||||
0x2d, 0xac, 0x88, 0x35, 0x41, 0x4c, 0x54, 0x3e, 0xe1, 0x24, 0x6e, 0x5e, 0x17, 0x4d, 0xb9, 0x72,
|
||||
0xa0, 0x5e, 0x0b, 0xad, 0x40, 0xe2, 0xe6, 0x37, 0x7b, 0x8b, 0x26, 0x12, 0xb7, 0x6e, 0x2d, 0xe7,
|
||||
0xb3, 0x3f, 0xa0, 0xcb, 0xf6, 0x4a, 0xbe, 0x80, 0x65, 0xfa, 0x34, 0xd8, 0x3d, 0x88, 0x06, 0xf1,
|
||||
0xd9, 0x9f, 0x7e, 0x9a, 0x76, 0x0d, 0x32, 0x67, 0x07, 0xf2, 0x7a, 0x1a, 0xfe, 0x7b, 0x23, 0x8d,
|
||||
0x90, 0x58, 0x83, 0x5a, 0x90, 0xf3, 0x23, 0xa5, 0xe3, 0x97, 0x65, 0x2a, 0xd2, 0x7c, 0x3e, 0xe5,
|
||||
0x3c, 0x83, 0x47, 0x96, 0xa3, 0xf1, 0xd0, 0x95, 0x52, 0x3f, 0x52, 0xda, 0xa2, 0xec, 0x12, 0x37,
|
||||
0x1a, 0x8f, 0x96, 0x82, 0x9f, 0xa7, 0x59, 0x06, 0x22, 0x67, 0x34, 0x1e, 0x6a, 0x09, 0x11, 0x39,
|
||||
0x3e, 0xe1, 0xfc, 0xb4, 0xe6, 0x58, 0x9e, 0xfe, 0xab, 0x13, 0xd0, 0x3b, 0x50, 0xc7, 0x11, 0x52,
|
||||
0x3f, 0xad, 0x09, 0x21, 0xe7, 0xa7, 0x42, 0xc7, 0xd8, 0x8f, 0xa6, 0x6c, 0x42, 0x75, 0x04, 0xa2,
|
||||
0x7e, 0x2a, 0x94, 0x82, 0x1b, 0x9f, 0xcf, 0xde, 0xff, 0x9f, 0xaf, 0x6e, 0xac, 0xfd, 0xf4, 0xab,
|
||||
0x1b, 0x6b, 0xff, 0xfb, 0xd5, 0x8d, 0xb5, 0x9f, 0x7c, 0x7d, 0xe3, 0x8d, 0x9f, 0x7e, 0x7d, 0xe3,
|
||||
0x8d, 0x9f, 0x7d, 0x7d, 0xe3, 0x8d, 0x2f, 0xdf, 0x54, 0xbf, 0x9f, 0x7f, 0xf6, 0x0b, 0xf2, 0x57,
|
||||
0xf0, 0x9f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x64, 0x71, 0xe6, 0xb7, 0x63, 0x7f, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
|
@ -505,6 +504,7 @@ type ClientCommandsHandler interface {
|
|||
ObjectUndo(context.Context, *pb.RpcObjectUndoRequest) *pb.RpcObjectUndoResponse
|
||||
ObjectRedo(context.Context, *pb.RpcObjectRedoRequest) *pb.RpcObjectRedoResponse
|
||||
ObjectListExport(context.Context, *pb.RpcObjectListExportRequest) *pb.RpcObjectListExportResponse
|
||||
ObjectExport(context.Context, *pb.RpcObjectExportRequest) *pb.RpcObjectExportResponse
|
||||
ObjectBookmarkFetch(context.Context, *pb.RpcObjectBookmarkFetchRequest) *pb.RpcObjectBookmarkFetchResponse
|
||||
ObjectToBookmark(context.Context, *pb.RpcObjectToBookmarkRequest) *pb.RpcObjectToBookmarkResponse
|
||||
ObjectImport(context.Context, *pb.RpcObjectImportRequest) *pb.RpcObjectImportResponse
|
||||
|
@ -749,8 +749,6 @@ type ClientCommandsHandler interface {
|
|||
AIAutofill(context.Context, *pb.RpcAIAutofillRequest) *pb.RpcAIAutofillResponse
|
||||
AIListSummary(context.Context, *pb.RpcAIListSummaryRequest) *pb.RpcAIListSummaryResponse
|
||||
AIObjectCreateFromUrl(context.Context, *pb.RpcAIObjectCreateFromUrlRequest) *pb.RpcAIObjectCreateFromUrlResponse
|
||||
// Push
|
||||
PushNotificationRegisterToken(context.Context, *pb.RpcPushNotificationRegisterTokenRequest) *pb.RpcPushNotificationRegisterTokenResponse
|
||||
}
|
||||
|
||||
func registerClientCommandsHandler(srv ClientCommandsHandler) {
|
||||
|
@ -2657,6 +2655,26 @@ func ObjectListExport(b []byte) (resp []byte) {
|
|||
return resp
|
||||
}
|
||||
|
||||
func ObjectExport(b []byte) (resp []byte) {
|
||||
defer func() {
|
||||
if PanicHandler != nil {
|
||||
if r := recover(); r != nil {
|
||||
resp, _ = (&pb.RpcObjectExportResponse{Error: &pb.RpcObjectExportResponseError{Code: pb.RpcObjectExportResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
|
||||
PanicHandler(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
in := new(pb.RpcObjectExportRequest)
|
||||
if err := in.Unmarshal(b); err != nil {
|
||||
resp, _ = (&pb.RpcObjectExportResponse{Error: &pb.RpcObjectExportResponseError{Code: pb.RpcObjectExportResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
|
||||
return resp
|
||||
}
|
||||
|
||||
resp, _ = clientCommandsHandler.ObjectExport(context.Background(), in).Marshal()
|
||||
return resp
|
||||
}
|
||||
|
||||
func ObjectBookmarkFetch(b []byte) (resp []byte) {
|
||||
defer func() {
|
||||
if PanicHandler != nil {
|
||||
|
@ -6557,26 +6575,6 @@ func AIObjectCreateFromUrl(b []byte) (resp []byte) {
|
|||
return resp
|
||||
}
|
||||
|
||||
func PushNotificationRegisterToken(b []byte) (resp []byte) {
|
||||
defer func() {
|
||||
if PanicHandler != nil {
|
||||
if r := recover(); r != nil {
|
||||
resp, _ = (&pb.RpcPushNotificationRegisterTokenResponse{Error: &pb.RpcPushNotificationRegisterTokenResponseError{Code: pb.RpcPushNotificationRegisterTokenResponseError_UNKNOWN_ERROR, Description: "panic recovered"}}).Marshal()
|
||||
PanicHandler(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
in := new(pb.RpcPushNotificationRegisterTokenRequest)
|
||||
if err := in.Unmarshal(b); err != nil {
|
||||
resp, _ = (&pb.RpcPushNotificationRegisterTokenResponse{Error: &pb.RpcPushNotificationRegisterTokenResponseError{Code: pb.RpcPushNotificationRegisterTokenResponseError_BAD_INPUT, Description: err.Error()}}).Marshal()
|
||||
return resp
|
||||
}
|
||||
|
||||
resp, _ = clientCommandsHandler.PushNotificationRegisterToken(context.Background(), in).Marshal()
|
||||
return resp
|
||||
}
|
||||
|
||||
var PanicHandler func(v interface{})
|
||||
|
||||
func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
|
||||
|
@ -6773,6 +6771,8 @@ func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
|
|||
cd = ObjectRedo(data)
|
||||
case "ObjectListExport":
|
||||
cd = ObjectListExport(data)
|
||||
case "ObjectExport":
|
||||
cd = ObjectExport(data)
|
||||
case "ObjectBookmarkFetch":
|
||||
cd = ObjectBookmarkFetch(data)
|
||||
case "ObjectToBookmark":
|
||||
|
@ -7163,8 +7163,6 @@ func CommandAsync(cmd string, data []byte, callback func(data []byte)) {
|
|||
cd = AIListSummary(data)
|
||||
case "AIObjectCreateFromUrl":
|
||||
cd = AIObjectCreateFromUrl(data)
|
||||
case "PushNotificationRegisterToken":
|
||||
cd = PushNotificationRegisterToken(data)
|
||||
default:
|
||||
log.Errorf("unknown command type: %s\n", cmd)
|
||||
}
|
||||
|
@ -8517,6 +8515,20 @@ func (h *ClientCommandsHandlerProxy) ObjectListExport(ctx context.Context, req *
|
|||
call, _ := actualCall(ctx, req)
|
||||
return call.(*pb.RpcObjectListExportResponse)
|
||||
}
|
||||
func (h *ClientCommandsHandlerProxy) ObjectExport(ctx context.Context, req *pb.RpcObjectExportRequest) *pb.RpcObjectExportResponse {
|
||||
actualCall := func(ctx context.Context, req any) (any, error) {
|
||||
return h.client.ObjectExport(ctx, req.(*pb.RpcObjectExportRequest)), nil
|
||||
}
|
||||
for _, interceptor := range h.interceptors {
|
||||
toCall := actualCall
|
||||
currentInterceptor := interceptor
|
||||
actualCall = func(ctx context.Context, req any) (any, error) {
|
||||
return currentInterceptor(ctx, req, "ObjectExport", toCall)
|
||||
}
|
||||
}
|
||||
call, _ := actualCall(ctx, req)
|
||||
return call.(*pb.RpcObjectExportResponse)
|
||||
}
|
||||
func (h *ClientCommandsHandlerProxy) ObjectBookmarkFetch(ctx context.Context, req *pb.RpcObjectBookmarkFetchRequest) *pb.RpcObjectBookmarkFetchResponse {
|
||||
actualCall := func(ctx context.Context, req any) (any, error) {
|
||||
return h.client.ObjectBookmarkFetch(ctx, req.(*pb.RpcObjectBookmarkFetchRequest)), nil
|
||||
|
@ -11247,17 +11259,3 @@ func (h *ClientCommandsHandlerProxy) AIObjectCreateFromUrl(ctx context.Context,
|
|||
call, _ := actualCall(ctx, req)
|
||||
return call.(*pb.RpcAIObjectCreateFromUrlResponse)
|
||||
}
|
||||
func (h *ClientCommandsHandlerProxy) PushNotificationRegisterToken(ctx context.Context, req *pb.RpcPushNotificationRegisterTokenRequest) *pb.RpcPushNotificationRegisterTokenResponse {
|
||||
actualCall := func(ctx context.Context, req any) (any, error) {
|
||||
return h.client.PushNotificationRegisterToken(ctx, req.(*pb.RpcPushNotificationRegisterTokenRequest)), nil
|
||||
}
|
||||
for _, interceptor := range h.interceptors {
|
||||
toCall := actualCall
|
||||
currentInterceptor := interceptor
|
||||
actualCall = func(ctx context.Context, req any) (any, error) {
|
||||
return currentInterceptor(ctx, req, "PushNotificationRegisterToken", toCall)
|
||||
}
|
||||
}
|
||||
call, _ := actualCall(ctx, req)
|
||||
return call.(*pb.RpcPushNotificationRegisterTokenResponse)
|
||||
}
|
||||
|
|
53
core/api/apicore/apicore.go
Normal file
53
core/api/apicore/apicore.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package apicore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
type AccountService interface {
|
||||
GetInfo(ctx context.Context) (*model.AccountInfo, error)
|
||||
}
|
||||
|
||||
type ExportService interface {
|
||||
ExportSingleInMemory(ctx context.Context, spaceId string, objectId string, format model.ExportFormat) (res string, err error)
|
||||
}
|
||||
|
||||
type ClientCommands interface {
|
||||
// Wallet
|
||||
AccountLocalLinkNewChallenge(context.Context, *pb.RpcAccountLocalLinkNewChallengeRequest) *pb.RpcAccountLocalLinkNewChallengeResponse
|
||||
AccountLocalLinkSolveChallenge(context.Context, *pb.RpcAccountLocalLinkSolveChallengeRequest) *pb.RpcAccountLocalLinkSolveChallengeResponse
|
||||
WalletCreateSession(context.Context, *pb.RpcWalletCreateSessionRequest) *pb.RpcWalletCreateSessionResponse
|
||||
|
||||
// Space
|
||||
WorkspaceCreate(context.Context, *pb.RpcWorkspaceCreateRequest) *pb.RpcWorkspaceCreateResponse
|
||||
WorkspaceOpen(context.Context, *pb.RpcWorkspaceOpenRequest) *pb.RpcWorkspaceOpenResponse
|
||||
WorkspaceSetInfo(context.Context, *pb.RpcWorkspaceSetInfoRequest) *pb.RpcWorkspaceSetInfoResponse
|
||||
SpaceRequestApprove(context.Context, *pb.RpcSpaceRequestApproveRequest) *pb.RpcSpaceRequestApproveResponse
|
||||
SpaceRequestDecline(context.Context, *pb.RpcSpaceRequestDeclineRequest) *pb.RpcSpaceRequestDeclineResponse
|
||||
SpaceParticipantRemove(context.Context, *pb.RpcSpaceParticipantRemoveRequest) *pb.RpcSpaceParticipantRemoveResponse
|
||||
SpaceParticipantPermissionsChange(context.Context, *pb.RpcSpaceParticipantPermissionsChangeRequest) *pb.RpcSpaceParticipantPermissionsChangeResponse
|
||||
|
||||
// Object
|
||||
ObjectShow(context.Context, *pb.RpcObjectShowRequest) *pb.RpcObjectShowResponse
|
||||
ObjectCreate(context.Context, *pb.RpcObjectCreateRequest) *pb.RpcObjectCreateResponse
|
||||
ObjectCreateBookmark(context.Context, *pb.RpcObjectCreateBookmarkRequest) *pb.RpcObjectCreateBookmarkResponse
|
||||
ObjectSearch(context.Context, *pb.RpcObjectSearchRequest) *pb.RpcObjectSearchResponse
|
||||
ObjectSearchSubscribe(context.Context, *pb.RpcObjectSearchSubscribeRequest) *pb.RpcObjectSearchSubscribeResponse
|
||||
ObjectSetDetails(context.Context, *pb.RpcObjectSetDetailsRequest) *pb.RpcObjectSetDetailsResponse
|
||||
ObjectSetIsArchived(context.Context, *pb.RpcObjectSetIsArchivedRequest) *pb.RpcObjectSetIsArchivedResponse
|
||||
ObjectListExport(context.Context, *pb.RpcObjectListExportRequest) *pb.RpcObjectListExportResponse
|
||||
|
||||
// List
|
||||
ObjectCollectionAdd(context.Context, *pb.RpcObjectCollectionAddRequest) *pb.RpcObjectCollectionAddResponse
|
||||
ObjectCollectionRemove(context.Context, *pb.RpcObjectCollectionRemoveRequest) *pb.RpcObjectCollectionRemoveResponse
|
||||
|
||||
// Property
|
||||
ObjectRelationAddFeatured(context.Context, *pb.RpcObjectRelationAddFeaturedRequest) *pb.RpcObjectRelationAddFeaturedResponse
|
||||
|
||||
// Block
|
||||
BlockCreate(context.Context, *pb.RpcBlockCreateRequest) *pb.RpcBlockCreateResponse
|
||||
BlockPaste(context.Context, *pb.RpcBlockPasteRequest) *pb.RpcBlockPasteResponse
|
||||
}
|
95
core/api/apicore/mock_apicore/mock_AccountService.go
Normal file
95
core/api/apicore/mock_apicore/mock_AccountService.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Code generated by mockery. DO NOT EDIT.
|
||||
|
||||
package mock_apicore
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
model "github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockAccountService is an autogenerated mock type for the AccountService type
|
||||
type MockAccountService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockAccountService_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockAccountService) EXPECT() *MockAccountService_Expecter {
|
||||
return &MockAccountService_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetInfo provides a mock function with given fields: ctx
|
||||
func (_m *MockAccountService) GetInfo(ctx context.Context) (*model.AccountInfo, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetInfo")
|
||||
}
|
||||
|
||||
var r0 *model.AccountInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) (*model.AccountInfo, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) *model.AccountInfo); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.AccountInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockAccountService_GetInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInfo'
|
||||
type MockAccountService_GetInfo_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetInfo is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
func (_e *MockAccountService_Expecter) GetInfo(ctx interface{}) *MockAccountService_GetInfo_Call {
|
||||
return &MockAccountService_GetInfo_Call{Call: _e.mock.On("GetInfo", ctx)}
|
||||
}
|
||||
|
||||
func (_c *MockAccountService_GetInfo_Call) Run(run func(ctx context.Context)) *MockAccountService_GetInfo_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAccountService_GetInfo_Call) Return(_a0 *model.AccountInfo, _a1 error) *MockAccountService_GetInfo_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockAccountService_GetInfo_Call) RunAndReturn(run func(context.Context) (*model.AccountInfo, error)) *MockAccountService_GetInfo_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockAccountService creates a new instance of MockAccountService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockAccountService(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockAccountService {
|
||||
mock := &MockAccountService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
1164
core/api/apicore/mock_apicore/mock_ClientCommands.go
Normal file
1164
core/api/apicore/mock_apicore/mock_ClientCommands.go
Normal file
File diff suppressed because it is too large
Load diff
96
core/api/apicore/mock_apicore/mock_ExportService.go
Normal file
96
core/api/apicore/mock_apicore/mock_ExportService.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Code generated by mockery. DO NOT EDIT.
|
||||
|
||||
package mock_apicore
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
model "github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockExportService is an autogenerated mock type for the ExportService type
|
||||
type MockExportService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockExportService_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockExportService) EXPECT() *MockExportService_Expecter {
|
||||
return &MockExportService_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// ExportSingleInMemory provides a mock function with given fields: ctx, spaceId, objectId, format
|
||||
func (_m *MockExportService) ExportSingleInMemory(ctx context.Context, spaceId string, objectId string, format model.ExportFormat) (string, error) {
|
||||
ret := _m.Called(ctx, spaceId, objectId, format)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ExportSingleInMemory")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.ExportFormat) (string, error)); ok {
|
||||
return rf(ctx, spaceId, objectId, format)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.ExportFormat) string); ok {
|
||||
r0 = rf(ctx, spaceId, objectId, format)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, model.ExportFormat) error); ok {
|
||||
r1 = rf(ctx, spaceId, objectId, format)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockExportService_ExportSingleInMemory_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExportSingleInMemory'
|
||||
type MockExportService_ExportSingleInMemory_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ExportSingleInMemory is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - spaceId string
|
||||
// - objectId string
|
||||
// - format model.ExportFormat
|
||||
func (_e *MockExportService_Expecter) ExportSingleInMemory(ctx interface{}, spaceId interface{}, objectId interface{}, format interface{}) *MockExportService_ExportSingleInMemory_Call {
|
||||
return &MockExportService_ExportSingleInMemory_Call{Call: _e.mock.On("ExportSingleInMemory", ctx, spaceId, objectId, format)}
|
||||
}
|
||||
|
||||
func (_c *MockExportService_ExportSingleInMemory_Call) Run(run func(ctx context.Context, spaceId string, objectId string, format model.ExportFormat)) *MockExportService_ExportSingleInMemory_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(model.ExportFormat))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockExportService_ExportSingleInMemory_Call) Return(res string, err error) *MockExportService_ExportSingleInMemory_Call {
|
||||
_c.Call.Return(res, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockExportService_ExportSingleInMemory_Call) RunAndReturn(run func(context.Context, string, string, model.ExportFormat) (string, error)) *MockExportService_ExportSingleInMemory_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockExportService creates a new instance of MockExportService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockExportService(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockExportService {
|
||||
mock := &MockExportService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -10,15 +10,16 @@ import (
|
|||
|
||||
// DisplayCodeHandler starts a new challenge and returns the challenge ID
|
||||
//
|
||||
// @Summary Start new challenge
|
||||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param app_name query string true "App name requesting the challenge"
|
||||
// @Success 200 {object} DisplayCodeResponse "Challenge ID"
|
||||
// @Failure 400 {object} util.ValidationError "Invalid input"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /auth/display_code [post]
|
||||
// @Summary Start new challenge
|
||||
// @Description This endpoint initiates a secure authentication flow by generating a new challenge. Clients must supply the name of the application (via a query parameter) that is requesting authentication. On success, the service returns a unique challenge ID. This challenge ID must then be used with the token endpoint (see below) to solve the challenge and retrieve an authentication token. In essence, this endpoint “boots up” the login process and is the first step in a multi-phase authentication sequence.
|
||||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param app_name query string true "App name requesting the challenge"
|
||||
// @Success 200 {object} DisplayCodeResponse "Challenge ID"
|
||||
// @Failure 400 {object} util.ValidationError "Invalid input"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /auth/display_code [post]
|
||||
func DisplayCodeHandler(s *AuthService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
appName := c.Query("app_name")
|
||||
|
@ -40,16 +41,17 @@ func DisplayCodeHandler(s *AuthService) gin.HandlerFunc {
|
|||
|
||||
// TokenHandler retrieves an authentication token using a code and challenge ID
|
||||
//
|
||||
// @Summary Retrieve token
|
||||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param challenge_id query string true "Challenge ID"
|
||||
// @Param code query string true "4-digit code retrieved from Anytype Desktop app"
|
||||
// @Success 200 {object} TokenResponse "Authentication token"
|
||||
// @Failure 400 {object} util.ValidationError "Invalid input"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /auth/token [post]
|
||||
// @Summary Retrieve token
|
||||
// @Description After receiving a challenge ID from the display_code endpoint, the client calls this endpoint to provide the corresponding 4-digit code (also via a query parameter) along with the challenge ID. The endpoint verifies that the challenge solution is correct and, if it is, returns an ephemeral session token together with a permanent app key. These tokens are then used in subsequent API requests to authorize access. This endpoint is central to ensuring that only properly authenticated sessions can access further resources.
|
||||
// @Tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param challenge_id query string true "Challenge ID"
|
||||
// @Param code query string true "4-digit code retrieved from Anytype Desktop app"
|
||||
// @Success 200 {object} TokenResponse "Authentication token"
|
||||
// @Failure 400 {object} util.ValidationError "Invalid input"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /auth/token [post]
|
||||
func TokenHandler(s *AuthService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
challengeId := c.Query("challenge_id")
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package auth
|
||||
|
||||
type DisplayCodeResponse struct {
|
||||
ChallengeId string `json:"challenge_id" example:"67647f5ecda913e9a2e11b26"`
|
||||
ChallengeId string `json:"challenge_id" example:"67647f5ecda913e9a2e11b26"` // The challenge id associated with the displayed code and needed to solve the challenge for token
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
SessionToken string `json:"session_token" example:"eyJhbGciOeJIRzI1NiIsInR5cCI6IkpXVCJ1.eyJzZWVkIjaiY0dmVndlUnAifQ.Y1EZecYnwmvMkrXKOa2XJnAbaRt34urBabe06tmDQII"`
|
||||
AppKey string `json:"app_key" example:"zhSG/zQRmgADyilWPtgdnfo1qD60oK02/SVgi1GaFt6="`
|
||||
SessionToken string `json:"session_token" example:"eyJhbGciOeJIRzI1NiIsInR5cCI6IkpXVCJ1.eyJzZWVkIjaiY0dmVndlUnAifQ.Y1EZecYnwmvMkrXKOa2XJnAbaRt34urBabe06tmDQII"` // The ephemeral session token
|
||||
AppKey string `json:"app_key" example:"zhSG/zQRmgADyilWPtgdnfo1qD60oK02/SVgi1GaFt6="` // The permanent app key
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
|
@ -22,10 +22,10 @@ type Service interface {
|
|||
}
|
||||
|
||||
type AuthService struct {
|
||||
mw service.ClientCommandsServer
|
||||
mw apicore.ClientCommands
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer) *AuthService {
|
||||
func NewService(mw apicore.ClientCommands) *AuthService {
|
||||
return &AuthService{mw: mw}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service/mock_service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
|
@ -22,16 +22,16 @@ const (
|
|||
|
||||
type fixture struct {
|
||||
*AuthService
|
||||
mwMock *mock_service.MockClientCommandsServer
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mw := mock_service.NewMockClientCommandsServer(t)
|
||||
authService := NewService(mw)
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
authService := NewService(mwMock)
|
||||
|
||||
return &fixture{
|
||||
AuthService: authService,
|
||||
mwMock: mw,
|
||||
mwMock: mwMock,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,33 +10,29 @@ import (
|
|||
|
||||
// GetObjectExportHandler exports an object in specified format
|
||||
//
|
||||
// @Summary Export object
|
||||
// @Tags export
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Param format path string true "Export format" Enums(markdown,protobuf)
|
||||
// @Success 200 {object} ObjectExportResponse "Object exported successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/objects/{object_id}/export/{format} [post]
|
||||
// @Summary Export object
|
||||
// @Description This endpoint exports a single object from the specified space into a desired format. The export format is provided as a path parameter (currently supporting “markdown” only). The endpoint calls the export service which converts the object’s content into the requested format. It is useful for sharing, or displaying the markdown representation of the objecte externally.
|
||||
// @Tags export
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Param format path string true "Export format" Enums(markdown)
|
||||
// @Success 200 {object} ObjectExportResponse "Object exported successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/objects/{object_id}/{format} [get]
|
||||
func GetObjectExportHandler(s *ExportService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
objectId := c.Param("object_id")
|
||||
format := c.Query("format")
|
||||
format := c.Param("format")
|
||||
|
||||
objectAsRequest := ObjectExportRequest{}
|
||||
if err := c.ShouldBindJSON(&objectAsRequest); err != nil {
|
||||
apiErr := util.CodeToAPIError(http.StatusBadRequest, ErrBadInput.Error())
|
||||
c.JSON(http.StatusBadRequest, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
outputPath, err := s.GetObjectExport(c.Request.Context(), spaceId, objectId, format, objectAsRequest.Path)
|
||||
code := util.MapErrorCode(err, util.ErrToCode(ErrFailedExportObjectAsMarkdown, http.StatusInternalServerError))
|
||||
markdown, err := s.GetObjectExport(c.Request.Context(), spaceId, objectId, format)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrInvalidExportFormat, http.StatusInternalServerError))
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
|
@ -44,6 +40,6 @@ func GetObjectExportHandler(s *ExportService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ObjectExportResponse{Path: outputPath})
|
||||
c.JSON(http.StatusOK, ObjectExportResponse{Markdown: markdown})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package export
|
||||
|
||||
type ObjectExportRequest struct {
|
||||
Path string `json:"path" example:"/path/to/export"`
|
||||
}
|
||||
|
||||
type ObjectExportResponse struct {
|
||||
Path string `json:"path" example:"/path/to/export"`
|
||||
Markdown string `json:"markdown" example:"# This is the title\n..."`
|
||||
}
|
||||
|
|
|
@ -4,14 +4,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedExportObjectAsMarkdown = errors.New("failed to export object as markdown")
|
||||
ErrBadInput = errors.New("bad input")
|
||||
ErrInvalidExportFormat = errors.New("format is not supported")
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
|
@ -19,34 +17,26 @@ type Service interface {
|
|||
}
|
||||
|
||||
type ExportService struct {
|
||||
mw service.ClientCommandsServer
|
||||
AccountInfo *model.AccountInfo
|
||||
mw apicore.ClientCommands
|
||||
exportService apicore.ExportService
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer) *ExportService {
|
||||
return &ExportService{mw: mw}
|
||||
func NewService(mw apicore.ClientCommands, exportService apicore.ExportService) *ExportService {
|
||||
return &ExportService{mw: mw, exportService: exportService}
|
||||
}
|
||||
|
||||
// GetObjectExport retrieves an object from a space and exports it as a specific format.
|
||||
func (s *ExportService) GetObjectExport(ctx context.Context, spaceId string, objectId string, format string, path string) (string, error) {
|
||||
resp := s.mw.ObjectListExport(ctx, &pb.RpcObjectListExportRequest{
|
||||
SpaceId: spaceId,
|
||||
Path: path,
|
||||
ObjectIds: []string{objectId},
|
||||
Format: s.mapStringToFormat(format),
|
||||
Zip: false,
|
||||
IncludeNested: false,
|
||||
IncludeFiles: true,
|
||||
IsJson: false,
|
||||
IncludeArchived: false,
|
||||
NoProgress: true,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectListExportResponseError_NULL {
|
||||
return "", ErrFailedExportObjectAsMarkdown
|
||||
func (s *ExportService) GetObjectExport(ctx context.Context, spaceId string, objectId string, format string) (string, error) {
|
||||
if format != "markdown" {
|
||||
return "", ErrInvalidExportFormat
|
||||
}
|
||||
|
||||
return resp.Path, nil
|
||||
result, err := s.exportService.ExportSingleInMemory(ctx, spaceId, objectId, s.mapStringToFormat(format))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// mapStringToFormat maps a format string to an ExportFormat enum.
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
package export
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service/mock_service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,113 +16,117 @@ const (
|
|||
|
||||
type fixture struct {
|
||||
*ExportService
|
||||
mwMock *mock_service.MockClientCommandsServer
|
||||
exportMock *mock_apicore.MockExportService
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mw := mock_service.NewMockClientCommandsServer(t)
|
||||
exportService := NewService(mw)
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
exportMock := mock_apicore.NewMockExportService(t)
|
||||
exportService := NewService(mwMock, exportMock)
|
||||
|
||||
return &fixture{
|
||||
ExportService: exportService,
|
||||
mwMock: mw,
|
||||
exportMock: exportMock,
|
||||
mwMock: mwMock,
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportService_GetObjectExport(t *testing.T) {
|
||||
t.Run("successful export to markdown", func(t *testing.T) {
|
||||
// Given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Mock the ObjectListExport call
|
||||
fx.mwMock.
|
||||
On("ObjectListExport", mock.Anything, &pb.RpcObjectListExportRequest{
|
||||
SpaceId: spaceID,
|
||||
Path: exportPath,
|
||||
ObjectIds: []string{objectID},
|
||||
Format: model.Export_Markdown,
|
||||
Zip: false,
|
||||
IncludeNested: false,
|
||||
IncludeFiles: true,
|
||||
IsJson: false,
|
||||
IncludeArchived: false,
|
||||
NoProgress: true,
|
||||
}).
|
||||
Return(&pb.RpcObjectListExportResponse{
|
||||
Path: exportPath,
|
||||
Error: &pb.RpcObjectListExportResponseError{
|
||||
Code: pb.RpcObjectListExportResponseError_NULL,
|
||||
},
|
||||
}).
|
||||
Once()
|
||||
|
||||
// When
|
||||
gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, exportFormat, exportPath)
|
||||
|
||||
// Then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, exportPath, gotPath)
|
||||
fx.mwMock.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("failed export returns error", func(t *testing.T) {
|
||||
// Given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Mock the ObjectListExport call to return an error code
|
||||
fx.mwMock.
|
||||
On("ObjectListExport", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectListExportResponse{
|
||||
Path: "",
|
||||
Error: &pb.RpcObjectListExportResponseError{
|
||||
Code: pb.RpcObjectListExportResponseError_UNKNOWN_ERROR,
|
||||
},
|
||||
}).
|
||||
Once()
|
||||
|
||||
// When
|
||||
gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, exportFormat, exportPath)
|
||||
|
||||
// Then
|
||||
require.Error(t, err)
|
||||
require.Empty(t, gotPath)
|
||||
require.ErrorIs(t, err, ErrFailedExportObjectAsMarkdown)
|
||||
fx.mwMock.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("unrecognized format defaults to markdown", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectListExport", mock.Anything, &pb.RpcObjectListExportRequest{
|
||||
SpaceId: spaceID,
|
||||
Path: exportPath,
|
||||
ObjectIds: []string{objectID},
|
||||
Format: model.Export_Markdown, // fallback
|
||||
Zip: false,
|
||||
IncludeNested: false,
|
||||
IncludeFiles: true,
|
||||
IsJson: false,
|
||||
IncludeArchived: false,
|
||||
NoProgress: true,
|
||||
}).
|
||||
Return(&pb.RpcObjectListExportResponse{
|
||||
Path: exportPath,
|
||||
Error: &pb.RpcObjectListExportResponseError{
|
||||
Code: pb.RpcObjectListExportResponseError_NULL,
|
||||
},
|
||||
}).
|
||||
Once()
|
||||
|
||||
// When
|
||||
gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, unrecognizedFormat, exportPath) //
|
||||
|
||||
// Then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, exportPath, gotPath)
|
||||
fx.mwMock.AssertExpectations(t)
|
||||
})
|
||||
// TODO: revive tests once export is finalized
|
||||
// t.Run("successful export to markdown", func(t *testing.T) {
|
||||
// // Given
|
||||
// ctx := context.Background()
|
||||
// fx := newFixture(t)
|
||||
//
|
||||
// // Mock the ObjectListExport call
|
||||
// fx.mwMock.
|
||||
// On("ObjectListExport", mock.Anything, &pb.RpcObjectListExportRequest{
|
||||
// SpaceId: spaceID,
|
||||
// Path: exportPath,
|
||||
// ObjectIds: []string{objectID},
|
||||
// Format: model.Export_Markdown,
|
||||
// Zip: false,
|
||||
// IncludeNested: false,
|
||||
// IncludeFiles: true,
|
||||
// IsJson: false,
|
||||
// IncludeArchived: false,
|
||||
// NoProgress: true,
|
||||
// }).
|
||||
// Return(&pb.RpcObjectListExportResponse{
|
||||
// Path: exportPath,
|
||||
// Error: &pb.RpcObjectListExportResponseError{
|
||||
// Code: pb.RpcObjectListExportResponseError_NULL,
|
||||
// },
|
||||
// }).
|
||||
// Once()
|
||||
//
|
||||
// // When
|
||||
// gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, exportFormat)
|
||||
//
|
||||
// // Then
|
||||
// require.NoError(t, err)
|
||||
// require.Equal(t, exportPath, gotPath)
|
||||
// fx.mwMock.AssertExpectations(t)
|
||||
// })
|
||||
//
|
||||
// t.Run("failed export returns error", func(t *testing.T) {
|
||||
// // Given
|
||||
// ctx := context.Background()
|
||||
// fx := newFixture(t)
|
||||
//
|
||||
// // Mock the ObjectListExport call to return an error code
|
||||
// fx.mwMock.
|
||||
// On("ObjectListExport", mock.Anything, mock.Anything).
|
||||
// Return(&pb.RpcObjectListExportResponse{
|
||||
// Path: "",
|
||||
// Error: &pb.RpcObjectListExportResponseError{
|
||||
// Code: pb.RpcObjectListExportResponseError_UNKNOWN_ERROR,
|
||||
// },
|
||||
// }).
|
||||
// Once()
|
||||
//
|
||||
// // When
|
||||
// gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, exportFormat)
|
||||
//
|
||||
// // Then
|
||||
// require.Error(t, err)
|
||||
// require.Empty(t, gotPath)
|
||||
// require.ErrorIs(t, err, ErrFailedExportObjectAsMarkdown)
|
||||
// fx.mwMock.AssertExpectations(t)
|
||||
// })
|
||||
//
|
||||
// t.Run("unrecognized format defaults to markdown", func(t *testing.T) {
|
||||
// ctx := context.Background()
|
||||
// fx := newFixture(t)
|
||||
//
|
||||
// fx.mwMock.
|
||||
// On("ObjectListExport", mock.Anything, &pb.RpcObjectListExportRequest{
|
||||
// SpaceId: spaceID,
|
||||
// Path: exportPath,
|
||||
// ObjectIds: []string{objectID},
|
||||
// Format: model.Export_Markdown, // fallback
|
||||
// Zip: false,
|
||||
// IncludeNested: false,
|
||||
// IncludeFiles: true,
|
||||
// IsJson: false,
|
||||
// IncludeArchived: false,
|
||||
// NoProgress: true,
|
||||
// }).
|
||||
// Return(&pb.RpcObjectListExportResponse{
|
||||
// Path: exportPath,
|
||||
// Error: &pb.RpcObjectListExportResponseError{
|
||||
// Code: pb.RpcObjectListExportResponseError_NULL,
|
||||
// },
|
||||
// }).
|
||||
// Once()
|
||||
//
|
||||
// // When
|
||||
// gotPath, err := fx.GetObjectExport(ctx, spaceID, objectID, unrecognizedFormat)
|
||||
//
|
||||
// // Then
|
||||
// require.NoError(t, err)
|
||||
// require.Equal(t, exportPath, gotPath)
|
||||
// fx.mwMock.AssertExpectations(t)
|
||||
// })
|
||||
}
|
||||
|
|
175
core/api/internal/list/handler.go
Normal file
175
core/api/internal/list/handler.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
)
|
||||
|
||||
// GetListViewsHandler
|
||||
//
|
||||
// @Summary Get list views
|
||||
// @Description Returns a paginated list of views defined for a specific list (query or collection) within a space. Each view includes configuration details such as layout, applied filters, and sorting options, enabling clients to render the list according to user preferences and context. This endpoint supports pagination parameters to control the number of views returned and the starting point of the result set.
|
||||
// @Tags lists
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param list_id path string true "List ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[View] "List of views"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/lists/{list_id}/views [get]
|
||||
func GetListViewsHandler(s *ListService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
listId := c.Param("list_id")
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
views, total, hasMore, err := s.GetListViews(c, spaceId, listId, offset, limit)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedGetList, http.StatusNotFound),
|
||||
util.ErrToCode(ErrFailedGetListDataview, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
pagination.RespondWithPagination(c, http.StatusOK, views, total, offset, limit, hasMore)
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectsInListHandler
|
||||
//
|
||||
// @Summary Get objects in list
|
||||
// @Description Returns a paginated list of objects associated with a specific list (query or collection) within a space. When a view ID is provided, the objects are filtered and sorted according to the view's configuration. If no view ID is specified, all list objects are returned without filtering and sorting. This endpoint helps clients to manage grouped objects (for example, tasks within a list) by returning information for each item of the list.
|
||||
// @Tags lists
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param list_id path string true "List ID"
|
||||
// @Param view_id path string true "View ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[object.Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/lists/{list_id}/{view_id}/objects [get]
|
||||
func GetObjectsInListHandler(s *ListService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
listId := c.Param("list_id")
|
||||
viewId := c.Param("view_id")
|
||||
offset := c.GetInt("offset")
|
||||
limit := c.GetInt("limit")
|
||||
|
||||
objects, total, hasMore, err := s.GetObjectsInList(c, spaceId, listId, viewId, offset, limit)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedGetList, http.StatusNotFound),
|
||||
util.ErrToCode(ErrFailedGetListDataview, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedGetListDataviewView, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrUnsupportedListType, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedGetObjectsInList, http.StatusInternalServerError),
|
||||
util.ErrToCode(util.ErrorTypeNotFound, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
pagination.RespondWithPagination(c, http.StatusOK, objects, total, offset, limit, hasMore)
|
||||
}
|
||||
}
|
||||
|
||||
// AddObjectsToListHandler
|
||||
//
|
||||
// @Summary Add objects to list
|
||||
// @Description Enables clients to add one or more objects to a specific list (collection only) by submitting a JSON array of object IDs. Upon success, the endpoint returns a confirmation message. This endpoint is vital for building user interfaces that allow drag‑and‑drop or multi‑select additions to collections.
|
||||
// @Tags lists
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param list_id path string true "List ID"
|
||||
// @Param objects body []string true "List of object IDs"
|
||||
// @Success 200 {object} string "Objects added successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/lists/{list_id}/objects [post]
|
||||
func AddObjectsToListHandler(s *ListService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
listId := c.Param("list_id")
|
||||
|
||||
objects := []string{}
|
||||
if err := c.ShouldBindJSON(&objects); err != nil {
|
||||
apiErr := util.CodeToAPIError(http.StatusBadRequest, err.Error())
|
||||
c.JSON(http.StatusBadRequest, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
err := s.AddObjectsToList(c, spaceId, listId, objects)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedAddObjectsToList, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, "Objects added successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveObjectFromListHandler
|
||||
//
|
||||
// @Summary Remove object from list
|
||||
// @Description Removes a given object from the specified list (collection only) in a space. The endpoint takes the space, list, and object identifiers as path parameters. It's subject to rate limiting and returns a success message on completion. It is used for dynamically managing collections without affecting the underlying object data.
|
||||
// @Tags lists
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param list_id path string true "List ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Success 200 {object} string "Objects removed successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/lists/{list_id}/objects/{object_id} [delete]
|
||||
func RemoveObjectFromListHandler(s *ListService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
listId := c.Param("list_id")
|
||||
objectId := c.Param("object_id")
|
||||
|
||||
err := s.RemoveObjectsFromList(c, spaceId, listId, []string{objectId})
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedRemoveObjectsFromList, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, "Objects removed successfully")
|
||||
}
|
||||
}
|
24
core/api/internal/list/model.go
Normal file
24
core/api/internal/list/model.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package list
|
||||
|
||||
type View struct {
|
||||
Id string `json:"id" example:"67bf3f21cda9134102e2422c"` // The id of the view
|
||||
Name string `json:"name" example:"All"` // The name of the view
|
||||
Layout string `json:"layout" example:"grid" enums:"grid,table"` // The layout of the view
|
||||
Filters []Filter `json:"filters"` // The list of filters
|
||||
Sorts []Sort `json:"sorts"` // The list of sorts
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
Id string `json:"id" example:"67bf3f21cda9134102e2422c"` // The id of the filter
|
||||
PropertyKey string `json:"property_key" example:"name"` // The property key used for filtering
|
||||
Format string `json:"format" example:"text" enum:"text,number,select,multi_select,date,file,checkbox,url,email,phone,object"` // The format of the property used for filtering
|
||||
Condition string `json:"condition" example:"contains" enum:"equal,not_equal,greater,less,greater_or_equal,less_or_equal,like,not_like,in,not_in,empty,not_empty,all_in,not_all_in,exact_in,not_exact_in,exists"` // The filter condition
|
||||
Value string `json:"value" example:"Some value..."` // The value used for filtering
|
||||
}
|
||||
|
||||
type Sort struct {
|
||||
Id string `json:"id" example:"67bf3f21cda9134102e2422c"` // The id of the sort
|
||||
PropertyKey string `json:"property_key" example:"name"` // The property key used for sorting
|
||||
Format string `json:"format" example:"text" enum:"text,number,select,multi_select,date,file,checkbox,url,email,phone,object"` // The format of the property used for sorting
|
||||
SortType string `json:"sort_type" example:"asc" enum:"asc,desc,custom"` // The sort direction
|
||||
}
|
244
core/api/internal/list/service.go
Normal file
244
core/api/internal/list/service.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedGetList = errors.New("failed to get list")
|
||||
ErrFailedGetListDataview = errors.New("failed to get list dataview")
|
||||
ErrFailedGetListDataviewView = errors.New("failed to get list dataview view")
|
||||
ErrUnsupportedListType = errors.New("unsupported list type")
|
||||
ErrFailedGetObjectsInList = errors.New("failed to get objects in list")
|
||||
ErrFailedAddObjectsToList = errors.New("failed to add objects to list")
|
||||
ErrFailedRemoveObjectsFromList = errors.New("failed to remove objects from list")
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
GetListViews(ctx context.Context, spaceId string, listId string, offset, limit int) ([]View, int, bool, error)
|
||||
GetObjectsInList(ctx context.Context, spaceId string, listId string, viewId string, offset, limit int) ([]object.Object, int, bool, error)
|
||||
AddObjectsToList(ctx context.Context, spaceId string, listId string, objectIds []string) error
|
||||
RemoveObjectsFromList(ctx context.Context, spaceId string, listId string, objectIds []string) error
|
||||
}
|
||||
|
||||
type ListService struct {
|
||||
mw apicore.ClientCommands
|
||||
objectService *object.ObjectService
|
||||
}
|
||||
|
||||
func NewService(mw apicore.ClientCommands, objectService *object.ObjectService) *ListService {
|
||||
return &ListService{mw: mw, objectService: objectService}
|
||||
}
|
||||
|
||||
// GetListViews retrieves views of a list
|
||||
func (s *ListService) GetListViews(ctx context.Context, spaceId string, listId string, offset, limit int) ([]View, int, bool, error) {
|
||||
resp := s.mw.ObjectShow(ctx, &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: listId,
|
||||
})
|
||||
|
||||
if resp.Error != nil && resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedGetList
|
||||
}
|
||||
|
||||
var dataviewBlock *model.Block
|
||||
for _, block := range resp.ObjectView.Blocks {
|
||||
if block.Id == "dataview" {
|
||||
dataviewBlock = block
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if dataviewBlock == nil {
|
||||
return nil, 0, false, ErrFailedGetListDataview
|
||||
}
|
||||
|
||||
var views []View
|
||||
switch content := dataviewBlock.Content.(type) {
|
||||
case *model.BlockContentOfDataview:
|
||||
for _, view := range content.Dataview.Views {
|
||||
var filters []Filter
|
||||
for _, f := range view.Filters {
|
||||
filters = append(filters, Filter{
|
||||
Id: f.Id,
|
||||
PropertyKey: f.RelationKey,
|
||||
Format: s.objectService.MapRelationFormat(f.Format),
|
||||
Condition: strcase.ToSnake(model.BlockContentDataviewFilterCondition_name[int32(f.Condition)]),
|
||||
Value: f.Value.GetStringValue(),
|
||||
})
|
||||
}
|
||||
var sorts []Sort
|
||||
for _, srt := range view.Sorts {
|
||||
sorts = append(sorts, Sort{
|
||||
Id: srt.Id,
|
||||
PropertyKey: srt.RelationKey,
|
||||
Format: s.objectService.MapRelationFormat(srt.Format),
|
||||
SortType: strcase.ToSnake(model.BlockContentDataviewSortType_name[int32(srt.Type)]),
|
||||
})
|
||||
}
|
||||
views = append(views, View{
|
||||
Id: view.Id,
|
||||
Name: view.Name,
|
||||
Layout: s.mapDataviewTypeName(view.Type),
|
||||
Filters: filters,
|
||||
Sorts: sorts,
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, 0, false, ErrFailedGetListDataview
|
||||
}
|
||||
|
||||
total := len(views)
|
||||
paginatedViews, hasMore := pagination.Paginate(views, offset, limit)
|
||||
|
||||
return paginatedViews, total, hasMore, nil
|
||||
}
|
||||
|
||||
// GetObjectsInList retrieves objects in a list
|
||||
func (s *ListService) GetObjectsInList(ctx context.Context, spaceId string, listId string, viewId string, offset, limit int) ([]object.Object, int, bool, error) {
|
||||
resp := s.mw.ObjectShow(ctx, &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: listId,
|
||||
})
|
||||
|
||||
if resp.Error != nil && resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedGetList
|
||||
}
|
||||
|
||||
var dataviewBlock *model.Block
|
||||
for _, block := range resp.ObjectView.Blocks {
|
||||
if block.Id == "dataview" {
|
||||
dataviewBlock = block
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if dataviewBlock == nil {
|
||||
return nil, 0, false, ErrFailedGetListDataview
|
||||
}
|
||||
|
||||
var sorts []*model.BlockContentDataviewSort
|
||||
var filters []*model.BlockContentDataviewFilter
|
||||
|
||||
switch content := dataviewBlock.Content.(type) {
|
||||
case *model.BlockContentOfDataview:
|
||||
// if view not specified: return all objects without filtering and sorting
|
||||
if viewId != "" {
|
||||
var targetView *model.BlockContentDataviewView
|
||||
for _, view := range content.Dataview.Views {
|
||||
if view.Id == viewId {
|
||||
targetView = view
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetView == nil {
|
||||
return nil, 0, false, ErrFailedGetListDataviewView
|
||||
}
|
||||
sorts = targetView.Sorts
|
||||
filters = targetView.Filters
|
||||
}
|
||||
default:
|
||||
return nil, 0, false, ErrFailedGetListDataview
|
||||
}
|
||||
|
||||
var collectionId string
|
||||
var source []string
|
||||
listType := s.objectService.GetTypeFromDetails(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyType.String()].GetStringValue(), resp.ObjectView.Details)
|
||||
|
||||
if listType.RecommendedLayout == "set" {
|
||||
// for queries, we search within the space for objects of the setOf type
|
||||
setOfValues := resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySetOf.String()].GetListValue().Values
|
||||
for _, value := range setOfValues {
|
||||
typeKey, err := util.ResolveIdtoUniqueKey(s.mw, spaceId, value.GetStringValue())
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
source = append(source, typeKey)
|
||||
}
|
||||
} else if listType.RecommendedLayout == "collection" {
|
||||
// for collections, we need to search within that collection
|
||||
collectionId = listId
|
||||
} else {
|
||||
return nil, 0, false, ErrUnsupportedListType
|
||||
}
|
||||
|
||||
// TODO: use subscription service with internal: 'true' to not send events to clients
|
||||
searchResp := s.mw.ObjectSearchSubscribe(ctx, &pb.RpcObjectSearchSubscribeRequest{
|
||||
SpaceId: spaceId,
|
||||
Limit: int64(limit), // nolint: gosec
|
||||
Offset: int64(offset), // nolint: gosec
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
Source: source,
|
||||
CollectionId: collectionId,
|
||||
})
|
||||
|
||||
// TODO: returned error from ObjectSearchSubscribe is inconsistent with other RPCs: Error is nil instead of Code being NULL
|
||||
if searchResp.Error != nil && searchResp.Error.Code != pb.RpcObjectSearchSubscribeResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedGetObjectsInList
|
||||
}
|
||||
|
||||
total := int(searchResp.Counters.Total)
|
||||
hasMore := searchResp.Counters.Total > int64(offset+limit)
|
||||
|
||||
objects := make([]object.Object, 0, len(searchResp.Records))
|
||||
for _, record := range searchResp.Records {
|
||||
object, err := s.objectService.GetObject(ctx, spaceId, record.Fields[bundle.RelationKeyId.String()].GetStringValue())
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
objects = append(objects, object)
|
||||
}
|
||||
|
||||
return objects, total, hasMore, nil
|
||||
}
|
||||
|
||||
// AddObjectsToList adds objects to a list
|
||||
func (s *ListService) AddObjectsToList(ctx context.Context, spaceId string, listId string, objectIds []string) error {
|
||||
resp := s.mw.ObjectCollectionAdd(ctx, &pb.RpcObjectCollectionAddRequest{
|
||||
ContextId: listId,
|
||||
ObjectIds: objectIds,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectCollectionAddResponseError_NULL {
|
||||
return ErrFailedAddObjectsToList
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveObjectsFromList removes objects from a list
|
||||
func (s *ListService) RemoveObjectsFromList(ctx context.Context, spaceId string, listId string, objectIds []string) error {
|
||||
resp := s.mw.ObjectCollectionRemove(ctx, &pb.RpcObjectCollectionRemoveRequest{
|
||||
ContextId: listId,
|
||||
ObjectIds: objectIds,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectCollectionRemoveResponseError_NULL {
|
||||
return ErrFailedRemoveObjectsFromList
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapDataviewTypeName maps the dataview type to a string.
|
||||
func (s *ListService) mapDataviewTypeName(dataviewType model.BlockContentDataviewViewType) string {
|
||||
switch dataviewType {
|
||||
case model.BlockContentDataviewView_Table:
|
||||
return "grid"
|
||||
default:
|
||||
return strcase.ToSnake(model.BlockContentDataviewViewType_name[int32(dataviewType)])
|
||||
}
|
||||
}
|
947
core/api/internal/list/service_test.go
Normal file
947
core/api/internal/list/service_test.go
Normal file
|
@ -0,0 +1,947 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"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/util/pbtypes"
|
||||
)
|
||||
|
||||
const (
|
||||
mockedSpaceId = "mocked-space-id"
|
||||
mockedListId = "mocked-list-id"
|
||||
mockedTypeId = "mocked-type-id"
|
||||
mockedSetOfTypeId = "mocked-set-of-type-id"
|
||||
mockedUniqueKey = "mocked-unique-key"
|
||||
mockedViewId = "view-1"
|
||||
offset = 0
|
||||
limit = 100
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*ListService
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
objectService *object.ObjectService
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
spaceService := space.NewService(mwMock)
|
||||
objectService := object.NewService(mwMock, spaceService)
|
||||
objectService.AccountInfo = &model.AccountInfo{
|
||||
TechSpaceId: "mocked-tech-space-id",
|
||||
GatewayUrl: "http://localhost:31006",
|
||||
}
|
||||
listService := NewService(mwMock, objectService)
|
||||
return &fixture{
|
||||
ListService: listService,
|
||||
mwMock: mwMock,
|
||||
objectService: objectService,
|
||||
}
|
||||
}
|
||||
|
||||
func TestListService_GetListViews(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("successful", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
// Prepare a view with one sort and one filter
|
||||
sorts := []*model.BlockContentDataviewSort{
|
||||
{
|
||||
Id: "sort-1",
|
||||
RelationKey: "dummy-sort",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
}
|
||||
filters := []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Id: "filter-1",
|
||||
RelationKey: "dummy-filter",
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("dummy-value"),
|
||||
},
|
||||
}
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: "view-1",
|
||||
Name: "Test View",
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
Type: model.BlockContentDataviewView_Table,
|
||||
}
|
||||
|
||||
resp := &pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(resp, nil).Once()
|
||||
|
||||
views, total, hasMore, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, views, 1)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
|
||||
retView := views[0]
|
||||
require.Equal(t, "view-1", retView.Id)
|
||||
require.Equal(t, "Test View", retView.Name)
|
||||
require.Len(t, retView.Filters, 1)
|
||||
require.Len(t, retView.Sorts, 1)
|
||||
})
|
||||
|
||||
t.Run("object show error", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
_, _, _, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.ErrorIs(t, err, ErrFailedGetList)
|
||||
})
|
||||
|
||||
t.Run("no dataview block", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{Id: "non-dataview"},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
_, _, _, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.ErrorIs(t, err, ErrFailedGetListDataview)
|
||||
})
|
||||
|
||||
t.Run("invalid dataview content", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{Id: "dataview", Content: nil},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
_, _, _, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.ErrorIs(t, err, ErrFailedGetListDataview)
|
||||
})
|
||||
|
||||
t.Run("view with no sorts", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
// Create a view with filters but no sorts
|
||||
filters := []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Id: "filter-1",
|
||||
RelationKey: "dummy-filter",
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("dummy-value"),
|
||||
},
|
||||
}
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: "view-2",
|
||||
Name: "No Sort View",
|
||||
Sorts: []*model.BlockContentDataviewSort{},
|
||||
Filters: filters,
|
||||
Type: model.BlockContentDataviewView_Table,
|
||||
}
|
||||
|
||||
resp := &pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(resp, nil).Once()
|
||||
|
||||
views, total, hasMore, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, views, 1)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("view with multiple sorts", func(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
|
||||
// Create a view with 2 sorts
|
||||
sorts := []*model.BlockContentDataviewSort{
|
||||
{
|
||||
Id: "sort-1",
|
||||
RelationKey: "dummy-sort",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
{
|
||||
Id: "sort-2",
|
||||
RelationKey: "dummy-sort2",
|
||||
Type: model.BlockContentDataviewSort_Desc,
|
||||
},
|
||||
}
|
||||
filters := []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Id: "filter-1",
|
||||
RelationKey: "dummy-filter",
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("dummy-value"),
|
||||
},
|
||||
}
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: "view-3",
|
||||
Name: "Multi-Sort View",
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
Type: model.BlockContentDataviewView_Table,
|
||||
}
|
||||
|
||||
resp := &pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(resp, nil).Once()
|
||||
|
||||
views, total, hasMore, err := fx.GetListViews(ctx, mockedSpaceId, mockedListId, offset, limit)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, views, 1)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
|
||||
require.Equal(t, "view-3", views[0].Id)
|
||||
require.Len(t, views[0].Sorts, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListService_GetObjectsInList(t *testing.T) {
|
||||
|
||||
t.Run("successful", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Prepare a dataview view with dummy sorts and filters.
|
||||
sorts := []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: "dummy-sort",
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
}
|
||||
filters := []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: "dummy-filter",
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("dummy-value"),
|
||||
},
|
||||
}
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: mockedViewId,
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
}
|
||||
|
||||
// Expect the ObjectShow call for the list to return the type in the details and dataview block.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedListId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeySetOf.String(): pbtypes.StringList([]string{mockedSetOfTypeId}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Int64(int64(model.ObjectType_set)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Expect the ObjectShow to return the type's unique key.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedSetOfTypeId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedSetOfTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedUniqueKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Expect the ObjectSearchSubscribe call to return one record.
|
||||
fx.mwMock.
|
||||
On("ObjectSearchSubscribe", mock.Anything, &pb.RpcObjectSearchSubscribeRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Limit: int64(limit),
|
||||
Offset: int64(offset),
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
Source: []string{mockedUniqueKey},
|
||||
}).
|
||||
Return(&pb.RpcObjectSearchSubscribeResponse{
|
||||
Error: &pb.RpcObjectSearchSubscribeResponseError{Code: pb.RpcObjectSearchSubscribeResponseError_NULL},
|
||||
Counters: &pb.EventObjectSubscriptionCounters{Total: 1},
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String("object-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Expect the object service to be called to get details for "object-1".
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: "object-1",
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: "object-1",
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String("object-1"),
|
||||
bundle.RelationKeyName.String(): pbtypes.String("Object One"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, mockedViewId, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 1)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, "object-1", objects[0].Id)
|
||||
require.Equal(t, "Object One", objects[0].Name)
|
||||
})
|
||||
|
||||
t.Run("successful with empty viewId", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Prepare an ObjectShow response with a dataview block containing a view (which will not be used since viewId is empty)
|
||||
resp := &pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedListId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeySetOf.String(): pbtypes.StringList([]string{mockedSetOfTypeId}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Int64(int64(model.ObjectType_collection)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{
|
||||
{
|
||||
Id: mockedListId,
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
Id: "view_sort",
|
||||
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Id: "view_filter",
|
||||
RelationKey: bundle.RelationKeyStatus.String(),
|
||||
Format: model.RelationFormat_longtext,
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("active"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(resp, nil).Once()
|
||||
|
||||
// Since viewId is empty, sorts and filters should be nil.
|
||||
fx.mwMock.
|
||||
On("ObjectSearchSubscribe", mock.Anything, &pb.RpcObjectSearchSubscribeRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Limit: int64(limit),
|
||||
Offset: int64(offset),
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Sorts: nil,
|
||||
Filters: nil,
|
||||
CollectionId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectSearchSubscribeResponse{
|
||||
Error: &pb.RpcObjectSearchSubscribeResponseError{Code: pb.RpcObjectSearchSubscribeResponseError_NULL},
|
||||
Counters: &pb.EventObjectSubscriptionCounters{Total: 1},
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String("object-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Expect the ObjectShow call for "object-1" to return its details.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: "object-1",
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: "object-1",
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String("object-1"),
|
||||
bundle.RelationKeyName.String(): pbtypes.String("Object One"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "", offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 1)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
require.Equal(t, "object-1", objects[0].Id)
|
||||
require.Equal(t, "Object One", objects[0].Name)
|
||||
})
|
||||
|
||||
t.Run("object show error", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Simulate an error response (non-NULL error code) from ObjectShow for the list.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedGetList)
|
||||
})
|
||||
|
||||
t.Run("no dataview block", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Return an ObjectView that does not contain a block with ID "dataview".
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{Id: "non-dataview"},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedGetListDataview)
|
||||
})
|
||||
|
||||
t.Run("invalid dataview content", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Return a "dataview" block that does not have the expected content type.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedGetListDataview)
|
||||
})
|
||||
|
||||
t.Run("view not found", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Prepare a dataview that only contains a view with an ID different from the one requested.
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: "some-other-view",
|
||||
Sorts: []*model.BlockContentDataviewSort{},
|
||||
Filters: []*model.BlockContentDataviewFilter{},
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "non-existent-view", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedGetListDataviewView)
|
||||
})
|
||||
|
||||
t.Run("search subscribe error", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Prepare an empty dataview view (no sorts/filters).
|
||||
sorts := []*model.BlockContentDataviewSort{}
|
||||
filters := []*model.BlockContentDataviewFilter{}
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: mockedViewId,
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedListId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeySetOf.String(): pbtypes.StringList([]string{mockedSetOfTypeId}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Int64(int64(model.ObjectType_collection)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Simulate an error from ObjectSearchSubscribe.
|
||||
fx.mwMock.
|
||||
On("ObjectSearchSubscribe", mock.Anything, &pb.RpcObjectSearchSubscribeRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Limit: int64(limit),
|
||||
Offset: int64(offset),
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
CollectionId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectSearchSubscribeResponse{
|
||||
Error: &pb.RpcObjectSearchSubscribeResponseError{Code: pb.RpcObjectSearchSubscribeResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, mockedViewId, offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedGetObjectsInList)
|
||||
})
|
||||
|
||||
t.Run("get object error", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
var sorts []*model.BlockContentDataviewSort
|
||||
var filters []*model.BlockContentDataviewFilter
|
||||
view := &model.BlockContentDataviewView{
|
||||
Id: mockedViewId,
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedListId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeySetOf.String(): pbtypes.StringList([]string{mockedSetOfTypeId}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Int64(int64(model.ObjectType_collection)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: "dataview",
|
||||
Content: &model.BlockContentOfDataview{
|
||||
Dataview: &model.BlockContentDataview{
|
||||
Views: []*model.BlockContentDataviewView{view},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectSearchSubscribe", mock.Anything, &pb.RpcObjectSearchSubscribeRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Limit: int64(limit),
|
||||
Offset: int64(offset),
|
||||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
Sorts: sorts,
|
||||
Filters: filters,
|
||||
CollectionId: mockedListId,
|
||||
}).
|
||||
Return(&pb.RpcObjectSearchSubscribeResponse{
|
||||
Error: &pb.RpcObjectSearchSubscribeResponseError{Code: pb.RpcObjectSearchSubscribeResponseError_NULL},
|
||||
Counters: &pb.EventObjectSubscriptionCounters{Total: 1},
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String("object-err"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Simulate an error when trying to retrieve the object details.
|
||||
fx.mwMock.
|
||||
On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: "object-err",
|
||||
}).
|
||||
Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NOT_FOUND},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
_, _, _, err := fx.GetObjectsInList(ctx, mockedSpaceId, mockedListId, "", offset, limit)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, object.ErrObjectNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListService_AddObjectsToList(t *testing.T) {
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
objectIds := []string{"obj-1", "obj-2"}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectCollectionAdd", mock.Anything, &pb.RpcObjectCollectionAddRequest{
|
||||
ContextId: mockedListId,
|
||||
ObjectIds: objectIds,
|
||||
}).
|
||||
Return(&pb.RpcObjectCollectionAddResponse{
|
||||
Error: &pb.RpcObjectCollectionAddResponseError{Code: pb.RpcObjectCollectionAddResponseError_NULL},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
err := fx.AddObjectsToList(ctx, mockedSpaceId, mockedListId, objectIds)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("failure", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
objectIds := []string{"obj-1"}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectCollectionAdd", mock.Anything, &pb.RpcObjectCollectionAddRequest{
|
||||
ContextId: mockedListId,
|
||||
ObjectIds: objectIds,
|
||||
}).
|
||||
Return(&pb.RpcObjectCollectionAddResponse{
|
||||
Error: &pb.RpcObjectCollectionAddResponseError{Code: pb.RpcObjectCollectionAddResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
err := fx.AddObjectsToList(ctx, mockedSpaceId, mockedListId, objectIds)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedAddObjectsToList)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListService_RemoveObjectsFromList(t *testing.T) {
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
objectIds := []string{"obj-1", "obj-2"}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectCollectionRemove", mock.Anything, &pb.RpcObjectCollectionRemoveRequest{
|
||||
ContextId: mockedListId,
|
||||
ObjectIds: objectIds,
|
||||
}).
|
||||
Return(&pb.RpcObjectCollectionRemoveResponse{
|
||||
Error: &pb.RpcObjectCollectionRemoveResponseError{Code: pb.RpcObjectCollectionRemoveResponseError_NULL},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
err := fx.RemoveObjectsFromList(ctx, mockedSpaceId, mockedListId, objectIds)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("failure", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
objectIds := []string{"obj-1"}
|
||||
|
||||
fx.mwMock.
|
||||
On("ObjectCollectionRemove", mock.Anything, &pb.RpcObjectCollectionRemoveRequest{
|
||||
ContextId: mockedListId,
|
||||
ObjectIds: objectIds,
|
||||
}).
|
||||
Return(&pb.RpcObjectCollectionRemoveResponse{
|
||||
Error: &pb.RpcObjectCollectionRemoveResponseError{Code: pb.RpcObjectCollectionRemoveResponseError_UNKNOWN_ERROR},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
err := fx.RemoveObjectsFromList(ctx, mockedSpaceId, mockedListId, objectIds)
|
||||
|
||||
// then
|
||||
require.ErrorIs(t, err, ErrFailedRemoveObjectsFromList)
|
||||
})
|
||||
}
|
|
@ -11,17 +11,18 @@ import (
|
|||
|
||||
// GetObjectsHandler retrieves a list of objects in a space
|
||||
//
|
||||
// @Summary List objects
|
||||
// @Tags objects
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/objects [get]
|
||||
// @Summary List objects
|
||||
// @Description Retrieves a paginated list of objects in the given space. The endpoint takes query parameters for pagination (offset and limit) and returns detailed data about each object including its ID, name, icon, type information, a snippet of the content (if applicable), layout, space ID, blocks and details. It is intended for building views where users can see all objects in a space at a glance.
|
||||
// @Tags objects
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/objects [get]
|
||||
func GetObjectsHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -47,17 +48,19 @@ func GetObjectsHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// GetObjectHandler retrieves an object in a space
|
||||
//
|
||||
// @Summary Get object
|
||||
// @Tags objects
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Success 200 {object} ObjectResponse "The requested object"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/objects/{object_id} [get]
|
||||
// @Summary Get object
|
||||
// @Description Fetches the full details of a single object identified by the object ID within the specified space. The response includes not only basic metadata (ID, name, icon, type) but also the complete set of blocks (which may include text, files, properties and dataviews) and extra details (such as timestamps and linked member information). This endpoint is essential when a client needs to render or edit the full object view.
|
||||
// @Tags objects
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Success 200 {object} ObjectResponse "The requested object"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 410 {object} util.GoneError "Resource deleted"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/objects/{object_id} [get]
|
||||
func GetObjectHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -66,6 +69,7 @@ func GetObjectHandler(s *ObjectService) gin.HandlerFunc {
|
|||
object, err := s.GetObject(c.Request.Context(), spaceId, objectId)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrObjectNotFound, http.StatusNotFound),
|
||||
util.ErrToCode(ErrObjectDeleted, http.StatusGone),
|
||||
util.ErrToCode(ErrFailedRetrieveObject, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
|
@ -81,18 +85,20 @@ func GetObjectHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// DeleteObjectHandler deletes an object in a space
|
||||
//
|
||||
// @Summary Delete object
|
||||
// @Tags objects
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Success 200 {object} ObjectResponse "The deleted object"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 403 {object} util.ForbiddenError "Forbidden"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/objects/{object_id} [delete]
|
||||
// @Summary Delete object
|
||||
// @Description This endpoint “deletes” an object by marking it as archived. The deletion process is performed safely and is subject to rate limiting. It returns the object’s details after it has been archived. Proper error handling is in place for situations such as when the object isn’t found or the deletion cannot be performed because of permission issues.
|
||||
// @Tags objects
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object_id path string true "Object ID"
|
||||
// @Success 200 {object} ObjectResponse "The deleted object"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 403 {object} util.ForbiddenError "Forbidden"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 423 {object} util.RateLimitError "Rate limit exceeded"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/objects/{object_id} [delete]
|
||||
func DeleteObjectHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -117,17 +123,20 @@ func DeleteObjectHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// CreateObjectHandler creates a new object in a space
|
||||
//
|
||||
// @Summary Create object
|
||||
// @Tags objects
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object body CreateObjectRequest true "Object to create"
|
||||
// @Success 200 {object} ObjectResponse "The created object"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/objects [post]
|
||||
// @Summary Create object
|
||||
// @Description Creates a new object in the specified space using a JSON payload. The creation process is subject to rate limiting. The payload must include key details such as the object name, icon, description, body content (which may support Markdown), source URL (required for bookmark objects), template identifier, and the type_key (which is the non-unique identifier of the type of object to create). Post-creation, additional operations (like setting featured properties or fetching bookmark metadata) may occur. The endpoint then returns the full object data, ready for further interactions.
|
||||
// @Tags objects
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param object body CreateObjectRequest true "Object to create"
|
||||
// @Success 200 {object} ObjectResponse "The created object"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 423 {object} util.RateLimitError "Rate limit exceeded"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/objects [post]
|
||||
func CreateObjectHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -142,9 +151,12 @@ func CreateObjectHandler(s *ObjectService) gin.HandlerFunc {
|
|||
object, err := s.CreateObject(c.Request.Context(), spaceId, request)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrInputMissingSource, http.StatusBadRequest),
|
||||
util.ErrToCode(ErrIconNameColorNotSupported, http.StatusBadRequest),
|
||||
util.ErrToCode(ErrFailedCreateBookmark, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedCreateObject, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedSetRelationFeatured, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedFetchBookmark, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedSetPropertyFeatured, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedCreateBlock, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedPasteBody, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrObjectNotFound, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedRetrieveObject, http.StatusInternalServerError),
|
||||
)
|
||||
|
@ -161,17 +173,18 @@ func CreateObjectHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// GetTypesHandler retrieves a list of types in a space
|
||||
//
|
||||
// @Summary List types
|
||||
// @Tags types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Type] "List of types"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/types [get]
|
||||
// @Summary List types
|
||||
// @Description This endpoint retrieves a paginated list of object types (e.g. 'Page', 'Note', 'Task') available within the specified space. Each type’s record includes its unique identifier, type key, display name, icon, and a recommended layout. While a type's id is truly unique, a type's key can be the same across spaces for known types, e.g. 'ot-page' for 'Page'. Clients use this information when offering choices for object creation or for filtering objects by type through search.
|
||||
// @Tags types
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Type] "List of types"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/types [get]
|
||||
func GetTypesHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -195,17 +208,19 @@ func GetTypesHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// GetTypeHandler retrieves a type in a space
|
||||
//
|
||||
// @Summary Get type
|
||||
// @Tags types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Success 200 {object} TypeResponse "The requested type"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/types/{type_id} [get]
|
||||
// @Summary Get type
|
||||
// @Description Fetches detailed information about one specific object type by its ID. This includes the type’s unique key, name, icon, and recommended layout. This detailed view assists clients in understanding the expected structure and style for objects of that type and in guiding the user interface (such as displaying appropriate icons or layout hints).
|
||||
// @Tags types
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Success 200 {object} TypeResponse "The requested type"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 410 {object} util.GoneError "Resource deleted"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/types/{type_id} [get]
|
||||
func GetTypeHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -214,6 +229,7 @@ func GetTypeHandler(s *ObjectService) gin.HandlerFunc {
|
|||
object, err := s.GetType(c.Request.Context(), spaceId, typeId)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrTypeNotFound, http.StatusNotFound),
|
||||
util.ErrToCode(ErrTypeDeleted, http.StatusGone),
|
||||
util.ErrToCode(ErrFailedRetrieveType, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
|
@ -229,18 +245,19 @@ func GetTypeHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// GetTemplatesHandler retrieves a list of templates for a type in a space
|
||||
//
|
||||
// @Summary List templates
|
||||
// @Tags types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Template] "List of templates"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/types/{type_id}/templates [get]
|
||||
// @Summary List templates
|
||||
// @Description This endpoint returns a paginated list of templates that are associated with a specific object type within a space. Templates provide pre‑configured structures for creating new objects. Each template record contains its identifier, name, and icon, so that clients can offer users a selection of templates when creating objects.
|
||||
// @Tags templates
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Template] "List of templates"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/types/{type_id}/templates [get]
|
||||
func GetTemplatesHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -268,18 +285,20 @@ func GetTemplatesHandler(s *ObjectService) gin.HandlerFunc {
|
|||
|
||||
// GetTemplateHandler retrieves a template for a type in a space
|
||||
//
|
||||
// @Summary Get template
|
||||
// @Tags types
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Param template_id path string true "Template ID"
|
||||
// @Success 200 {object} TemplateResponse "The requested template"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/types/{type_id}/templates/{template_id} [get]
|
||||
// @Summary Get template
|
||||
// @Description Fetches full details for one template associated with a particular object type in a space. The response provides the template’s identifier, name, icon, and any other relevant metadata. This endpoint is useful when a client needs to preview or apply a template to prefill object creation fields.
|
||||
// @Tags templates
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param type_id path string true "Type ID"
|
||||
// @Param template_id path string true "Template ID"
|
||||
// @Success 200 {object} TemplateResponse "The requested template"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Resource not found"
|
||||
// @Failure 410 {object} util.GoneError "Resource deleted"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/types/{type_id}/templates/{template_id} [get]
|
||||
func GetTemplateHandler(s *ObjectService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -289,6 +308,7 @@ func GetTemplateHandler(s *ObjectService) gin.HandlerFunc {
|
|||
object, err := s.GetTemplate(c.Request.Context(), spaceId, typeId, templateId)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrTemplateNotFound, http.StatusNotFound),
|
||||
util.ErrToCode(ErrTemplateDeleted, http.StatusGone),
|
||||
util.ErrToCode(ErrFailedRetrieveTemplate, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,93 +1,111 @@
|
|||
package object
|
||||
|
||||
import "github.com/anyproto/anytype-heart/core/api/util"
|
||||
|
||||
type CreateObjectRequest struct {
|
||||
Name string `json:"name" example:"Object Name"`
|
||||
Icon string `json:"icon" example:"📄"`
|
||||
Description string `json:"description" example:"Object Description"`
|
||||
Body string `json:"body" example:"Object Body"`
|
||||
Source string `json:"source" example:"https://source.com"`
|
||||
TemplateId string `json:"template_id" example:"bafyreictrp3obmnf6dwejy5o4p7bderaaia4bdg2psxbfzf44yya5uutge"`
|
||||
ObjectTypeUniqueKey string `json:"object_type_unique_key" example:"ot-page"`
|
||||
Name string `json:"name" example:"My object"` // The name of the object
|
||||
Icon util.Icon `json:"icon"` // The icon of the object
|
||||
Description string `json:"description" example:"This is a description of the object."` // The description of the object
|
||||
Body string `json:"body" example:"This is the body of the object. Markdown syntax is supported here."` // The body of the object
|
||||
Source string `json:"source" example:"https://bookmark-source.com"` // The source url, only applicable for bookmarks
|
||||
TemplateId string `json:"template_id" example:"bafyreictrp3obmnf6dwejy5o4p7bderaaia4bdg2psxbfzf44yya5uutge"` // The id of the template to use
|
||||
TypeKey string `json:"type_key" example:"ot-page"` // The key of the type of object to create
|
||||
}
|
||||
|
||||
type ObjectResponse struct {
|
||||
Object Object `json:"object"`
|
||||
Object Object `json:"object"` // The object
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Type string `json:"type" example:"Page"`
|
||||
Id string `json:"id" example:"bafyreie6n5l5nkbjal37su54cha4coy7qzuhrnajluzv5qd5jvtsrxkequ"`
|
||||
Name string `json:"name" example:"Object Name"`
|
||||
Icon string `json:"icon" example:"📄"`
|
||||
Snippet string `json:"snippet" example:"The beginning of the object body..."`
|
||||
Layout string `json:"layout" example:"basic"`
|
||||
SpaceId string `json:"space_id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"`
|
||||
RootId string `json:"root_id" example:"bafyreicypzj6uvu54664ucv3hmbsd5cmdy2dv4fwua26sciq74khzpyn4u"`
|
||||
Blocks []Block `json:"blocks"`
|
||||
Details []Detail `json:"details"`
|
||||
Object string `json:"object" example:"object"` // The data model of the object
|
||||
Id string `json:"id" example:"bafyreie6n5l5nkbjal37su54cha4coy7qzuhrnajluzv5qd5jvtsrxkequ"` // The id of the object
|
||||
Name string `json:"name" example:"My object"` // The name of the object
|
||||
Icon util.Icon `json:"icon"` // The icon of the object
|
||||
Archived bool `json:"archived" example:"false"` // Whether the object is archived
|
||||
SpaceId string `json:"space_id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"` // The id of the space the object is in
|
||||
Snippet string `json:"snippet" example:"The beginning of the object body..."` // The snippet of the object, especially important for notes as they don't have a name
|
||||
Layout string `json:"layout" example:"basic"` // The layout of the object
|
||||
Type Type `json:"type"` // The type of the object
|
||||
Blocks []Block `json:"blocks"` // The blocks of the object. Omitted in Search endpoints, returned only in GetObject endpoint
|
||||
Properties []Property `json:"properties"` // The properties of the object
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Id string `json:"id" example:"64394517de52ad5acb89c66c"`
|
||||
ChildrenIds []string `json:"children_ids" example:"['6797ce8ecda913cde14b02dc']"`
|
||||
BackgroundColor string `json:"background_color" example:"red"`
|
||||
Align string `json:"align" enums:"AlignLeft,AlignCenter,AlignRight,AlignJustify" example:"AlignLeft"`
|
||||
VerticalAlign string `json:"vertical_align" enums:"VerticalAlignTop,VerticalAlignMiddle,VerticalAlignBottom" example:"VerticalAlignTop"`
|
||||
Text *Text `json:"text,omitempty"`
|
||||
File *File `json:"file,omitempty"`
|
||||
Id string `json:"id" example:"64394517de52ad5acb89c66c"` // The id of the block
|
||||
ChildrenIds []string `json:"children_ids" example:"['6797ce8ecda913cde14b02dc']"` // The ids of the block's children
|
||||
BackgroundColor string `json:"background_color" example:"red"` // The background color of the block
|
||||
Align string `json:"align" enums:"AlignLeft,AlignCenter,AlignRight,AlignJustify" example:"AlignLeft"` // The alignment of the block
|
||||
VerticalAlign string `json:"vertical_align" enums:"VerticalAlignTop,VerticalAlignMiddle,VerticalAlignBottom" example:"VerticalAlignTop"` // The vertical alignment of the block
|
||||
Text *Text `json:"text,omitempty"` // The text of the block, if applicable
|
||||
File *File `json:"file,omitempty"` // The file of the block, if applicable
|
||||
Property *Property `json:"property,omitempty"` // The property block, if applicable
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Text string `json:"text" example:"Some text"`
|
||||
Style string `json:"style" enums:"Paragraph,Header1,Header2,Header3,Header4,Quote,Code,Title,Checkbox,Marked,Numbered,Toggle,Description,Callout" example:"Paragraph"`
|
||||
Checked bool `json:"checked" example:"true"`
|
||||
Color string `json:"color" example:"red"`
|
||||
Icon string `json:"icon" example:"📄"`
|
||||
Text string `json:"text" example:"Some text..."` // The text
|
||||
Style string `json:"style" enums:"Paragraph,Header1,Header2,Header3,Header4,Quote,Code,Title,Checkbox,Marked,Numbered,Toggle,Description,Callout" example:"Paragraph"` // The style of the text
|
||||
Checked bool `json:"checked" example:"true"` // Whether the text is checked
|
||||
Color string `json:"color" example:"red"` // The color of the text
|
||||
Icon util.Icon `json:"icon"` // The icon of the text
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Hash string `json:"hash"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Mime string `json:"mime"`
|
||||
Size int `json:"size"`
|
||||
AddedAt int `json:"added_at"`
|
||||
TargetObjectId string `json:"target_object_id"`
|
||||
State string `json:"state"`
|
||||
Style string `json:"style"`
|
||||
Hash string `json:"hash"` // The hash of the file
|
||||
Name string `json:"name"` // The name of the file
|
||||
Type string `json:"type"` // The type of the file
|
||||
Mime string `json:"mime"` // The mime of the file
|
||||
Size int `json:"size"` // The size of the file
|
||||
AddedAt int `json:"added_at"` // The added at of the file
|
||||
TargetObjectId string `json:"target_object_id"` // The target object id of the file
|
||||
State string `json:"state"` // The state of the file
|
||||
Style string `json:"style"` // The style of the file
|
||||
}
|
||||
|
||||
type Detail struct {
|
||||
Id string `json:"id" enums:"last_modified_date,last_modified_by,created_date,created_by,last_opened_date,tags" example:"last_modified_date"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
type Property struct {
|
||||
Id string `json:"id" example:"last_modified_date"` // The id of the property
|
||||
Name string `json:"name" example:"Last modified date"` // The name of the property
|
||||
Format string `json:"format" example:"date" enums:"text,number,select,multi_select,date,file,checkbox,url,email,phone,object"` // The format of the property
|
||||
Text *string `json:"text,omitempty" example:"Some text..."` // The text value, if applicable
|
||||
Number *float64 `json:"number,omitempty" example:"42"` // The number value, if applicable
|
||||
Select *Tag `json:"select,omitempty"` // The select value, if applicable
|
||||
MultiSelect []Tag `json:"multi_select,omitempty"` // The multi-select values, if applicable
|
||||
Date *string `json:"date,omitempty" example:"2025-02-14T12:34:56Z"` // The date value, if applicable
|
||||
File []string `json:"file,omitempty" example:"['fileId']"` // The file references, if applicable
|
||||
Checkbox *bool `json:"checkbox,omitempty" example:"true" enum:"true,false"` // The checkbox value, if applicable
|
||||
Url *string `json:"url,omitempty" example:"https://example.com"` // The url value, if applicable
|
||||
Email *string `json:"email,omitempty" example:"example@example.com"` // The email value, if applicable
|
||||
Phone *string `json:"phone,omitempty" example:"+1234567890"` // The phone number value, if applicable
|
||||
Object []string `json:"object,omitempty" example:"['objectId']"` // The object references, if applicable
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Id string `json:"id" example:"bafyreiaixlnaefu3ci22zdenjhsdlyaeeoyjrsid5qhfeejzlccijbj7sq"`
|
||||
Name string `json:"name" example:"Tag Name"`
|
||||
Color string `json:"color" example:"yellow"`
|
||||
Id string `json:"id" example:"bafyreiaixlnaefu3ci22zdenjhsdlyaeeoyjrsid5qhfeejzlccijbj7sq"` // The id of the tag
|
||||
Name string `json:"name" example:"in-progress"` // The name of the tag
|
||||
Color string `json:"color" example:"yellow"` // The color of the tag
|
||||
}
|
||||
|
||||
type TypeResponse struct {
|
||||
Type Type `json:"type"`
|
||||
Type Type `json:"type"` // The type
|
||||
}
|
||||
|
||||
type Type struct {
|
||||
Type string `json:"type" example:"type"`
|
||||
Id string `json:"id" example:"bafyreigyb6l5szohs32ts26ku2j42yd65e6hqy2u3gtzgdwqv6hzftsetu"`
|
||||
UniqueKey string `json:"unique_key" example:"ot-page"`
|
||||
Name string `json:"name" example:"Page"`
|
||||
Icon string `json:"icon" example:"📄"`
|
||||
RecommendedLayout string `json:"recommended_layout" example:"todo"`
|
||||
Object string `json:"object" example:"type"` // The data model of the object
|
||||
Id string `json:"id" example:"bafyreigyb6l5szohs32ts26ku2j42yd65e6hqy2u3gtzgdwqv6hzftsetu"` // The id of the type (which is unique across spaces)
|
||||
Key string `json:"key" example:"ot-page"` // The key of the type (can be the same across spaces for known types)
|
||||
Name string `json:"name" example:"Page"` // The name of the type
|
||||
Icon util.Icon `json:"icon"` // The icon of the type
|
||||
Archived bool `json:"archived" example:"false"` // Whether the type is archived
|
||||
RecommendedLayout string `json:"recommended_layout" example:"todo"` // The recommended layout of the type
|
||||
}
|
||||
|
||||
type TemplateResponse struct {
|
||||
Template Template `json:"template"`
|
||||
Template Template `json:"template"` // The template
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Type string `json:"type" example:"template"`
|
||||
Id string `json:"id" example:"bafyreictrp3obmnf6dwejy5o4p7bderaaia4bdg2psxbfzf44yya5uutge"`
|
||||
Name string `json:"name" example:"Template Name"`
|
||||
Icon string `json:"icon" example:"📄"`
|
||||
Object string `json:"object" example:"template"` // The data model of the object
|
||||
Id string `json:"id" example:"bafyreictrp3obmnf6dwejy5o4p7bderaaia4bdg2psxbfzf44yya5uutge"` // The id of the template
|
||||
Name string `json:"name" example:"My template"` // The name of the template
|
||||
Icon util.Icon `json:"icon"` // The icon of the template
|
||||
Archived bool `json:"archived" example:"false"` // Whether the template is archived
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/iancoleman/strcase"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
|
@ -20,26 +22,75 @@ import (
|
|||
var (
|
||||
// objects
|
||||
ErrObjectNotFound = errors.New("object not found")
|
||||
ErrObjectDeleted = errors.New("object deleted")
|
||||
ErrFailedRetrieveObject = errors.New("failed to retrieve object")
|
||||
ErrFailedRetrieveObjects = errors.New("failed to retrieve list of objects")
|
||||
ErrFailedDeleteObject = errors.New("failed to delete object")
|
||||
ErrFailedCreateObject = errors.New("failed to create object")
|
||||
ErrInputMissingSource = errors.New("source is missing for bookmark")
|
||||
ErrFailedSetRelationFeatured = errors.New("failed to set relation featured")
|
||||
ErrFailedFetchBookmark = errors.New("failed to fetch bookmark")
|
||||
ErrIconNameColorNotSupported = errors.New("icon name and color are not supported for object")
|
||||
ErrFailedSetPropertyFeatured = errors.New("failed to set property featured")
|
||||
ErrFailedCreateBookmark = errors.New("failed to fetch bookmark")
|
||||
ErrFailedCreateBlock = errors.New("failed to create block")
|
||||
ErrFailedPasteBody = errors.New("failed to paste body")
|
||||
|
||||
// types
|
||||
ErrFailedRetrieveTypes = errors.New("failed to retrieve types")
|
||||
ErrTypeNotFound = errors.New("type not found")
|
||||
ErrTypeDeleted = errors.New("type deleted")
|
||||
ErrFailedRetrieveType = errors.New("failed to retrieve type")
|
||||
ErrFailedRetrieveTemplateType = errors.New("failed to retrieve template type")
|
||||
ErrTemplateTypeNotFound = errors.New("template type not found")
|
||||
ErrFailedRetrieveTemplate = errors.New("failed to retrieve template")
|
||||
ErrFailedRetrieveTemplates = errors.New("failed to retrieve templates")
|
||||
ErrTemplateNotFound = errors.New("template not found")
|
||||
ErrTemplateDeleted = errors.New("template deleted")
|
||||
)
|
||||
|
||||
var excludedSystemProperties = map[string]bool{
|
||||
bundle.RelationKeyId.String(): true,
|
||||
bundle.RelationKeySpaceId.String(): true,
|
||||
bundle.RelationKeyName.String(): true,
|
||||
bundle.RelationKeyIconEmoji.String(): true,
|
||||
bundle.RelationKeyIconImage.String(): true,
|
||||
bundle.RelationKeyType.String(): true,
|
||||
bundle.RelationKeyResolvedLayout.String(): true,
|
||||
bundle.RelationKeyIsFavorite.String(): true,
|
||||
bundle.RelationKeyIsArchived.String(): true,
|
||||
bundle.RelationKeyIsDeleted.String(): true,
|
||||
bundle.RelationKeyIsHidden.String(): true,
|
||||
bundle.RelationKeyWorkspaceId.String(): true,
|
||||
bundle.RelationKeyInternalFlags.String(): true,
|
||||
bundle.RelationKeyRestrictions.String(): true,
|
||||
bundle.RelationKeyOrigin.String(): true,
|
||||
bundle.RelationKeySnippet.String(): true,
|
||||
bundle.RelationKeySyncStatus.String(): true,
|
||||
bundle.RelationKeySyncError.String(): true,
|
||||
bundle.RelationKeySyncDate.String(): true,
|
||||
bundle.RelationKeyCoverId.String(): true,
|
||||
bundle.RelationKeyCoverType.String(): true,
|
||||
bundle.RelationKeyCoverScale.String(): true,
|
||||
bundle.RelationKeyCoverX.String(): true,
|
||||
bundle.RelationKeyCoverY.String(): true,
|
||||
bundle.RelationKeyMentions.String(): true,
|
||||
bundle.RelationKeyOldAnytypeID.String(): true,
|
||||
bundle.RelationKeySource.String(): true,
|
||||
bundle.RelationKeySourceFilePath.String(): true,
|
||||
bundle.RelationKeyImportType.String(): true,
|
||||
bundle.RelationKeyTargetObjectType.String(): true,
|
||||
bundle.RelationKeyFeaturedRelations.String(): true,
|
||||
bundle.RelationKeySetOf.String(): true,
|
||||
bundle.RelationKeyLinks.String(): true,
|
||||
bundle.RelationKeyBacklinks.String(): true,
|
||||
bundle.RelationKeySourceObject.String(): true,
|
||||
bundle.RelationKeyLayoutAlign.String(): true,
|
||||
bundle.RelationKeyIsHiddenDiscovery.String(): true,
|
||||
bundle.RelationKeyLayout.String(): true,
|
||||
bundle.RelationKeyIsReadonly.String(): true,
|
||||
bundle.RelationKeyParticipantStatus.String(): true,
|
||||
bundle.RelationKeyParticipantPermissions.String(): true,
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
ListObjects(ctx context.Context, spaceId string, offset int, limit int) ([]Object, int, bool, error)
|
||||
GetObject(ctx context.Context, spaceId string, objectId string) (Object, error)
|
||||
|
@ -52,12 +103,12 @@ type Service interface {
|
|||
}
|
||||
|
||||
type ObjectService struct {
|
||||
mw service.ClientCommandsServer
|
||||
mw apicore.ClientCommands
|
||||
spaceService *space.SpaceService
|
||||
AccountInfo *model.AccountInfo
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer, spaceService *space.SpaceService) *ObjectService {
|
||||
func NewService(mw apicore.ClientCommands, spaceService *space.SpaceService) *ObjectService {
|
||||
return &ObjectService{mw: mw, spaceService: spaceService}
|
||||
}
|
||||
|
||||
|
@ -67,7 +118,8 @@ func (s *ObjectService) ListObjects(ctx context.Context, spaceId string, offset
|
|||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
|
@ -80,6 +132,18 @@ func (s *ObjectService) ListObjects(ctx context.Context, spaceId string, offset
|
|||
int(model.ObjectType_participant),
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyIsHidden.String(),
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{{
|
||||
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
|
||||
|
@ -117,31 +181,35 @@ func (s *ObjectService) GetObject(ctx context.Context, spaceId string, objectId
|
|||
ObjectId: objectId,
|
||||
})
|
||||
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Object{}, ErrObjectNotFound
|
||||
if resp.Error != nil {
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Object{}, ErrObjectNotFound
|
||||
}
|
||||
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_OBJECT_DELETED {
|
||||
return Object{}, ErrObjectDeleted
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Object{}, ErrFailedRetrieveObject
|
||||
}
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Object{}, ErrFailedRetrieveObject
|
||||
}
|
||||
|
||||
icon := util.GetIconFromEmojiOrImage(s.AccountInfo, resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconImage.String()].GetStringValue())
|
||||
objectTypeName, err := util.ResolveTypeToName(s.mw, spaceId, resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyType.String()].GetStringValue())
|
||||
if err != nil {
|
||||
return Object{}, err
|
||||
}
|
||||
details := resp.ObjectView.Details[0].Details.Fields
|
||||
icon := util.GetIcon(s.AccountInfo, details[bundle.RelationKeyIconEmoji.String()].GetStringValue(), details[bundle.RelationKeyIconImage.String()].GetStringValue(), details[bundle.RelationKeyIconName.String()].GetStringValue(), details[bundle.RelationKeyIconOption.String()].GetNumberValue())
|
||||
|
||||
object := Object{
|
||||
Type: objectTypeName,
|
||||
Id: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
Name: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: icon,
|
||||
Snippet: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySnippet.String()].GetStringValue(),
|
||||
Layout: model.ObjectTypeLayout_name[int32(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyLayout.String()].GetNumberValue())],
|
||||
SpaceId: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(),
|
||||
RootId: resp.ObjectView.RootId,
|
||||
Blocks: s.GetBlocks(resp),
|
||||
Details: s.GetDetails(resp),
|
||||
Object: "object",
|
||||
Id: details[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
Name: details[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: icon,
|
||||
Archived: details[bundle.RelationKeyIsArchived.String()].GetBoolValue(),
|
||||
SpaceId: details[bundle.RelationKeySpaceId.String()].GetStringValue(),
|
||||
Snippet: details[bundle.RelationKeySnippet.String()].GetStringValue(),
|
||||
Layout: model.ObjectTypeLayout_name[int32(details[bundle.RelationKeyResolvedLayout.String()].GetNumberValue())],
|
||||
Type: s.GetTypeFromDetails(details[bundle.RelationKeyType.String()].GetStringValue(), resp.ObjectView.Details),
|
||||
Blocks: s.getBlocks(resp),
|
||||
Properties: s.getProperties(resp),
|
||||
}
|
||||
|
||||
return object, nil
|
||||
|
@ -168,62 +236,54 @@ func (s *ObjectService) DeleteObject(ctx context.Context, spaceId string, object
|
|||
|
||||
// CreateObject creates a new object in a specific space.
|
||||
func (s *ObjectService) CreateObject(ctx context.Context, spaceId string, request CreateObjectRequest) (Object, error) {
|
||||
if request.ObjectTypeUniqueKey == "ot-bookmark" && request.Source == "" {
|
||||
return Object{}, ErrInputMissingSource
|
||||
details, err := s.buildObjectDetails(request)
|
||||
if err != nil {
|
||||
return Object{}, err
|
||||
}
|
||||
|
||||
details := &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(request.Name),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(request.Icon),
|
||||
bundle.RelationKeyDescription.String(): pbtypes.String(request.Description),
|
||||
bundle.RelationKeySource.String(): pbtypes.String(request.Source),
|
||||
bundle.RelationKeyOrigin.String(): pbtypes.Int64(int64(model.ObjectOrigin_api)),
|
||||
},
|
||||
}
|
||||
var objectId string
|
||||
if request.TypeKey == "ot-bookmark" {
|
||||
resp := s.mw.ObjectCreateBookmark(ctx, &pb.RpcObjectCreateBookmarkRequest{
|
||||
Details: details,
|
||||
SpaceId: spaceId,
|
||||
TemplateId: request.TemplateId,
|
||||
})
|
||||
|
||||
resp := s.mw.ObjectCreate(ctx, &pb.RpcObjectCreateRequest{
|
||||
Details: details,
|
||||
TemplateId: request.TemplateId,
|
||||
SpaceId: spaceId,
|
||||
ObjectTypeUniqueKey: request.ObjectTypeUniqueKey,
|
||||
WithChat: false,
|
||||
})
|
||||
if resp.Error.Code != pb.RpcObjectCreateBookmarkResponseError_NULL {
|
||||
return Object{}, ErrFailedCreateBookmark
|
||||
}
|
||||
objectId = resp.ObjectId
|
||||
} else {
|
||||
resp := s.mw.ObjectCreate(ctx, &pb.RpcObjectCreateRequest{
|
||||
Details: details,
|
||||
TemplateId: request.TemplateId,
|
||||
SpaceId: spaceId,
|
||||
ObjectTypeUniqueKey: request.TypeKey,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectCreateResponseError_NULL {
|
||||
return Object{}, ErrFailedCreateObject
|
||||
if resp.Error.Code != pb.RpcObjectCreateResponseError_NULL {
|
||||
return Object{}, ErrFailedCreateObject
|
||||
}
|
||||
objectId = resp.ObjectId
|
||||
}
|
||||
|
||||
// ObjectRelationAddFeatured if description was set
|
||||
if request.Description != "" {
|
||||
relAddFeatResp := s.mw.ObjectRelationAddFeatured(ctx, &pb.RpcObjectRelationAddFeaturedRequest{
|
||||
ContextId: resp.ObjectId,
|
||||
ContextId: objectId,
|
||||
Relations: []string{bundle.RelationKeyDescription.String()},
|
||||
})
|
||||
|
||||
if relAddFeatResp.Error.Code != pb.RpcObjectRelationAddFeaturedResponseError_NULL {
|
||||
object, _ := s.GetObject(ctx, spaceId, resp.ObjectId) // nolint:errcheck
|
||||
return object, ErrFailedSetRelationFeatured
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectBookmarkFetch after creating a bookmark object
|
||||
if request.ObjectTypeUniqueKey == "ot-bookmark" {
|
||||
bookmarkResp := s.mw.ObjectBookmarkFetch(ctx, &pb.RpcObjectBookmarkFetchRequest{
|
||||
ContextId: resp.ObjectId,
|
||||
Url: request.Source,
|
||||
})
|
||||
|
||||
if bookmarkResp.Error.Code != pb.RpcObjectBookmarkFetchResponseError_NULL {
|
||||
object, _ := s.GetObject(ctx, spaceId, resp.ObjectId) // nolint:errcheck
|
||||
return object, ErrFailedFetchBookmark
|
||||
object, _ := s.GetObject(ctx, spaceId, objectId) // nolint:errcheck
|
||||
return object, ErrFailedSetPropertyFeatured
|
||||
}
|
||||
}
|
||||
|
||||
// First call BlockCreate at top, then BlockPaste to paste the body
|
||||
if request.Body != "" {
|
||||
blockCreateResp := s.mw.BlockCreate(ctx, &pb.RpcBlockCreateRequest{
|
||||
ContextId: resp.ObjectId,
|
||||
ContextId: objectId,
|
||||
TargetId: "",
|
||||
Block: &model.Block{
|
||||
Id: "",
|
||||
|
@ -245,23 +305,55 @@ func (s *ObjectService) CreateObject(ctx context.Context, spaceId string, reques
|
|||
})
|
||||
|
||||
if blockCreateResp.Error.Code != pb.RpcBlockCreateResponseError_NULL {
|
||||
object, _ := s.GetObject(ctx, spaceId, resp.ObjectId) // nolint:errcheck
|
||||
return object, ErrFailedCreateObject
|
||||
object, _ := s.GetObject(ctx, spaceId, objectId) // nolint:errcheck
|
||||
return object, ErrFailedCreateBlock
|
||||
}
|
||||
|
||||
blockPasteResp := s.mw.BlockPaste(ctx, &pb.RpcBlockPasteRequest{
|
||||
ContextId: resp.ObjectId,
|
||||
ContextId: objectId,
|
||||
FocusedBlockId: blockCreateResp.BlockId,
|
||||
TextSlot: request.Body,
|
||||
})
|
||||
|
||||
if blockPasteResp.Error.Code != pb.RpcBlockPasteResponseError_NULL {
|
||||
object, _ := s.GetObject(ctx, spaceId, resp.ObjectId) // nolint:errcheck
|
||||
object, _ := s.GetObject(ctx, spaceId, objectId) // nolint:errcheck
|
||||
return object, ErrFailedPasteBody
|
||||
}
|
||||
}
|
||||
|
||||
return s.GetObject(ctx, spaceId, resp.ObjectId)
|
||||
return s.GetObject(ctx, spaceId, objectId)
|
||||
}
|
||||
|
||||
// buildObjectDetails extracts the details structure from the CreateObjectRequest.
|
||||
func (s *ObjectService) buildObjectDetails(request CreateObjectRequest) (*types.Struct, error) {
|
||||
// Validate bookmark source
|
||||
if request.TypeKey == "ot-bookmark" && request.Source == "" {
|
||||
return nil, ErrInputMissingSource
|
||||
}
|
||||
|
||||
// Validate icon: only allow either emoji or file, and disallow name and color fields.
|
||||
if request.Icon.Name != nil || request.Icon.Color != nil {
|
||||
return nil, ErrIconNameColorNotSupported
|
||||
}
|
||||
|
||||
iconFields := map[string]*types.Value{}
|
||||
if request.Icon.Emoji != nil {
|
||||
iconFields[bundle.RelationKeyIconEmoji.String()] = pbtypes.String(*request.Icon.Emoji)
|
||||
} else if request.Icon.File != nil {
|
||||
iconFields[bundle.RelationKeyIconImage.String()] = pbtypes.String(*request.Icon.File)
|
||||
}
|
||||
|
||||
fields := map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(request.Name),
|
||||
bundle.RelationKeyDescription.String(): pbtypes.String(request.Description),
|
||||
bundle.RelationKeySource.String(): pbtypes.String(request.Source),
|
||||
bundle.RelationKeyOrigin.String(): pbtypes.Int64(int64(model.ObjectOrigin_api)),
|
||||
}
|
||||
for k, v := range iconFields {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
return &types.Struct{Fields: fields}, nil
|
||||
}
|
||||
|
||||
// ListTypes returns a paginated list of types in a specific space.
|
||||
|
@ -270,11 +362,13 @@ func (s *ObjectService) ListTypes(ctx context.Context, spaceId string, offset in
|
|||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_objectType)),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyIsHidden.String(),
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
|
@ -286,7 +380,7 @@ func (s *ObjectService) ListTypes(ctx context.Context, spaceId string, offset in
|
|||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyUniqueKey.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyRecommendedLayout.String()},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyUniqueKey.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconName.String(), bundle.RelationKeyIconOption.String(), bundle.RelationKeyRecommendedLayout.String(), bundle.RelationKeyIsArchived.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
|
@ -299,11 +393,12 @@ func (s *ObjectService) ListTypes(ctx context.Context, spaceId string, offset in
|
|||
|
||||
for _, record := range paginatedTypes {
|
||||
types = append(types, Type{
|
||||
Type: "type",
|
||||
Object: "type",
|
||||
Id: record.Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
UniqueKey: record.Fields[bundle.RelationKeyUniqueKey.String()].GetStringValue(),
|
||||
Key: record.Fields[bundle.RelationKeyUniqueKey.String()].GetStringValue(),
|
||||
Name: record.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: record.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(),
|
||||
Icon: util.GetIcon(s.AccountInfo, record.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), "", record.Fields[bundle.RelationKeyIconName.String()].GetStringValue(), record.Fields[bundle.RelationKeyIconOption.String()].GetNumberValue()),
|
||||
Archived: record.Fields[bundle.RelationKeyIsArchived.String()].GetBoolValue(),
|
||||
RecommendedLayout: model.ObjectTypeLayout_name[int32(record.Fields[bundle.RelationKeyRecommendedLayout.String()].GetNumberValue())],
|
||||
})
|
||||
}
|
||||
|
@ -317,21 +412,29 @@ func (s *ObjectService) GetType(ctx context.Context, spaceId string, typeId stri
|
|||
ObjectId: typeId,
|
||||
})
|
||||
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Type{}, ErrTypeNotFound
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Type{}, ErrFailedRetrieveType
|
||||
if resp.Error != nil {
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Type{}, ErrTypeNotFound
|
||||
}
|
||||
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_OBJECT_DELETED {
|
||||
return Type{}, ErrTypeDeleted
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Type{}, ErrFailedRetrieveType
|
||||
}
|
||||
}
|
||||
|
||||
details := resp.ObjectView.Details[0].Details.Fields
|
||||
return Type{
|
||||
Type: "type",
|
||||
Object: "type",
|
||||
Id: typeId,
|
||||
UniqueKey: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyUniqueKey.String()].GetStringValue(),
|
||||
Name: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(),
|
||||
RecommendedLayout: model.ObjectTypeLayout_name[int32(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyRecommendedLayout.String()].GetNumberValue())],
|
||||
Key: details[bundle.RelationKeyUniqueKey.String()].GetStringValue(),
|
||||
Name: details[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: util.GetIcon(s.AccountInfo, details[bundle.RelationKeyIconEmoji.String()].GetStringValue(), "", details[bundle.RelationKeyIconName.String()].GetStringValue(), details[bundle.RelationKeyIconOption.String()].GetNumberValue()),
|
||||
Archived: details[bundle.RelationKeyIsArchived.String()].GetBoolValue(),
|
||||
RecommendedLayout: model.ObjectTypeLayout_name[int32(details[bundle.RelationKeyRecommendedLayout.String()].GetNumberValue())],
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -342,6 +445,7 @@ func (s *ObjectService) ListTemplates(ctx context.Context, spaceId string, typeI
|
|||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyUniqueKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
|
@ -364,12 +468,13 @@ func (s *ObjectService) ListTemplates(ctx context.Context, spaceId string, typeI
|
|||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyType.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(templateTypeId),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyTargetObjectType.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String()},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyTargetObjectType.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIsArchived.String()},
|
||||
})
|
||||
|
||||
if templateObjectsResp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
|
@ -399,10 +504,11 @@ func (s *ObjectService) ListTemplates(ctx context.Context, spaceId string, typeI
|
|||
}
|
||||
|
||||
templates = append(templates, Template{
|
||||
Type: "template",
|
||||
Id: templateId,
|
||||
Name: templateResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: templateResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(),
|
||||
Object: "template",
|
||||
Id: templateId,
|
||||
Name: templateResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: util.GetIcon(s.AccountInfo, templateResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), "", "", 0),
|
||||
Archived: templateResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIsArchived.String()].GetBoolValue(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -416,89 +522,299 @@ func (s *ObjectService) GetTemplate(ctx context.Context, spaceId string, typeId
|
|||
ObjectId: templateId,
|
||||
})
|
||||
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Template{}, ErrTemplateNotFound
|
||||
}
|
||||
if resp.Error != nil {
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_NOT_FOUND {
|
||||
return Template{}, ErrTemplateNotFound
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Template{}, ErrFailedRetrieveTemplate
|
||||
if resp.Error.Code == pb.RpcObjectShowResponseError_OBJECT_DELETED {
|
||||
return Template{}, ErrTemplateDeleted
|
||||
}
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Template{}, ErrFailedRetrieveTemplate
|
||||
}
|
||||
}
|
||||
|
||||
return Template{
|
||||
Type: "template",
|
||||
Id: templateId,
|
||||
Name: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(),
|
||||
Object: "template",
|
||||
Id: templateId,
|
||||
Name: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: util.GetIcon(s.AccountInfo, resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), "", "", 0),
|
||||
Archived: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIsArchived.String()].GetBoolValue(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetDetails returns the list of details from the ObjectShowResponse.
|
||||
func (s *ObjectService) GetDetails(resp *pb.RpcObjectShowResponse) []Detail {
|
||||
creator := resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyCreator.String()].GetStringValue()
|
||||
lastModifiedBy := resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyLastModifiedBy.String()].GetStringValue()
|
||||
|
||||
var creatorId, lastModifiedById string
|
||||
for _, detail := range resp.ObjectView.Details {
|
||||
if detail.Id == creator {
|
||||
creatorId = detail.Id
|
||||
}
|
||||
if detail.Id == lastModifiedBy {
|
||||
lastModifiedById = detail.Id
|
||||
// GetTypeFromDetails returns the type from the details of the ObjectShowResponse.
|
||||
func (s *ObjectService) GetTypeFromDetails(typeId string, details []*model.ObjectViewDetailsSet) Type {
|
||||
var objectTypeDetail *types.Struct
|
||||
for _, detail := range details {
|
||||
if detail.Id == typeId {
|
||||
objectTypeDetail = detail.GetDetails()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return []Detail{
|
||||
{
|
||||
Id: "last_modified_date",
|
||||
Details: map[string]interface{}{
|
||||
"last_modified_date": PosixToISO8601(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyLastModifiedDate.String()].GetNumberValue()),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "last_modified_by",
|
||||
Details: map[string]interface{}{
|
||||
"details": s.spaceService.GetParticipantDetails(s.mw, resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(), lastModifiedById),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "created_date",
|
||||
Details: map[string]interface{}{
|
||||
"created_date": PosixToISO8601(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyCreatedDate.String()].GetNumberValue()),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "created_by",
|
||||
Details: map[string]interface{}{
|
||||
"details": s.spaceService.GetParticipantDetails(s.mw, resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(), creatorId),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "last_opened_date",
|
||||
Details: map[string]interface{}{
|
||||
"last_opened_date": PosixToISO8601(resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyLastOpenedDate.String()].GetNumberValue()),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "tags",
|
||||
Details: map[string]interface{}{
|
||||
"tags": s.getTags(resp),
|
||||
},
|
||||
},
|
||||
if objectTypeDetail == nil {
|
||||
return Type{}
|
||||
}
|
||||
|
||||
return Type{
|
||||
Object: "type",
|
||||
Id: typeId,
|
||||
Key: objectTypeDetail.Fields[bundle.RelationKeyUniqueKey.String()].GetStringValue(),
|
||||
Name: objectTypeDetail.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: util.GetIcon(s.AccountInfo, objectTypeDetail.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), "", objectTypeDetail.Fields[bundle.RelationKeyIconName.String()].GetStringValue(), objectTypeDetail.Fields[bundle.RelationKeyIconOption.String()].GetNumberValue()),
|
||||
RecommendedLayout: model.ObjectTypeLayout_name[int32(objectTypeDetail.Fields[bundle.RelationKeyRecommendedLayout.String()].GetNumberValue())],
|
||||
}
|
||||
}
|
||||
|
||||
// getProperties returns a list of properties by iterating over all properties found in the RelationLinks and mapping their format and value.
|
||||
func (s *ObjectService) getProperties(resp *pb.RpcObjectShowResponse) []Property {
|
||||
propertyFormatMap := s.getPropertyFormatMap(resp.ObjectView.RelationLinks)
|
||||
linkedProperties := resp.ObjectView.RelationLinks
|
||||
primaryDetailFields := resp.ObjectView.Details[0].Details.Fields
|
||||
|
||||
properties := make([]Property, 0, len(linkedProperties))
|
||||
for _, r := range linkedProperties {
|
||||
key := r.Key
|
||||
if _, isExcluded := excludedSystemProperties[key]; isExcluded {
|
||||
continue
|
||||
}
|
||||
if _, ok := primaryDetailFields[key]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
id, name := s.getProperty(key, resp)
|
||||
format := propertyFormatMap[key]
|
||||
convertedVal := s.convertValue(key, primaryDetailFields[key], format, resp.ObjectView.Details)
|
||||
|
||||
if s.isMissingObject(convertedVal) {
|
||||
continue
|
||||
}
|
||||
|
||||
properties = append(properties, s.buildProperty(id, name, format, convertedVal))
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
// isMissingObject returns true if val indicates a "_missing_object" placeholder.
|
||||
func (s *ObjectService) isMissingObject(val interface{}) bool {
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return v == "_missing_object"
|
||||
case []interface{}:
|
||||
if len(v) == 1 {
|
||||
if str, ok := v[0].(string); ok {
|
||||
return str == "_missing_object"
|
||||
}
|
||||
}
|
||||
case Tag:
|
||||
return v.Id == "_missing_object"
|
||||
case []Tag:
|
||||
if len(v) == 1 && v[0].Id == "_missing_object" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// buildProperty creates a Property based on the format and converted value.
|
||||
func (s *ObjectService) buildProperty(id string, name string, format string, val interface{}) Property {
|
||||
prop := &Property{
|
||||
Id: id,
|
||||
Name: name,
|
||||
Format: format,
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "text":
|
||||
if str, ok := val.(string); ok {
|
||||
prop.Text = &str
|
||||
}
|
||||
case "number":
|
||||
if num, ok := val.(float64); ok {
|
||||
prop.Number = &num
|
||||
}
|
||||
case "select":
|
||||
if sel, ok := val.(Tag); ok {
|
||||
prop.Select = &sel
|
||||
}
|
||||
case "multi_select":
|
||||
if ms, ok := val.([]Tag); ok {
|
||||
prop.MultiSelect = ms
|
||||
}
|
||||
case "date":
|
||||
if dateStr, ok := val.(string); ok {
|
||||
prop.Date = &dateStr
|
||||
}
|
||||
case "file":
|
||||
if fileList, ok := val.([]interface{}); ok {
|
||||
var files []string
|
||||
for _, v := range fileList {
|
||||
if str, ok := v.(string); ok {
|
||||
files = append(files, str)
|
||||
}
|
||||
}
|
||||
prop.File = files
|
||||
}
|
||||
case "checkbox":
|
||||
if cb, ok := val.(bool); ok {
|
||||
prop.Checkbox = &cb
|
||||
}
|
||||
case "url":
|
||||
if urlStr, ok := val.(string); ok {
|
||||
prop.Url = &urlStr
|
||||
}
|
||||
case "email":
|
||||
if email, ok := val.(string); ok {
|
||||
prop.Email = &email
|
||||
}
|
||||
case "phone":
|
||||
if phone, ok := val.(string); ok {
|
||||
prop.Phone = &phone
|
||||
}
|
||||
case "object":
|
||||
if obj, ok := val.(string); ok {
|
||||
prop.Object = []string{obj}
|
||||
} else if objSlice, ok := val.([]interface{}); ok {
|
||||
var objects []string
|
||||
for _, v := range objSlice {
|
||||
if str, ok := v.(string); ok {
|
||||
objects = append(objects, str)
|
||||
}
|
||||
}
|
||||
prop.Object = objects
|
||||
}
|
||||
default:
|
||||
if str, ok := val.(string); ok {
|
||||
prop.Text = &str
|
||||
}
|
||||
}
|
||||
|
||||
return *prop
|
||||
}
|
||||
|
||||
// getProperty returns the property id and name from the ObjectShowResponse.
|
||||
func (s *ObjectService) getProperty(key string, resp *pb.RpcObjectShowResponse) (string, string) {
|
||||
// Handle special cases first
|
||||
switch key {
|
||||
case bundle.RelationKeyCreator.String():
|
||||
return "created_by", "Created By"
|
||||
case bundle.RelationKeyCreatedDate.String():
|
||||
return "created_date", "Created Date"
|
||||
}
|
||||
|
||||
if property, err := bundle.GetRelation(domain.RelationKey(key)); err == nil {
|
||||
return strcase.ToSnake(key), property.Name
|
||||
}
|
||||
|
||||
// Fallback to resolving the property name
|
||||
spaceId := resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue()
|
||||
if name, err2 := util.ResolveRelationKeyToPropertyName(s.mw, spaceId, key); err2 == nil {
|
||||
return key, name
|
||||
}
|
||||
return key, key
|
||||
}
|
||||
|
||||
// convertValue converts a protobuf types.Value into a native Go value.
|
||||
func (s *ObjectService) convertValue(key string, value *types.Value, format string, details []*model.ObjectViewDetailsSet) interface{} {
|
||||
switch kind := value.Kind.(type) {
|
||||
case *types.Value_NullValue:
|
||||
return nil
|
||||
case *types.Value_NumberValue:
|
||||
if format == "date" {
|
||||
return time.Unix(int64(kind.NumberValue), 0).UTC().Format(time.RFC3339)
|
||||
}
|
||||
return kind.NumberValue
|
||||
case *types.Value_StringValue:
|
||||
// TODO: investigate how this is possible? select option not list and not returned in further details
|
||||
if format == "select" {
|
||||
return s.resolveTag(details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(), kind.StringValue)
|
||||
}
|
||||
if format == "multi_select" {
|
||||
return []Tag{s.resolveTag(details[0].Details.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(), kind.StringValue)}
|
||||
}
|
||||
return kind.StringValue
|
||||
case *types.Value_BoolValue:
|
||||
return kind.BoolValue
|
||||
case *types.Value_StructValue:
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range kind.StructValue.Fields {
|
||||
m[k] = s.convertValue(key, v, format, details)
|
||||
}
|
||||
return m
|
||||
case *types.Value_ListValue:
|
||||
var list []interface{}
|
||||
for _, v := range kind.ListValue.Values {
|
||||
list = append(list, s.convertValue(key, v, format, details))
|
||||
}
|
||||
if format == "select" {
|
||||
tags := s.getTags(key, details)
|
||||
if len(tags) > 0 {
|
||||
return tags[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if format == "multi_select" {
|
||||
return s.getTags(key, details)
|
||||
}
|
||||
return list
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getPropertyFormatMap returns the map of property key to property format from the ObjectShowResponse.
|
||||
func (s *ObjectService) getPropertyFormatMap(propertyLinks []*model.RelationLink) map[string]string {
|
||||
propertyFormatToName := make(map[int32]string, len(model.RelationFormat_name))
|
||||
for k := range model.RelationFormat_name {
|
||||
propertyFormatToName[k] = s.MapRelationFormat(model.RelationFormat(k))
|
||||
}
|
||||
|
||||
propertyFormatMap := map[string]string{}
|
||||
for _, detail := range propertyLinks {
|
||||
propertyFormatMap[detail.Key] = propertyFormatToName[int32(detail.Format)]
|
||||
}
|
||||
|
||||
return propertyFormatMap
|
||||
}
|
||||
|
||||
// TODO: remove once bug of select option not being returned in details is fixed
|
||||
func (s *ObjectService) resolveTag(spaceId, tagId string) Tag {
|
||||
if tagId == "" {
|
||||
return Tag{}
|
||||
}
|
||||
|
||||
resp := s.mw.ObjectShow(context.Background(), &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: tagId,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Tag{}
|
||||
}
|
||||
|
||||
return Tag{
|
||||
Id: tagId,
|
||||
Name: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Color: resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyRelationOptionColor.String()].GetStringValue(),
|
||||
}
|
||||
}
|
||||
|
||||
// getTags returns the list of tags from the ObjectShowResponse
|
||||
func (s *ObjectService) getTags(resp *pb.RpcObjectShowResponse) []Tag {
|
||||
func (s *ObjectService) getTags(key string, details []*model.ObjectViewDetailsSet) []Tag {
|
||||
tags := []Tag{}
|
||||
|
||||
tagField, ok := resp.ObjectView.Details[0].Details.Fields["tag"]
|
||||
tagField, ok := details[0].Details.Fields[key]
|
||||
if !ok || tagField.GetListValue() == nil {
|
||||
return tags
|
||||
}
|
||||
|
||||
for _, tagId := range tagField.GetListValue().Values {
|
||||
id := tagId.GetStringValue()
|
||||
for _, detail := range resp.ObjectView.Details {
|
||||
for _, detail := range details {
|
||||
if detail.Id == id {
|
||||
tags = append(tags, Tag{
|
||||
Id: id,
|
||||
|
@ -512,13 +828,14 @@ func (s *ObjectService) getTags(resp *pb.RpcObjectShowResponse) []Tag {
|
|||
return tags
|
||||
}
|
||||
|
||||
// GetBlocks returns the list of blocks from the ObjectShowResponse.
|
||||
func (s *ObjectService) GetBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
||||
// getBlocks returns the list of blocks from the ObjectShowResponse.
|
||||
func (s *ObjectService) getBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
||||
blocks := []Block{}
|
||||
|
||||
for _, block := range resp.ObjectView.Blocks {
|
||||
var text *Text
|
||||
var file *File
|
||||
var property *Property
|
||||
|
||||
switch content := block.Content.(type) {
|
||||
case *model.BlockContentOfText:
|
||||
|
@ -527,7 +844,7 @@ func (s *ObjectService) GetBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
|||
Style: model.BlockContentTextStyle_name[int32(content.Text.Style)],
|
||||
Checked: content.Text.Checked,
|
||||
Color: content.Text.Color,
|
||||
Icon: util.GetIconFromEmojiOrImage(s.AccountInfo, content.Text.IconEmoji, content.Text.IconImage),
|
||||
Icon: util.GetIcon(s.AccountInfo, content.Text.IconEmoji, content.Text.IconImage, "", 0),
|
||||
}
|
||||
case *model.BlockContentOfFile:
|
||||
file = &File{
|
||||
|
@ -541,8 +858,13 @@ func (s *ObjectService) GetBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
|||
State: model.BlockContentFileState_name[int32(content.File.State)],
|
||||
Style: model.BlockContentFileStyle_name[int32(content.File.Style)],
|
||||
}
|
||||
// TODO: other content types?
|
||||
case *model.BlockContentOfRelation:
|
||||
property = &Property{
|
||||
// TODO: is it sufficient to return the id only?
|
||||
Id: content.Relation.Key,
|
||||
}
|
||||
}
|
||||
// TODO: other content types?
|
||||
|
||||
blocks = append(blocks, Block{
|
||||
Id: block.Id,
|
||||
|
@ -552,13 +874,25 @@ func (s *ObjectService) GetBlocks(resp *pb.RpcObjectShowResponse) []Block {
|
|||
VerticalAlign: model.BlockVerticalAlign_name[int32(block.VerticalAlign)],
|
||||
Text: text,
|
||||
File: file,
|
||||
Property: property,
|
||||
})
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
func PosixToISO8601(posix float64) string {
|
||||
t := time.Unix(int64(posix), 0).UTC()
|
||||
return t.Format(time.RFC3339)
|
||||
// MapRelationFormat maps the relation format to a string.
|
||||
func (s *ObjectService) MapRelationFormat(format model.RelationFormat) string {
|
||||
switch format {
|
||||
case model.RelationFormat_longtext:
|
||||
return "text"
|
||||
case model.RelationFormat_shorttext:
|
||||
return "text"
|
||||
case model.RelationFormat_tag:
|
||||
return "multi_select"
|
||||
case model.RelationFormat_status:
|
||||
return "select"
|
||||
default:
|
||||
return strcase.ToSnake(model.RelationFormat_name[int32(format)])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,46 +8,46 @@ import (
|
|||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service/mock_service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
const (
|
||||
offset = 0
|
||||
limit = 100
|
||||
mockedTechSpaceId = "mocked-tech-space-id"
|
||||
gatewayUrl = "http://localhost:31006"
|
||||
mockedSpaceId = "mocked-space-id"
|
||||
mockedObjectId = "mocked-object-id"
|
||||
mockedObjectType = "mocked-object-type"
|
||||
mockedNewObjectId = "mocked-new-object-id"
|
||||
mockedObjectName = "mocked-object-name"
|
||||
mockedObjectSnippet = "mocked-object-snippet"
|
||||
mockedObjectIcon = "🔍"
|
||||
mockedObjectTypeUniqueKey = "ot-page"
|
||||
mockedTypeId = "mocked-type-id"
|
||||
mockedTypeName = "mocked-type-name"
|
||||
mockedTypeUniqueKey = "mocked-type-unique-key"
|
||||
mockedTypeIcon = "📝"
|
||||
mockedTemplateId = "mocked-template-id"
|
||||
mockedTemplateName = "mocked-template-name"
|
||||
mockedTemplateIcon = "📃"
|
||||
offset = 0
|
||||
limit = 100
|
||||
mockedTechSpaceId = "mocked-tech-space-id"
|
||||
gatewayUrl = "http://localhost:31006"
|
||||
mockedSpaceId = "mocked-space-id"
|
||||
mockedObjectId = "mocked-object-id"
|
||||
mockedNewObjectId = "mocked-new-object-id"
|
||||
mockedObjectName = "mocked-object-name"
|
||||
mockedObjectSnippet = "mocked-object-snippet"
|
||||
mockedObjectIcon = "🔍"
|
||||
mockedParticipantId = "mocked-participant-id"
|
||||
mockedTypeKey = "ot-page"
|
||||
mockedTypeId = "mocked-type-id"
|
||||
mockedTypeName = "mocked-type-name"
|
||||
mockedTypeIcon = "📝"
|
||||
mockedTemplateId = "mocked-template-id"
|
||||
mockedTemplateName = "mocked-template-name"
|
||||
mockedTemplateIcon = "📃"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*ObjectService
|
||||
mwMock *mock_service.MockClientCommandsServer
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mw := mock_service.NewMockClientCommandsServer(t)
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
|
||||
spaceService := space.NewService(mw)
|
||||
objectService := NewService(mw, spaceService)
|
||||
spaceService := space.NewService(mwMock)
|
||||
objectService := NewService(mwMock, spaceService)
|
||||
objectService.AccountInfo = &model.AccountInfo{
|
||||
TechSpaceId: mockedTechSpaceId,
|
||||
GatewayUrl: gatewayUrl,
|
||||
|
@ -55,7 +55,7 @@ func newFixture(t *testing.T) *fixture {
|
|||
|
||||
return &fixture{
|
||||
ObjectService: objectService,
|
||||
mwMock: mw,
|
||||
mwMock: mwMock,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,11 +65,12 @@ func TestObjectService_ListObjects(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
// Mock object search
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
|
@ -82,6 +83,18 @@ func TestObjectService_ListObjects(t *testing.T) {
|
|||
int(model.ObjectType_participant),
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyIsHidden.String(),
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{{
|
||||
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
|
||||
|
@ -95,12 +108,12 @@ func TestObjectService_ListObjects(t *testing.T) {
|
|||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeySnippet.String(): pbtypes.String(mockedObjectSnippet),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeySnippet.String(): pbtypes.String(mockedObjectSnippet),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyResolvedLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -116,100 +129,102 @@ func TestObjectService_ListObjects(t *testing.T) {
|
|||
RootId: mockedObjectId,
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedObjectId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeySnippet.String(): pbtypes.String(mockedObjectSnippet),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
bundle.RelationKeyCreatedDate.String(): pbtypes.Float64(888888),
|
||||
bundle.RelationKeyLastModifiedBy.String(): pbtypes.String(mockedParticipantId),
|
||||
bundle.RelationKeyLastModifiedDate.String(): pbtypes.Float64(999999),
|
||||
bundle.RelationKeyCreator.String(): pbtypes.String(mockedParticipantId),
|
||||
bundle.RelationKeyLastOpenedDate.String(): pbtypes.Float64(0),
|
||||
bundle.RelationKeySpaceId.String(): pbtypes.String(mockedSpaceId),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedParticipantId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedParticipantId),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTypeName),
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedTypeKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedTypeIcon),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RelationLinks: []*model.RelationLink{
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedBy.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreatedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreator.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyLastOpenedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyTag.String(),
|
||||
Format: model.RelationFormat_tag,
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock type resolution
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyUniqueKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectType),
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock participant details
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
Keys: []string{
|
||||
bundle.RelationKeyId.String(),
|
||||
bundle.RelationKeyName.String(),
|
||||
bundle.RelationKeyIconEmoji.String(),
|
||||
bundle.RelationKeyIconImage.String(),
|
||||
bundle.RelationKeyIdentity.String(),
|
||||
bundle.RelationKeyGlobalName.String(),
|
||||
bundle.RelationKeyParticipantPermissions.String(),
|
||||
},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
Records: []*types.Struct{
|
||||
{},
|
||||
},
|
||||
}).Twice()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.ListObjects(ctx, mockedSpaceId, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 1)
|
||||
require.Equal(t, mockedObjectType, objects[0].Type)
|
||||
require.Equal(t, mockedTypeId, objects[0].Type.Id)
|
||||
require.Equal(t, mockedTypeName, objects[0].Type.Name)
|
||||
require.Equal(t, mockedTypeKey, objects[0].Type.Key)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedTypeIcon)}, objects[0].Type.Icon)
|
||||
require.Equal(t, mockedObjectId, objects[0].Id)
|
||||
require.Equal(t, mockedObjectName, objects[0].Name)
|
||||
require.Equal(t, mockedObjectSnippet, objects[0].Snippet)
|
||||
require.Equal(t, mockedObjectIcon, objects[0].Icon)
|
||||
require.Equal(t, 6, len(objects[0].Details))
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedObjectIcon)}, objects[0].Icon)
|
||||
require.Equal(t, 5, len(objects[0].Properties))
|
||||
|
||||
for _, detail := range objects[0].Details {
|
||||
for _, detail := range objects[0].Properties {
|
||||
if detail.Id == "created_date" {
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", detail.Details["created_date"])
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", *detail.Date)
|
||||
} else if detail.Id == "created_by" {
|
||||
require.Empty(t, detail.Details["created_by"])
|
||||
require.Equal(t, []string{mockedParticipantId}, detail.Object)
|
||||
} else if detail.Id == "last_modified_date" {
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", detail.Details["last_modified_date"])
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", *detail.Date)
|
||||
} else if detail.Id == "last_modified_by" {
|
||||
require.Empty(t, detail.Details["last_modified_by"])
|
||||
require.Equal(t, []string{mockedParticipantId}, detail.Object)
|
||||
} else if detail.Id == "last_opened_date" {
|
||||
require.Equal(t, "1970-01-01T00:00:00Z", detail.Details["last_opened_date"])
|
||||
} else if detail.Id == "tags" {
|
||||
require.Empty(t, detail.Details["tags"])
|
||||
require.Equal(t, "1970-01-01T00:00:00Z", *detail.Date)
|
||||
} else if detail.Id == "tag" {
|
||||
require.Empty(t, detail.MultiSelect)
|
||||
} else {
|
||||
t.Errorf("unexpected detail id: %s", detail.Id)
|
||||
}
|
||||
|
@ -224,14 +239,52 @@ func TestObjectService_ListObjects(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
// Mock object search
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
int(model.ObjectType_profile),
|
||||
int(model.ObjectType_todo),
|
||||
int(model.ObjectType_note),
|
||||
int(model.ObjectType_bookmark),
|
||||
int(model.ObjectType_set),
|
||||
int(model.ObjectType_collection),
|
||||
int(model.ObjectType_participant),
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyIsHidden.String(),
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{{
|
||||
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Type: model.BlockContentDataviewSort_Desc,
|
||||
Format: model.RelationFormat_longtext,
|
||||
IncludeTime: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_NotSpecified,
|
||||
}},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.ListObjects(ctx, "empty-space", offset, limit)
|
||||
objects, total, hasMore, err := fx.ListObjects(ctx, mockedSpaceId, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
|
@ -257,13 +310,14 @@ func TestObjectService_GetObject(t *testing.T) {
|
|||
RootId: mockedObjectId,
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedObjectId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeySnippet.String(): pbtypes.String(mockedObjectSnippet),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyLastModifiedDate.String(): pbtypes.Float64(999999),
|
||||
bundle.RelationKeyCreatedDate.String(): pbtypes.Float64(888888),
|
||||
bundle.RelationKeyLastOpenedDate.String(): pbtypes.Float64(0),
|
||||
|
@ -271,86 +325,78 @@ func TestObjectService_GetObject(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTypeName),
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedTypeKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedTypeIcon),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RelationLinks: []*model.RelationLink{
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedBy.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreatedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreator.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyLastOpenedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyTag.String(),
|
||||
Format: model.RelationFormat_tag,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Mock type resolution
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyUniqueKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectType),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Mock participant details
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
Keys: []string{
|
||||
bundle.RelationKeyId.String(),
|
||||
bundle.RelationKeyName.String(),
|
||||
bundle.RelationKeyIconEmoji.String(),
|
||||
bundle.RelationKeyIconImage.String(),
|
||||
bundle.RelationKeyIdentity.String(),
|
||||
bundle.RelationKeyGlobalName.String(),
|
||||
bundle.RelationKeyParticipantPermissions.String(),
|
||||
},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
Records: []*types.Struct{
|
||||
{},
|
||||
},
|
||||
}).Twice()
|
||||
|
||||
// when
|
||||
object, err := fx.GetObject(ctx, mockedSpaceId, mockedObjectId)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, mockedObjectType, object.Type)
|
||||
require.Equal(t, "object", object.Object)
|
||||
require.Equal(t, mockedTypeId, object.Type.Id)
|
||||
require.Equal(t, mockedTypeName, object.Type.Name)
|
||||
require.Equal(t, mockedTypeKey, object.Type.Key)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedTypeIcon)}, object.Type.Icon)
|
||||
require.Equal(t, mockedObjectId, object.Id)
|
||||
require.Equal(t, mockedObjectName, object.Name)
|
||||
require.Equal(t, mockedObjectSnippet, object.Snippet)
|
||||
require.Equal(t, mockedObjectName, object.Icon)
|
||||
require.Equal(t, 6, len(object.Details))
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedObjectIcon)}, object.Icon)
|
||||
require.Equal(t, 3, len(object.Properties))
|
||||
|
||||
for _, detail := range object.Details {
|
||||
if detail.Id == "created_date" {
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", detail.Details["created_date"])
|
||||
} else if detail.Id == "created_by" {
|
||||
require.Empty(t, detail.Details["created_by"])
|
||||
} else if detail.Id == "last_modified_date" {
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", detail.Details["last_modified_date"])
|
||||
} else if detail.Id == "last_modified_by" {
|
||||
require.Empty(t, detail.Details["last_modified_by"])
|
||||
} else if detail.Id == "last_opened_date" {
|
||||
require.Equal(t, "1970-01-01T00:00:00Z", detail.Details["last_opened_date"])
|
||||
} else if detail.Id == "tags" {
|
||||
require.Empty(t, detail.Details["tags"])
|
||||
for _, property := range object.Properties {
|
||||
if property.Id == "created_date" {
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", *property.Date)
|
||||
} else if property.Id == "created_by" {
|
||||
require.Empty(t, property.Object)
|
||||
} else if property.Id == "last_modified_date" {
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", *property.Date)
|
||||
} else if property.Id == "last_modified_by" {
|
||||
require.Empty(t, property.Object)
|
||||
} else if property.Id == "last_opened_date" {
|
||||
require.Equal(t, "1970-01-01T00:00:00Z", *property.Date)
|
||||
} else if property.Id == "tag" {
|
||||
require.Empty(t, property.MultiSelect)
|
||||
} else {
|
||||
t.Errorf("unexpected detail id: %s", detail.Id)
|
||||
t.Errorf("unexpected property id: %s", property.Id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -390,9 +436,9 @@ func TestObjectService_CreateObject(t *testing.T) {
|
|||
bundle.RelationKeyOrigin.String(): pbtypes.Int64(int64(model.ObjectOrigin_api)),
|
||||
},
|
||||
},
|
||||
TemplateId: "",
|
||||
TemplateId: mockedTemplateId,
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectTypeUniqueKey: mockedObjectTypeUniqueKey,
|
||||
ObjectTypeUniqueKey: mockedTypeKey,
|
||||
WithChat: false,
|
||||
}).Return(&pb.RpcObjectCreateResponse{
|
||||
ObjectId: mockedNewObjectId,
|
||||
|
@ -416,14 +462,26 @@ func TestObjectService_CreateObject(t *testing.T) {
|
|||
RootId: mockedNewObjectId,
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedNewObjectId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedNewObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeySpaceId.String(): pbtypes.String(mockedSpaceId),
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedNewObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeyResolvedLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeySpaceId.String(): pbtypes.String(mockedSpaceId),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedTypeId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTypeName),
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedTypeKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedTypeIcon),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -432,70 +490,24 @@ func TestObjectService_CreateObject(t *testing.T) {
|
|||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock type resolution
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyUniqueKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedObjectTypeUniqueKey),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectType),
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Once()
|
||||
|
||||
// Mock participant details
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
Keys: []string{
|
||||
bundle.RelationKeyId.String(),
|
||||
bundle.RelationKeyName.String(),
|
||||
bundle.RelationKeyIconEmoji.String(),
|
||||
bundle.RelationKeyIconImage.String(),
|
||||
bundle.RelationKeyIdentity.String(),
|
||||
bundle.RelationKeyGlobalName.String(),
|
||||
bundle.RelationKeyParticipantPermissions.String(),
|
||||
},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
Records: []*types.Struct{
|
||||
{},
|
||||
},
|
||||
}).Twice()
|
||||
|
||||
// when
|
||||
object, err := fx.CreateObject(ctx, mockedSpaceId, CreateObjectRequest{
|
||||
Name: mockedObjectName,
|
||||
Icon: mockedObjectIcon,
|
||||
// TODO: use actual values
|
||||
TemplateId: "",
|
||||
ObjectTypeUniqueKey: mockedObjectTypeUniqueKey,
|
||||
Name: mockedObjectName,
|
||||
Icon: util.Icon{Format: util.IconFormatEmoji, Emoji: util.StringPtr(mockedObjectIcon)},
|
||||
TemplateId: mockedTemplateId,
|
||||
TypeKey: mockedTypeKey,
|
||||
})
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, mockedObjectType, object.Type)
|
||||
require.Equal(t, "object", object.Object)
|
||||
require.Equal(t, mockedTypeId, object.Type.Id)
|
||||
require.Equal(t, mockedTypeName, object.Type.Name)
|
||||
require.Equal(t, mockedTypeKey, object.Type.Key)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedTypeIcon)}, object.Type.Icon)
|
||||
require.Equal(t, mockedNewObjectId, object.Id)
|
||||
require.Equal(t, mockedObjectName, object.Name)
|
||||
require.Equal(t, mockedObjectIcon, object.Icon)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedObjectIcon)}, object.Icon)
|
||||
require.Equal(t, mockedSpaceId, object.SpaceId)
|
||||
})
|
||||
|
||||
|
@ -512,7 +524,7 @@ func TestObjectService_CreateObject(t *testing.T) {
|
|||
// when
|
||||
object, err := fx.CreateObject(ctx, mockedSpaceId, CreateObjectRequest{
|
||||
Name: "Fail Object",
|
||||
Icon: "",
|
||||
Icon: util.Icon{},
|
||||
})
|
||||
|
||||
// then
|
||||
|
@ -550,8 +562,8 @@ func TestObjectService_ListTypes(t *testing.T) {
|
|||
require.Len(t, types, 1)
|
||||
require.Equal(t, "type-1", types[0].Id)
|
||||
require.Equal(t, "Type One", types[0].Name)
|
||||
require.Equal(t, "type-one-key", types[0].UniqueKey)
|
||||
require.Equal(t, "🗂️", types[0].Icon)
|
||||
require.Equal(t, "type-one-key", types[0].Key)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr("🗂️")}, types[0].Icon)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
@ -596,7 +608,7 @@ func TestObjectService_GetType(t *testing.T) {
|
|||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedTypeId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTypeName),
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedTypeUniqueKey),
|
||||
bundle.RelationKeyUniqueKey.String(): pbtypes.String(mockedTypeKey),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedTypeIcon),
|
||||
bundle.RelationKeyRecommendedLayout.String(): pbtypes.Float64(float64(model.ObjectType_basic)),
|
||||
},
|
||||
|
@ -613,8 +625,8 @@ func TestObjectService_GetType(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, mockedTypeId, objType.Id)
|
||||
require.Equal(t, mockedTypeName, objType.Name)
|
||||
require.Equal(t, mockedTypeUniqueKey, objType.UniqueKey)
|
||||
require.Equal(t, mockedTypeIcon, objType.Icon)
|
||||
require.Equal(t, mockedTypeKey, objType.Key)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedTypeIcon)}, objType.Icon)
|
||||
require.Equal(t, model.ObjectTypeLayout_name[int32(model.ObjectType_basic)], objType.RecommendedLayout)
|
||||
})
|
||||
|
||||
|
@ -696,7 +708,7 @@ func TestObjectService_ListTemplates(t *testing.T) {
|
|||
require.Len(t, templates, 1)
|
||||
require.Equal(t, "template-1", templates[0].Id)
|
||||
require.Equal(t, "Template Name", templates[0].Name)
|
||||
require.Equal(t, "📝", templates[0].Icon)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr("📝")}, templates[0].Icon)
|
||||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
@ -750,13 +762,13 @@ func TestObjectService_GetTemplate(t *testing.T) {
|
|||
}).Once()
|
||||
|
||||
// when
|
||||
template, err := fx.GetTemplate(ctx, mockedSpaceId, mockedObjectType, mockedTemplateId)
|
||||
template, err := fx.GetTemplate(ctx, mockedSpaceId, mockedTypeId, mockedTemplateId)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, mockedTemplateId, template.Id)
|
||||
require.Equal(t, mockedTemplateName, template.Name)
|
||||
require.Equal(t, mockedTemplateIcon, template.Icon)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedTemplateIcon)}, template.Icon)
|
||||
})
|
||||
|
||||
t.Run("template not found", func(t *testing.T) {
|
||||
|
|
|
@ -11,17 +11,19 @@ import (
|
|||
|
||||
// GlobalSearchHandler searches and retrieves objects across all spaces
|
||||
//
|
||||
// @Summary Search objects across all spaces
|
||||
// @Tags search
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Param request body SearchRequest true "Search parameters"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[object.Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /search [post]
|
||||
// @Summary Search objects across all spaces
|
||||
// @Description This endpoint executes a global search over every space the user has access to. It accepts pagination parameters (offset and limit) and a JSON body containing search criteria. The criteria include a search query string, an optional list of object types, and sort options (e.g. ascending/descending by creation, modification, or last opened dates). Internally, the endpoint aggregates results from each space, merges and sorts them (after last modified date by default), and returns a unified, paginated list of objects that match the search parameters.
|
||||
// @Tags search
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Param request body SearchRequest true "Search parameters"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[object.Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /search [post]
|
||||
func GlobalSearchHandler(s *SearchService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
offset := c.GetInt("offset")
|
||||
|
@ -51,18 +53,20 @@ func GlobalSearchHandler(s *SearchService) gin.HandlerFunc {
|
|||
|
||||
// SearchHandler searches and retrieves objects within a space
|
||||
//
|
||||
// @Summary Search objects within a space
|
||||
// @Tags search
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Param request body SearchRequest true "Search parameters"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[object.Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/search [post]
|
||||
// @Summary Search objects within a space
|
||||
// @Description This endpoint performs a focused search within a single space (specified by the space_id path parameter). Like the global search, it accepts pagination parameters and a JSON payload containing the search query, object types, and sorting preferences. The search is limited to the provided space and returns a list of objects that match the query. This allows clients to implement space‑specific filtering without having to process extraneous results.
|
||||
// @Tags search
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Param request body SearchRequest true "Search parameters"
|
||||
// @Success 200 {object} pagination.PaginatedResponse[object.Object] "List of objects"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/search [post]
|
||||
func SearchHandler(s *SearchService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceID := c.Param("space_id")
|
||||
|
|
|
@ -1,12 +1,28 @@
|
|||
package search
|
||||
|
||||
type SortDirection string
|
||||
|
||||
const (
|
||||
Asc SortDirection = "asc"
|
||||
Desc SortDirection = "desc"
|
||||
)
|
||||
|
||||
type SortProperty string
|
||||
|
||||
const (
|
||||
CreatedDate SortProperty = "created_date"
|
||||
LastModifiedDate SortProperty = "last_modified_date"
|
||||
LastOpenedDate SortProperty = "last_opened_date"
|
||||
Name SortProperty = "name"
|
||||
)
|
||||
|
||||
type SearchRequest struct {
|
||||
Query string `json:"query"`
|
||||
Types []string `json:"types"`
|
||||
Sort SortOptions `json:"sort"`
|
||||
Query string `json:"query" example:"test"` // The search term to look for in object names and snippets
|
||||
Types []string `json:"types" example:"ot-page,ot-678043f0cda9133be777049f,bafyreightzrdts2ymxyaeyzspwdfo2juspyam76ewq6qq7ixnw3523gs7q"` // The types of objects to search for, specified by key or ID
|
||||
Sort SortOptions `json:"sort"` // The sorting criteria and direction for the search results
|
||||
}
|
||||
|
||||
type SortOptions struct {
|
||||
Direction string `json:"direction" enums:"asc,desc" default:"desc"`
|
||||
Timestamp string `json:"timestamp" enums:"created_date,last_modified_date,last_opened_date" default:"last_modified_date"`
|
||||
Property SortProperty `json:"property" enums:"created_date,last_modified_date,last_opened_date,name" default:"last_modified_date"` // The property to sort the search results by
|
||||
Direction SortDirection `json:"direction" enums:"asc,desc" default:"desc"` // The direction to sort the search results
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
|
@ -28,13 +28,13 @@ type Service interface {
|
|||
}
|
||||
|
||||
type SearchService struct {
|
||||
mw service.ClientCommandsServer
|
||||
mw apicore.ClientCommands
|
||||
spaceService *space.SpaceService
|
||||
objectService *object.ObjectService
|
||||
AccountInfo *model.AccountInfo
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer, spaceService *space.SpaceService, objectService *object.ObjectService) *SearchService {
|
||||
func NewService(mw apicore.ClientCommands, spaceService *space.SpaceService, objectService *object.ObjectService) *SearchService {
|
||||
return &SearchService{mw: mw, spaceService: spaceService, objectService: objectService}
|
||||
}
|
||||
|
||||
|
@ -48,19 +48,31 @@ func (s *SearchService) GlobalSearch(ctx context.Context, request SearchRequest,
|
|||
baseFilters := s.prepareBaseFilters()
|
||||
queryFilters := s.prepareQueryFilter(request.Query)
|
||||
sorts := s.prepareSorts(request.Sort)
|
||||
dateToSortAfter := sorts.RelationKey
|
||||
if len(sorts) == 0 {
|
||||
return nil, 0, false, errors.New("no sort criteria provided")
|
||||
}
|
||||
criterionToSortAfter := sorts[0].RelationKey
|
||||
|
||||
type sortRecord struct {
|
||||
Id string
|
||||
SpaceId string
|
||||
numericSort float64
|
||||
stringSort string
|
||||
}
|
||||
combinedRecords := make([]sortRecord, 0)
|
||||
|
||||
allResponses := make([]*pb.RpcObjectSearchResponse, 0, len(spaces))
|
||||
for _, space := range spaces {
|
||||
// Resolve object type IDs per space, as they are unique per space
|
||||
// Resolve template type and object type IDs per space, as they are unique per space
|
||||
templateFilter := s.prepareTemplateFilter()
|
||||
typeFilters := s.prepareObjectTypeFilters(space.Id, request.Types)
|
||||
filters := s.combineFilters(model.BlockContentDataviewFilter_And, baseFilters, queryFilters, typeFilters)
|
||||
filters := s.combineFilters(model.BlockContentDataviewFilter_And, baseFilters, templateFilter, queryFilters, typeFilters)
|
||||
|
||||
objResp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: space.Id,
|
||||
Filters: filters,
|
||||
Sorts: []*model.BlockContentDataviewSort{sorts},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeySpaceId.String(), dateToSortAfter},
|
||||
Sorts: sorts,
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeySpaceId.String(), criterionToSortAfter},
|
||||
Limit: int32(offset + limit), // nolint: gosec
|
||||
})
|
||||
|
||||
|
@ -71,29 +83,35 @@ func (s *SearchService) GlobalSearch(ctx context.Context, request SearchRequest,
|
|||
allResponses = append(allResponses, objResp)
|
||||
}
|
||||
|
||||
combinedRecords := make([]struct {
|
||||
Id string
|
||||
SpaceId string
|
||||
DateToSortAfter float64
|
||||
}, 0)
|
||||
for _, objResp := range allResponses {
|
||||
for _, record := range objResp.Records {
|
||||
combinedRecords = append(combinedRecords, struct {
|
||||
Id string
|
||||
SpaceId string
|
||||
DateToSortAfter float64
|
||||
}{
|
||||
Id: record.Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
SpaceId: record.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(),
|
||||
DateToSortAfter: record.Fields[dateToSortAfter].GetNumberValue(),
|
||||
})
|
||||
sr := sortRecord{
|
||||
Id: record.Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
SpaceId: record.Fields[bundle.RelationKeySpaceId.String()].GetStringValue(),
|
||||
}
|
||||
if criterionToSortAfter == bundle.RelationKeyName.String() {
|
||||
sr.stringSort = record.Fields[criterionToSortAfter].GetStringValue()
|
||||
} else {
|
||||
sr.numericSort = record.Fields[criterionToSortAfter].GetNumberValue()
|
||||
}
|
||||
combinedRecords = append(combinedRecords, sr)
|
||||
}
|
||||
}
|
||||
|
||||
// sort after posix last_modified_date to achieve descending sort order across all spaces
|
||||
sort.Slice(combinedRecords, func(i, j int) bool {
|
||||
return combinedRecords[i].DateToSortAfter > combinedRecords[j].DateToSortAfter
|
||||
})
|
||||
sortFunc := func(i, j int) bool {
|
||||
if criterionToSortAfter == bundle.RelationKeyName.String() {
|
||||
if sorts[0].Type == model.BlockContentDataviewSort_Asc {
|
||||
return combinedRecords[i].stringSort < combinedRecords[j].stringSort
|
||||
}
|
||||
return combinedRecords[i].stringSort > combinedRecords[j].stringSort
|
||||
} else {
|
||||
if sorts[0].Type == model.BlockContentDataviewSort_Asc {
|
||||
return combinedRecords[i].numericSort < combinedRecords[j].numericSort
|
||||
}
|
||||
return combinedRecords[i].numericSort > combinedRecords[j].numericSort
|
||||
}
|
||||
}
|
||||
sort.SliceStable(combinedRecords, sortFunc)
|
||||
|
||||
total = len(combinedRecords)
|
||||
paginatedRecords, hasMore := pagination.Paginate(combinedRecords, offset, limit)
|
||||
|
@ -113,18 +131,22 @@ func (s *SearchService) GlobalSearch(ctx context.Context, request SearchRequest,
|
|||
// Search retrieves a paginated list of objects from a specific space that match the search parameters.
|
||||
func (s *SearchService) Search(ctx context.Context, spaceId string, request SearchRequest, offset int, limit int) (objects []object.Object, total int, hasMore bool, err error) {
|
||||
baseFilters := s.prepareBaseFilters()
|
||||
templateFilter := s.prepareTemplateFilter()
|
||||
queryFilters := s.prepareQueryFilter(request.Query)
|
||||
typeFilters := s.prepareObjectTypeFilters(spaceId, request.Types)
|
||||
filters := s.combineFilters(model.BlockContentDataviewFilter_And, baseFilters, queryFilters, typeFilters)
|
||||
filters := s.combineFilters(model.BlockContentDataviewFilter_And, baseFilters, templateFilter, queryFilters, typeFilters)
|
||||
|
||||
sorts := s.prepareSorts(request.Sort)
|
||||
dateToSortAfter := sorts.RelationKey
|
||||
if len(sorts) == 0 {
|
||||
return nil, 0, false, errors.New("no sort criteria provided")
|
||||
}
|
||||
criterionToSortAfter := sorts[0].RelationKey
|
||||
|
||||
resp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: filters,
|
||||
Sorts: []*model.BlockContentDataviewSort{sorts},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeySpaceId.String(), dateToSortAfter},
|
||||
Sorts: sorts,
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeySpaceId.String(), criterionToSortAfter},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
|
@ -172,7 +194,7 @@ func (s *SearchService) prepareBaseFilters() []*model.BlockContentDataviewFilter
|
|||
return []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
|
@ -194,6 +216,18 @@ func (s *SearchService) prepareBaseFilters() []*model.BlockContentDataviewFilter
|
|||
}
|
||||
}
|
||||
|
||||
// prepareTemplateFilter returns a filter that excludes templates from the search results.
|
||||
func (s *SearchService) prepareTemplateFilter() []*model.BlockContentDataviewFilter {
|
||||
return []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// prepareQueryFilter combines object name and snippet filters with an OR condition.
|
||||
func (s *SearchService) prepareQueryFilter(searchQuery string) []*model.BlockContentDataviewFilter {
|
||||
if searchQuery == "" {
|
||||
|
@ -262,38 +296,66 @@ func (s *SearchService) prepareObjectTypeFilters(spaceId string, objectTypes []s
|
|||
}
|
||||
|
||||
// prepareSorts returns a sort filter based on the given sort parameters
|
||||
func (s *SearchService) prepareSorts(sort SortOptions) *model.BlockContentDataviewSort {
|
||||
return &model.BlockContentDataviewSort{
|
||||
RelationKey: s.getSortRelationKey(sort.Timestamp),
|
||||
func (s *SearchService) prepareSorts(sort SortOptions) []*model.BlockContentDataviewSort {
|
||||
primarySort := &model.BlockContentDataviewSort{
|
||||
RelationKey: s.getSortRelationKey(sort.Property),
|
||||
Type: s.getSortDirection(sort.Direction),
|
||||
Format: model.RelationFormat_date,
|
||||
Format: s.getSortFormat(sort.Property),
|
||||
IncludeTime: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_NotSpecified,
|
||||
}
|
||||
|
||||
// last_opened_date possibly is empty, wherefore we sort by last_modified_date as secondary criterion
|
||||
if primarySort.RelationKey == bundle.RelationKeyLastOpenedDate.String() {
|
||||
secondarySort := &model.BlockContentDataviewSort{
|
||||
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Type: s.getSortDirection(sort.Direction),
|
||||
Format: model.RelationFormat_date,
|
||||
IncludeTime: true,
|
||||
EmptyPlacement: model.BlockContentDataviewSort_NotSpecified,
|
||||
}
|
||||
return []*model.BlockContentDataviewSort{primarySort, secondarySort}
|
||||
}
|
||||
|
||||
return []*model.BlockContentDataviewSort{primarySort}
|
||||
}
|
||||
|
||||
// getSortRelationKey returns the relation key for the given sort timestamp
|
||||
func (s *SearchService) getSortRelationKey(timestamp string) string {
|
||||
func (s *SearchService) getSortRelationKey(timestamp SortProperty) string {
|
||||
switch timestamp {
|
||||
case "created_date":
|
||||
case CreatedDate:
|
||||
return bundle.RelationKeyCreatedDate.String()
|
||||
case "last_modified_date":
|
||||
case LastModifiedDate:
|
||||
return bundle.RelationKeyLastModifiedDate.String()
|
||||
case "last_opened_date":
|
||||
case LastOpenedDate:
|
||||
return bundle.RelationKeyLastOpenedDate.String()
|
||||
case Name:
|
||||
return bundle.RelationKeyName.String()
|
||||
default:
|
||||
return bundle.RelationKeyLastModifiedDate.String()
|
||||
}
|
||||
}
|
||||
|
||||
// getSortDirection returns the sort direction for the given string
|
||||
func (s *SearchService) getSortDirection(direction string) model.BlockContentDataviewSortType {
|
||||
func (s *SearchService) getSortDirection(direction SortDirection) model.BlockContentDataviewSortType {
|
||||
switch direction {
|
||||
case "asc":
|
||||
case Asc:
|
||||
return model.BlockContentDataviewSort_Asc
|
||||
case "desc":
|
||||
case Desc:
|
||||
return model.BlockContentDataviewSort_Desc
|
||||
default:
|
||||
return model.BlockContentDataviewSort_Desc
|
||||
}
|
||||
}
|
||||
|
||||
// getSortFormat returns the sort format for the given timestamp
|
||||
func (s *SearchService) getSortFormat(timestamp SortProperty) model.RelationFormat {
|
||||
switch timestamp {
|
||||
case CreatedDate, LastModifiedDate, LastOpenedDate:
|
||||
return model.RelationFormat_date
|
||||
case Name:
|
||||
return model.RelationFormat_longtext
|
||||
default:
|
||||
return model.RelationFormat_date
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,54 +8,49 @@ import (
|
|||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service/mock_service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
const (
|
||||
offset = 0
|
||||
limit = 100
|
||||
techSpaceId = "tech-space-id"
|
||||
gatewayUrl = "http://localhost:31006"
|
||||
mockedSpaceId = "mocked-space-id"
|
||||
mockedSearchTerm = "mocked-search-term"
|
||||
mockedObjectId = "mocked-object-id"
|
||||
mockedObjectName = "mocked-object-name"
|
||||
mockedRootId = "mocked-root-id"
|
||||
mockedParticipantId = "mocked-participant-id"
|
||||
mockedType = "mocked-type"
|
||||
mockedTagId1 = "mocked-tag-id-1"
|
||||
mockedTagValue1 = "mocked-tag-value-1"
|
||||
mockedTagColor1 = "mocked-tag-color-1"
|
||||
mockedTagId2 = "mocked-tag-id-2"
|
||||
mockedTagValue2 = "mocked-tag-value-2"
|
||||
mockedTagColor2 = "mocked-tag-color-2"
|
||||
mockedObjectTypeName = "mocked-object-type-name"
|
||||
mockedParticipantName = "mocked-participant-name"
|
||||
mockedParticipantIcon = "mocked-participant-icon"
|
||||
mockedParticipantImage = "mocked-participant-image"
|
||||
mockedParticipantIdentity = "mocked-participant-identity"
|
||||
mockedParticipantGlobalName = "mocked-participant-global-name"
|
||||
offset = 0
|
||||
limit = 100
|
||||
techSpaceId = "tech-space-id"
|
||||
gatewayUrl = "http://localhost:31006"
|
||||
mockedSpaceId = "mocked-space-id"
|
||||
mockedSearchTerm = "mocked-search-term"
|
||||
mockedObjectId = "mocked-object-id"
|
||||
mockedObjectName = "mocked-object-name"
|
||||
mockedObjectIcon = "🌐"
|
||||
mockedParticipantId = "mocked-participant-id"
|
||||
mockedType = "mocked-type"
|
||||
mockedTagId1 = "mocked-tag-id-1"
|
||||
mockedTagValue1 = "mocked-tag-value-1"
|
||||
mockedTagColor1 = "mocked-tag-color-1"
|
||||
mockedTagId2 = "mocked-tag-id-2"
|
||||
mockedTagValue2 = "mocked-tag-value-2"
|
||||
mockedTagColor2 = "mocked-tag-color-2"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*SearchService
|
||||
mwMock *mock_service.MockClientCommandsServer
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mw := mock_service.NewMockClientCommandsServer(t)
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
|
||||
spaceService := space.NewService(mw)
|
||||
spaceService := space.NewService(mwMock)
|
||||
spaceService.AccountInfo = &model.AccountInfo{TechSpaceId: techSpaceId, GatewayUrl: gatewayUrl}
|
||||
objectService := object.NewService(mw, spaceService)
|
||||
objectService := object.NewService(mwMock, spaceService)
|
||||
objectService.AccountInfo = &model.AccountInfo{TechSpaceId: techSpaceId}
|
||||
searchService := NewService(mw, spaceService, objectService)
|
||||
searchService := NewService(mwMock, spaceService, objectService)
|
||||
searchService.AccountInfo = &model.AccountInfo{
|
||||
TechSpaceId: techSpaceId,
|
||||
GatewayUrl: gatewayUrl,
|
||||
|
@ -63,7 +58,7 @@ func newFixture(t *testing.T) *fixture {
|
|||
|
||||
return &fixture{
|
||||
SearchService: searchService,
|
||||
mwMock: mw,
|
||||
mwMock: mwMock,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +74,7 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_spaceView)),
|
||||
},
|
||||
|
@ -98,7 +93,7 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
EmptyPlacement: model.BlockContentDataviewSort_End,
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyTargetSpaceId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String()},
|
||||
Keys: []string{bundle.RelationKeyTargetSpaceId.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
|
@ -112,15 +107,32 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
|
||||
// Mock workspace opening
|
||||
fx.mwMock.On("WorkspaceOpen", mock.Anything, &pb.RpcWorkspaceOpenRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
WithChat: true,
|
||||
SpaceId: mockedSpaceId,
|
||||
}).Return(&pb.RpcWorkspaceOpenResponse{
|
||||
Info: &model.AccountInfo{
|
||||
TechSpaceId: mockedSpaceId,
|
||||
WorkspaceObjectId: "workspace-object-id",
|
||||
},
|
||||
Error: &pb.RpcWorkspaceOpenResponseError{Code: pb.RpcWorkspaceOpenResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock object show of workspace
|
||||
fx.mwMock.On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: "workspace-object-id",
|
||||
}).Return(&pb.RpcObjectShowResponse{
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String("Space Name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// Mock objects in space
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
|
@ -130,7 +142,7 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
NestedFilters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
|
@ -149,6 +161,12 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_Or,
|
||||
NestedFilters: []*model.BlockContentDataviewFilter{
|
||||
|
@ -197,10 +215,10 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
ObjectId: mockedObjectId,
|
||||
}).Return(&pb.RpcObjectShowResponse{
|
||||
ObjectView: &model.ObjectView{
|
||||
RootId: mockedRootId,
|
||||
RootId: mockedObjectId,
|
||||
Blocks: []*model.Block{
|
||||
{
|
||||
Id: mockedRootId,
|
||||
Id: mockedObjectId,
|
||||
Restrictions: &model.BlockRestrictions{
|
||||
Read: false,
|
||||
Edit: false,
|
||||
|
@ -233,13 +251,13 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
},
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedRootId,
|
||||
Id: mockedObjectId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeyLayout.String(): pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String("🌐"),
|
||||
bundle.RelationKeyResolvedLayout.String(): pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedObjectIcon),
|
||||
bundle.RelationKeyLastModifiedDate.String(): pbtypes.Float64(999999),
|
||||
bundle.RelationKeyLastModifiedBy.String(): pbtypes.String(mockedParticipantId),
|
||||
bundle.RelationKeyCreatedDate.String(): pbtypes.Float64(888888),
|
||||
|
@ -276,108 +294,114 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedType,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedType),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RelationLinks: []*model.RelationLink{
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyLastModifiedBy.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreatedDate.String(),
|
||||
Format: model.RelationFormat_date,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyCreator.String(),
|
||||
Format: model.RelationFormat_object,
|
||||
},
|
||||
{
|
||||
Key: bundle.RelationKeyTag.String(),
|
||||
Format: model.RelationFormat_tag,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
}, nil).Once()
|
||||
|
||||
// Mock type resolution
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedType),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectTypeName),
|
||||
// Mock tag-1 open
|
||||
fx.mwMock.On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedTagId1,
|
||||
}).Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTagValue1),
|
||||
bundle.RelationKeyRelationOptionColor.String(): pbtypes.String(mockedTagColor1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
}, nil).Once()
|
||||
|
||||
// Mock participant details
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedParticipantId),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(),
|
||||
bundle.RelationKeyName.String(),
|
||||
bundle.RelationKeyIconEmoji.String(),
|
||||
bundle.RelationKeyIconImage.String(),
|
||||
bundle.RelationKeyIdentity.String(),
|
||||
bundle.RelationKeyGlobalName.String(),
|
||||
bundle.RelationKeyParticipantPermissions.String(),
|
||||
},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedParticipantId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedParticipantName),
|
||||
bundle.RelationKeyIconEmoji.String(): pbtypes.String(mockedParticipantIcon),
|
||||
bundle.RelationKeyIconImage.String(): pbtypes.String(mockedParticipantImage),
|
||||
bundle.RelationKeyIdentity.String(): pbtypes.String(mockedParticipantIdentity),
|
||||
bundle.RelationKeyGlobalName.String(): pbtypes.String(mockedParticipantGlobalName),
|
||||
bundle.RelationKeyParticipantPermissions.String(): pbtypes.Int64(int64(model.ParticipantPermissions_Reader)),
|
||||
// Mock tag-2 open
|
||||
fx.mwMock.On("ObjectShow", mock.Anything, &pb.RpcObjectShowRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
ObjectId: mockedTagId2,
|
||||
}).Return(&pb.RpcObjectShowResponse{
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
ObjectView: &model.ObjectView{
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedTagValue2),
|
||||
bundle.RelationKeyRelationOptionColor.String(): pbtypes.String(mockedTagColor2),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Twice()
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.GlobalSearch(ctx, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Direction: "desc", Timestamp: "last_modified_date"}}, offset, limit)
|
||||
objects, total, hasMore, err := fx.GlobalSearch(ctx, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 1)
|
||||
require.Equal(t, mockedObjectTypeName, objects[0].Type)
|
||||
require.Equal(t, mockedSpaceId, objects[0].SpaceId)
|
||||
require.Equal(t, mockedObjectName, objects[0].Name)
|
||||
require.Equal(t, mockedObjectId, objects[0].Id)
|
||||
require.Equal(t, mockedObjectName, objects[0].Name)
|
||||
require.Equal(t, mockedType, objects[0].Type.Id)
|
||||
require.Equal(t, mockedSpaceId, objects[0].SpaceId)
|
||||
require.Equal(t, model.ObjectTypeLayout_name[int32(model.ObjectType_basic)], objects[0].Layout)
|
||||
require.Equal(t, "🌐", objects[0].Icon)
|
||||
require.Equal(t, util.Icon{Format: "emoji", Emoji: util.StringPtr(mockedObjectIcon)}, objects[0].Icon)
|
||||
require.Equal(t, "This is a sample text block", objects[0].Blocks[2].Text.Text)
|
||||
|
||||
// check details
|
||||
for _, detail := range objects[0].Details {
|
||||
if detail.Id == "created_date" {
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", detail.Details["created_date"])
|
||||
} else if detail.Id == "last_modified_date" {
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", detail.Details["last_modified_date"])
|
||||
} else if detail.Id == "created_by" {
|
||||
require.Equal(t, mockedParticipantId, detail.Details["details"].(space.Member).Id)
|
||||
require.Equal(t, mockedParticipantName, detail.Details["details"].(space.Member).Name)
|
||||
require.Equal(t, gatewayUrl+"/image/"+mockedParticipantImage, detail.Details["details"].(space.Member).Icon)
|
||||
require.Equal(t, mockedParticipantIdentity, detail.Details["details"].(space.Member).Identity)
|
||||
require.Equal(t, mockedParticipantGlobalName, detail.Details["details"].(space.Member).GlobalName)
|
||||
} else if detail.Id == "last_modified_by" {
|
||||
require.Equal(t, mockedParticipantId, detail.Details["details"].(space.Member).Id)
|
||||
for _, property := range objects[0].Properties {
|
||||
if property.Id == "created_date" {
|
||||
require.Equal(t, "1970-01-11T06:54:48Z", *property.Date)
|
||||
} else if property.Id == "last_modified_date" {
|
||||
require.Equal(t, "1970-01-12T13:46:39Z", *property.Date)
|
||||
} else if property.Id == "created_by" {
|
||||
require.Equal(t, []string{mockedParticipantId}, property.Object)
|
||||
} else if property.Id == "last_modified_by" {
|
||||
require.Equal(t, []string{mockedParticipantId}, property.Object)
|
||||
}
|
||||
}
|
||||
|
||||
// check tags
|
||||
tags := []object.Tag{}
|
||||
for _, detail := range objects[0].Details {
|
||||
if tagList, ok := detail.Details["tags"].([]object.Tag); ok {
|
||||
for _, tag := range tagList {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
for _, detail := range objects[0].Properties {
|
||||
for _, tag := range detail.MultiSelect {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
require.Len(t, tags, 2)
|
||||
|
@ -391,6 +415,44 @@ func TestSearchService_GlobalSearch(t *testing.T) {
|
|||
require.Equal(t, 1, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("no objects found globally", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.GlobalSearch(ctx, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 0)
|
||||
require.Equal(t, 0, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
|
||||
t.Run("error during global search", func(t *testing.T) {
|
||||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).Return(&pb.RpcObjectSearchResponse{
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_UNKNOWN_ERROR},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.GlobalSearch(ctx, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.Error(t, err)
|
||||
require.Empty(t, objects)
|
||||
require.Equal(t, 0, total)
|
||||
require.False(t, hasMore)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchService_Search(t *testing.T) {
|
||||
|
@ -408,7 +470,7 @@ func TestSearchService_Search(t *testing.T) {
|
|||
NestedFilters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_In,
|
||||
Value: pbtypes.IntList([]int{
|
||||
int(model.ObjectType_basic),
|
||||
|
@ -427,6 +489,12 @@ func TestSearchService_Search(t *testing.T) {
|
|||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.Bool(true),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: "type.uniqueKey",
|
||||
Condition: model.BlockContentDataviewFilter_NotEqual,
|
||||
Value: pbtypes.String("ot-template"),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_Or,
|
||||
NestedFilters: []*model.BlockContentDataviewFilter{
|
||||
|
@ -474,80 +542,43 @@ func TestSearchService_Search(t *testing.T) {
|
|||
ObjectId: mockedObjectId,
|
||||
}).Return(&pb.RpcObjectShowResponse{
|
||||
ObjectView: &model.ObjectView{
|
||||
RootId: mockedRootId,
|
||||
RootId: mockedObjectId,
|
||||
Details: []*model.ObjectViewDetailsSet{
|
||||
{
|
||||
Id: mockedObjectId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedObjectId),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectName),
|
||||
bundle.RelationKeyLayout.String(): pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyResolvedLayout.String(): pbtypes.Int64(int64(model.ObjectType_basic)),
|
||||
bundle.RelationKeyLastModifiedDate.String(): pbtypes.Float64(999999),
|
||||
bundle.RelationKeySpaceId.String(): pbtypes.String(mockedSpaceId),
|
||||
bundle.RelationKeyType.String(): pbtypes.String(mockedType),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: mockedType,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyId.String(): pbtypes.String(mockedType),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectShowResponseError{Code: pb.RpcObjectShowResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock type resolution
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(mockedType),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{
|
||||
{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyName.String(): pbtypes.String(mockedObjectTypeName),
|
||||
},
|
||||
},
|
||||
},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// Mock participant details
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: mockedSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(""),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(),
|
||||
bundle.RelationKeyName.String(),
|
||||
bundle.RelationKeyIconEmoji.String(),
|
||||
bundle.RelationKeyIconImage.String(),
|
||||
bundle.RelationKeyIdentity.String(),
|
||||
bundle.RelationKeyGlobalName.String(),
|
||||
bundle.RelationKeyParticipantPermissions.String(),
|
||||
},
|
||||
}).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Twice()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Direction: "desc", Timestamp: "last_modified_date"}}, offset, limit)
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Len(t, objects, 1)
|
||||
require.Equal(t, mockedObjectName, objects[0].Name)
|
||||
require.Equal(t, mockedObjectId, objects[0].Id)
|
||||
require.Equal(t, mockedObjectName, objects[0].Name)
|
||||
require.Equal(t, mockedType, objects[0].Type.Id)
|
||||
require.Equal(t, mockedSpaceId, objects[0].SpaceId)
|
||||
require.Equal(t, model.ObjectTypeLayout_name[int32(model.ObjectType_basic)], objects[0].Layout)
|
||||
|
||||
|
@ -559,14 +590,13 @@ func TestSearchService_Search(t *testing.T) {
|
|||
// given
|
||||
ctx := context.Background()
|
||||
fx := newFixture(t)
|
||||
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Direction: "desc", Timestamp: "last_modified_date"}}, offset, limit)
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
|
@ -585,7 +615,7 @@ func TestSearchService_Search(t *testing.T) {
|
|||
}).Once()
|
||||
|
||||
// when
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Direction: "desc", Timestamp: "last_modified_date"}}, offset, limit)
|
||||
objects, total, hasMore, err := fx.Search(ctx, mockedSpaceId, SearchRequest{Query: mockedSearchTerm, Types: []string{}, Sort: SortOptions{Property: LastModifiedDate, Direction: Desc}}, offset, limit)
|
||||
|
||||
// then
|
||||
require.Error(t, err)
|
||||
|
|
|
@ -11,16 +11,17 @@ import (
|
|||
|
||||
// GetSpacesHandler retrieves a list of spaces
|
||||
//
|
||||
// @Summary List spaces
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Space] "List of spaces"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces [get]
|
||||
// @Summary List spaces
|
||||
// @Description Retrieves a paginated list of all spaces that are accessible by the authenticated user. Each space record contains detailed information such as the space ID, name, icon (derived either from an emoji or image URL), and additional metadata. This endpoint is key to displaying a user’s workspaces.
|
||||
// @Tags spaces
|
||||
// @Produce json
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Space] "List of spaces"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces [get]
|
||||
func GetSpacesHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
offset := c.GetInt("offset")
|
||||
|
@ -30,6 +31,7 @@ func GetSpacesHandler(s *SpaceService) gin.HandlerFunc {
|
|||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedListSpaces, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedOpenWorkspace, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedOpenSpace, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
|
@ -42,30 +44,28 @@ func GetSpacesHandler(s *SpaceService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// CreateSpaceHandler creates a new space
|
||||
// GetSpaceHandler retrieves a space
|
||||
//
|
||||
// @Summary Create space
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param name body CreateSpaceRequest true "Space to create"
|
||||
// @Success 200 {object} CreateSpaceResponse "Space created successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces [post]
|
||||
func CreateSpaceHandler(s *SpaceService) gin.HandlerFunc {
|
||||
// @Summary Get space
|
||||
// @Description Fetches full details about a single space identified by its space ID. The response includes metadata such as the space name, icon, and various workspace IDs (home, archive, profile, etc.). This detailed view supports use cases such as displaying space-specific settings.
|
||||
// @Tags spaces
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Success 200 {object} SpaceResponse "Space"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Space not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id} [get]
|
||||
func GetSpaceHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
nameRequest := CreateSpaceRequest{}
|
||||
if err := c.BindJSON(&nameRequest); err != nil {
|
||||
apiErr := util.CodeToAPIError(http.StatusBadRequest, err.Error())
|
||||
c.JSON(http.StatusBadRequest, apiErr)
|
||||
return
|
||||
}
|
||||
spaceId := c.Param("space_id")
|
||||
|
||||
space, err := s.CreateSpace(c.Request.Context(), nameRequest.Name)
|
||||
space, err := s.GetSpace(c.Request.Context(), spaceId)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedCreateSpace, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrWorkspaceNotFound, http.StatusNotFound),
|
||||
util.ErrToCode(ErrFailedOpenWorkspace, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedOpenSpace, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
|
@ -74,23 +74,66 @@ func CreateSpaceHandler(s *SpaceService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, CreateSpaceResponse{Space: space})
|
||||
c.JSON(http.StatusOK, SpaceResponse{Space: space})
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSpaceHandler creates a new space
|
||||
//
|
||||
// @Summary Create space
|
||||
// @Description Creates a new workspace (or space) based on a supplied name in the JSON request body. The endpoint is subject to rate limiting and automatically applies default configurations such as generating a random icon and initializing the workspace with default settings (for example, a default dashboard or home page). On success, the new space’s full metadata is returned, enabling the client to immediately switch context to the new space.
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param name body CreateSpaceRequest true "Space to create"
|
||||
// @Success 200 {object} SpaceResponse "Space created successfully"
|
||||
// @Failure 400 {object} util.ValidationError "Bad request"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 423 {object} util.RateLimitError "Rate limit exceeded"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces [post]
|
||||
func CreateSpaceHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var req CreateSpaceRequest
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiErr := util.CodeToAPIError(http.StatusBadRequest, err.Error())
|
||||
c.JSON(http.StatusBadRequest, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
space, err := s.CreateSpace(c.Request.Context(), req)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrFailedCreateSpace, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedSetSpaceInfo, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedOpenWorkspace, http.StatusInternalServerError),
|
||||
util.ErrToCode(ErrFailedOpenSpace, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, SpaceResponse{Space: space})
|
||||
}
|
||||
}
|
||||
|
||||
// GetMembersHandler retrieves a list of members in a space
|
||||
//
|
||||
// @Summary List members
|
||||
// @Tags spaces
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Member] "List of members"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Router /spaces/{space_id}/members [get]
|
||||
// @Summary List members
|
||||
// @Description Returns a paginated list of members belonging to the specified space. Each member record includes the member’s profile ID, name, icon (which may be derived from an emoji or image), network identity, global name, status (e.g. joining, active) and role (e.g. Viewer, Editor, Owner). This endpoint supports collaborative features by allowing clients to show who is in a space and manage access rights.
|
||||
// @Tags members
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param offset query int false "The number of items to skip before starting to collect the result set" default(0)
|
||||
// @Param limit query int false "The number of items to return" default(100) maximum(1000)
|
||||
// @Success 200 {object} pagination.PaginatedResponse[Member] "List of members"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/members [get]
|
||||
func GetMembersHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
|
@ -111,3 +154,38 @@ func GetMembersHandler(s *SpaceService) gin.HandlerFunc {
|
|||
pagination.RespondWithPagination(c, http.StatusOK, members, total, offset, limit, hasMore)
|
||||
}
|
||||
}
|
||||
|
||||
// GetMemberHandler retrieves a member in a space
|
||||
//
|
||||
// @Summary Get member
|
||||
// @Description Fetches detailed information about a single member within a space. The endpoint returns the member’s identifier, name, icon, identity, global name, status and role. The member_id path parameter can be provided as either the member's ID (starting with `_participant`) or the member's identity. This is useful for user profile pages, permission management, and displaying member-specific information in collaborative environments.
|
||||
// @Tags members
|
||||
// @Produce json
|
||||
// @Param space_id path string true "Space ID"
|
||||
// @Param member_id path string true "Member ID or Identity"
|
||||
// @Success 200 {object} MemberResponse "Member"
|
||||
// @Failure 401 {object} util.UnauthorizedError "Unauthorized"
|
||||
// @Failure 404 {object} util.NotFoundError "Member not found"
|
||||
// @Failure 500 {object} util.ServerError "Internal server error"
|
||||
// @Security bearerauth
|
||||
// @Router /spaces/{space_id}/members/{member_id} [get]
|
||||
func GetMemberHandler(s *SpaceService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
spaceId := c.Param("space_id")
|
||||
memberId := c.Param("member_id")
|
||||
|
||||
member, err := s.GetMember(c.Request.Context(), spaceId, memberId)
|
||||
code := util.MapErrorCode(err,
|
||||
util.ErrToCode(ErrMemberNotFound, http.StatusNotFound),
|
||||
util.ErrToCode(ErrFailedGetMember, http.StatusInternalServerError),
|
||||
)
|
||||
|
||||
if code != http.StatusOK {
|
||||
apiErr := util.CodeToAPIError(code, err.Error())
|
||||
c.JSON(code, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, MemberResponse{Member: member})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,44 @@
|
|||
package space
|
||||
|
||||
type CreateSpaceRequest struct {
|
||||
Name string `json:"name" example:"New Space"`
|
||||
import (
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
)
|
||||
|
||||
type SpaceResponse struct {
|
||||
Space Space `json:"space"` // The space
|
||||
}
|
||||
|
||||
type CreateSpaceResponse struct {
|
||||
Space Space `json:"space"`
|
||||
type CreateSpaceRequest struct {
|
||||
Name string `json:"name" example:"New Space"` // The name of the space
|
||||
Description string `json:"description" example:"The local-first wiki"` // The description of the space
|
||||
}
|
||||
|
||||
type Space struct {
|
||||
Type string `json:"type" example:"space"`
|
||||
Id string `json:"id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"`
|
||||
Name string `json:"name" example:"Space Name"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay"`
|
||||
HomeObjectId string `json:"home_object_id" example:"bafyreie4qcl3wczb4cw5hrfyycikhjyh6oljdis3ewqrk5boaav3sbwqya"`
|
||||
ArchiveObjectId string `json:"archive_object_id" example:"bafyreialsgoyflf3etjm3parzurivyaukzivwortf32b4twnlwpwocsrri"`
|
||||
ProfileObjectId string `json:"profile_object_id" example:"bafyreiaxhwreshjqwndpwtdsu4mtihaqhhmlygqnyqpfyfwlqfq3rm3gw4"`
|
||||
MarketplaceWorkspaceId string `json:"marketplace_workspace_id" example:"_anytype_marketplace"`
|
||||
WorkspaceObjectId string `json:"workspace_object_id" example:"bafyreiapey2g6e6za4zfxvlgwdy4hbbfu676gmwrhnqvjbxvrchr7elr3y"`
|
||||
DeviceId string `json:"device_id" example:"12D3KooWGZMJ4kQVyQVXaj7gJPZr3RZ2nvd9M2Eq2pprEoPih9WF"`
|
||||
AccountSpaceId string `json:"account_space_id" example:"bafyreihpd2knon5wbljhtfeg3fcqtg3i2pomhhnigui6lrjmzcjzep7gcy.23me69r569oi1"`
|
||||
WidgetsId string `json:"widgets_id" example:"bafyreialj7pceh53mifm5dixlho47ke4qjmsn2uh4wsjf7xq2pnlo5xfva"`
|
||||
SpaceViewId string `json:"space_view_id" example:"bafyreigzv3vq7qwlrsin6njoduq727ssnhwd6bgyfj6nm4hv3pxoc2rxhy"`
|
||||
TechSpaceId string `json:"tech_space_id" example:"bafyreif4xuwncrjl6jajt4zrrfnylpki476nv2w64yf42ovt7gia7oypii.23me69r569oi1"`
|
||||
GatewayUrl string `json:"gateway_url" example:"http://127.0.0.1:31006"`
|
||||
LocalStoragePath string `json:"local_storage_path" example:"/Users/johndoe/Library/Application Support/Anytype/data/AAHTtt1wuQEnaYBNZ2Cyfcvs6DqPqxgn8VXDVk4avsUkMuha"`
|
||||
Timezone string `json:"timezone" example:""`
|
||||
AnalyticsId string `json:"analytics_id" example:"624aecdd-4797-4611-9d61-a2ae5f53cf1c"`
|
||||
NetworkId string `json:"network_id" example:"N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"`
|
||||
Object string `json:"object" example:"space"` // The data model of the object
|
||||
Id string `json:"id" example:"bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1"` // The id of the space
|
||||
Name string `json:"name" example:"My Space"` // The name of the space
|
||||
Icon util.Icon `json:"icon"` // The icon of the space
|
||||
Description string `json:"description" example:"The local-first wiki"` // The description of the space
|
||||
GatewayUrl string `json:"gateway_url" example:"http://127.0.0.1:31006"` // The gateway url to serve files and media
|
||||
NetworkId string `json:"network_id" example:"N83gJpVd9MuNRZAuJLZ7LiMntTThhPc6DtzWWVjb1M3PouVU"` // The network id of the space
|
||||
}
|
||||
|
||||
type MemberResponse struct {
|
||||
Member Member `json:"member"` // The member
|
||||
}
|
||||
|
||||
type UpdateMemberRequest struct {
|
||||
Status string `json:"status" enums:"active,removed,declined" example:"active"` // Status of the member
|
||||
Role string `json:"role,omitempty" enums:"viewer,editor" example:"viewer"` // Role to assign if approving a joining member
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Type string `json:"type" example:"member"`
|
||||
Id string `json:"id" example:"_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"`
|
||||
Name string `json:"name" example:"John Doe"`
|
||||
Icon string `json:"icon" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay?width=100"`
|
||||
Identity string `json:"identity" example:"AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"`
|
||||
GlobalName string `json:"global_name" example:"john.any"`
|
||||
Role string `json:"role" enums:"Reader,Writer,Owner,NoPermission" example:"Owner"`
|
||||
Object string `json:"object" example:"member"` // The data model of the object
|
||||
Id string `json:"id" example:"_participant_bafyreigyfkt6rbv24sbv5aq2hko1bhmv5xxlf22b4bypdu6j7hnphm3psq_23me69r569oi1_AAjEaEwPF4nkEh9AWkqEnzcQ8HziBB4ETjiTpvRCQvWnSMDZ"` // The profile object id of the member
|
||||
Name string `json:"name" example:"John Doe"` // The name of the member
|
||||
Icon util.Icon `json:"icon"` // The icon of the member
|
||||
Identity string `json:"identity" example:"AAjEaEwPF4nkEh7AWkqEnzcQ8HziGB4ETjiTpvRCQvWnSMDZ"` // The identity of the member in the network
|
||||
GlobalName string `json:"global_name" example:"john.any"` // The global name of the member in the network
|
||||
Status string `json:"status" enums:"joining,active,removed,declined,removing,canceled" example:"active"` // The status of the member
|
||||
Role string `json:"role" enums:"viewer,editor,owner,no_permission" example:"owner"` // The role of the member
|
||||
}
|
||||
|
|
|
@ -5,38 +5,51 @@ import (
|
|||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/iancoleman/strcase"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedListSpaces = errors.New("failed to retrieve list of spaces")
|
||||
ErrFailedOpenWorkspace = errors.New("failed to open workspace")
|
||||
ErrFailedGenerateRandomIcon = errors.New("failed to generate random icon")
|
||||
ErrFailedCreateSpace = errors.New("failed to create space")
|
||||
ErrFailedListMembers = errors.New("failed to retrieve list of members")
|
||||
ErrFailedListSpaces = errors.New("failed to retrieve list of spaces")
|
||||
ErrFailedOpenWorkspace = errors.New("failed to open workspace")
|
||||
ErrFailedOpenSpace = errors.New("failed to open space")
|
||||
ErrWorkspaceNotFound = errors.New("workspace not found")
|
||||
ErrFailedGenerateRandomIcon = errors.New("failed to generate random icon")
|
||||
ErrFailedCreateSpace = errors.New("failed to create space")
|
||||
ErrFailedSetSpaceInfo = errors.New("failed to set space info")
|
||||
ErrFailedListMembers = errors.New("failed to retrieve list of members")
|
||||
ErrFailedGetMember = errors.New("failed to retrieve member")
|
||||
ErrMemberNotFound = errors.New("member not found")
|
||||
ErrInvalidApproveMemberStatus = errors.New("status must be 'active', 'declined', or 'removed'")
|
||||
ErrInvalidApproveMemberRole = errors.New("role must be 'reader' or 'writer'")
|
||||
ErrFailedUpdateMember = errors.New("failed to update member")
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
ListSpaces(ctx context.Context, offset int, limit int) ([]Space, int, bool, error)
|
||||
CreateSpace(ctx context.Context, name string) (Space, error)
|
||||
GetSpace(ctx context.Context, spaceId string) (Space, error)
|
||||
CreateSpace(ctx context.Context, request CreateSpaceRequest) (Space, error)
|
||||
ListMembers(ctx context.Context, spaceId string, offset int, limit int) ([]Member, int, bool, error)
|
||||
GetMember(ctx context.Context, spaceId string, memberId string) (Member, error)
|
||||
UpdateMember(ctx context.Context, spaceId string, memberId string, request UpdateMemberRequest) (Member, error)
|
||||
}
|
||||
|
||||
type SpaceService struct {
|
||||
mw service.ClientCommandsServer
|
||||
mw apicore.ClientCommands
|
||||
AccountInfo *model.AccountInfo
|
||||
}
|
||||
|
||||
func NewService(mw service.ClientCommandsServer) *SpaceService {
|
||||
func NewService(mw apicore.ClientCommands) *SpaceService {
|
||||
return &SpaceService{mw: mw}
|
||||
}
|
||||
|
||||
|
@ -47,7 +60,7 @@ func (s *SpaceService) ListSpaces(ctx context.Context, offset int, limit int) (s
|
|||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_spaceView)),
|
||||
},
|
||||
|
@ -66,7 +79,7 @@ func (s *SpaceService) ListSpaces(ctx context.Context, offset int, limit int) (s
|
|||
EmptyPlacement: model.BlockContentDataviewSort_End,
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyTargetSpaceId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String()},
|
||||
Keys: []string{bundle.RelationKeyTargetSpaceId.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
|
@ -78,34 +91,63 @@ func (s *SpaceService) ListSpaces(ctx context.Context, offset int, limit int) (s
|
|||
spaces = make([]Space, 0, len(paginatedRecords))
|
||||
|
||||
for _, record := range paginatedRecords {
|
||||
workspace, err := s.getWorkspaceInfo(record.Fields[bundle.RelationKeyTargetSpaceId.String()].GetStringValue())
|
||||
workspace, err := s.getSpaceInfo(record.Fields[bundle.RelationKeyTargetSpaceId.String()].GetStringValue())
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
|
||||
// TODO: name and icon are only returned here; fix that
|
||||
workspace.Name = record.Fields[bundle.RelationKeyName.String()].GetStringValue()
|
||||
workspace.Icon = util.GetIconFromEmojiOrImage(s.AccountInfo, record.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), record.Fields[bundle.RelationKeyIconImage.String()].GetStringValue())
|
||||
|
||||
spaces = append(spaces, workspace)
|
||||
}
|
||||
|
||||
return spaces, total, hasMore, nil
|
||||
}
|
||||
|
||||
// GetSpace returns the space info for the space with the given ID.
|
||||
func (s *SpaceService) GetSpace(ctx context.Context, spaceId string) (Space, error) {
|
||||
// Check if the workspace exists and is active
|
||||
resp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: s.AccountInfo.TechSpaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyTargetSpaceId.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(spaceId),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeySpaceLocalStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.SpaceStatus_Ok)),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyTargetSpaceId.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return Space{}, ErrFailedOpenWorkspace
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return Space{}, ErrWorkspaceNotFound
|
||||
}
|
||||
|
||||
return s.getSpaceInfo(spaceId)
|
||||
}
|
||||
|
||||
// CreateSpace creates a new space with the given name and returns the space info.
|
||||
func (s *SpaceService) CreateSpace(ctx context.Context, name string) (Space, error) {
|
||||
func (s *SpaceService) CreateSpace(ctx context.Context, request CreateSpaceRequest) (Space, error) {
|
||||
name := request.Name
|
||||
iconOption, err := rand.Int(rand.Reader, big.NewInt(13))
|
||||
if err != nil {
|
||||
return Space{}, ErrFailedGenerateRandomIcon
|
||||
}
|
||||
|
||||
// Create new workspace with a random icon and import default use case
|
||||
resp := s.mw.WorkspaceCreate(ctx, &pb.RpcWorkspaceCreateRequest{
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyIconOption.String(): pbtypes.Float64(float64(iconOption.Int64())),
|
||||
bundle.RelationKeyName.String(): pbtypes.String(name),
|
||||
bundle.RelationKeyIconOption.String(): pbtypes.Float64(float64(iconOption.Int64())),
|
||||
bundle.RelationKeySpaceDashboardId.String(): pbtypes.String("lastOpened"),
|
||||
},
|
||||
},
|
||||
|
@ -113,21 +155,37 @@ func (s *SpaceService) CreateSpace(ctx context.Context, name string) (Space, err
|
|||
WithChat: true,
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcWorkspaceCreateResponseError_NULL {
|
||||
if resp.Error != nil && resp.Error.Code != pb.RpcWorkspaceCreateResponseError_NULL {
|
||||
return Space{}, ErrFailedCreateSpace
|
||||
}
|
||||
|
||||
return s.getWorkspaceInfo(resp.SpaceId)
|
||||
description := request.Description
|
||||
if description != "" {
|
||||
infoResp := s.mw.WorkspaceSetInfo(ctx, &pb.RpcWorkspaceSetInfoRequest{
|
||||
SpaceId: resp.SpaceId,
|
||||
Details: &types.Struct{
|
||||
Fields: map[string]*types.Value{
|
||||
bundle.RelationKeyDescription.String(): pbtypes.String(description),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if infoResp.Error != nil && infoResp.Error.Code != pb.RpcWorkspaceSetInfoResponseError_NULL {
|
||||
return Space{}, ErrFailedSetSpaceInfo
|
||||
}
|
||||
}
|
||||
|
||||
return s.getSpaceInfo(resp.SpaceId)
|
||||
}
|
||||
|
||||
// ListMembers returns a paginated list of members in the space with the given ID.
|
||||
func (s *SpaceService) ListMembers(ctx context.Context, spaceId string, offset int, limit int) (members []Member, total int, hasMore bool, err error) {
|
||||
resp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
activeResp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyLayout.String(),
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_participant)),
|
||||
},
|
||||
|
@ -144,28 +202,62 @@ func (s *SpaceService) ListMembers(ctx context.Context, spaceId string, offset i
|
|||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIdentity.String(), bundle.RelationKeyGlobalName.String(), bundle.RelationKeyParticipantPermissions.String()},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIdentity.String(), bundle.RelationKeyGlobalName.String(), bundle.RelationKeyParticipantPermissions.String(), bundle.RelationKeyParticipantStatus.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
if activeResp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedListMembers
|
||||
}
|
||||
|
||||
total = len(resp.Records)
|
||||
paginatedMembers, hasMore := pagination.Paginate(resp.Records, offset, limit)
|
||||
joiningResp := s.mw.ObjectSearch(ctx, &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_participant)),
|
||||
},
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyParticipantStatus.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ParticipantStatus_Joining)),
|
||||
},
|
||||
},
|
||||
Sorts: []*model.BlockContentDataviewSort{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyName.String(),
|
||||
Type: model.BlockContentDataviewSort_Asc,
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIdentity.String(), bundle.RelationKeyGlobalName.String(), bundle.RelationKeyParticipantPermissions.String(), bundle.RelationKeyParticipantStatus.String()},
|
||||
})
|
||||
|
||||
if joiningResp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return nil, 0, false, ErrFailedListMembers
|
||||
}
|
||||
|
||||
combinedRecords := make([]*types.Struct, 0, len(joiningResp.Records)+len(activeResp.Records))
|
||||
combinedRecords = append(combinedRecords, joiningResp.Records...)
|
||||
combinedRecords = append(combinedRecords, activeResp.Records...)
|
||||
|
||||
total = len(combinedRecords)
|
||||
paginatedMembers, hasMore := pagination.Paginate(combinedRecords, offset, limit)
|
||||
members = make([]Member, 0, len(paginatedMembers))
|
||||
|
||||
for _, record := range paginatedMembers {
|
||||
icon := util.GetIconFromEmojiOrImage(s.AccountInfo, record.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), record.Fields[bundle.RelationKeyIconImage.String()].GetStringValue())
|
||||
icon := util.GetIcon(s.AccountInfo, record.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), record.Fields[bundle.RelationKeyIconImage.String()].GetStringValue(), "", 0)
|
||||
|
||||
member := Member{
|
||||
Type: "member",
|
||||
Object: "member",
|
||||
Id: record.Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
Name: record.Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: icon,
|
||||
Identity: record.Fields[bundle.RelationKeyIdentity.String()].GetStringValue(),
|
||||
GlobalName: record.Fields[bundle.RelationKeyGlobalName.String()].GetStringValue(),
|
||||
Role: model.ParticipantPermissions_name[int32(record.Fields[bundle.RelationKeyParticipantPermissions.String()].GetNumberValue())],
|
||||
Status: strcase.ToSnake(model.ParticipantStatus_name[int32(record.Fields[bundle.RelationKeyParticipantStatus.String()].GetNumberValue())]),
|
||||
Role: s.mapMemberPermissions(model.ParticipantPermissions(record.Fields[bundle.RelationKeyParticipantPermissions.String()].GetNumberValue())),
|
||||
}
|
||||
|
||||
members = append(members, member)
|
||||
|
@ -174,69 +266,170 @@ func (s *SpaceService) ListMembers(ctx context.Context, spaceId string, offset i
|
|||
return members, total, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SpaceService) GetParticipantDetails(mw service.ClientCommandsServer, spaceId string, participantId string) Member {
|
||||
resp := mw.ObjectSearch(context.Background(), &pb.RpcObjectSearchRequest{
|
||||
// GetMember returns the member with the given ID in the space with the given ID.
|
||||
func (s *SpaceService) GetMember(ctx context.Context, spaceId string, memberId string) (Member, error) {
|
||||
// Member ID can be either a participant ID or an identity.
|
||||
relationKey := bundle.RelationKeyId
|
||||
if !strings.HasPrefix(memberId, "_participant") {
|
||||
relationKey = bundle.RelationKeyIdentity
|
||||
}
|
||||
|
||||
resp := s.mw.ObjectSearch(context.Background(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: bundle.RelationKeyId.String(),
|
||||
RelationKey: relationKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(participantId),
|
||||
Value: pbtypes.String(memberId),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIdentity.String(), bundle.RelationKeyGlobalName.String(), bundle.RelationKeyParticipantPermissions.String()},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyIconEmoji.String(), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIdentity.String(), bundle.RelationKeyGlobalName.String(), bundle.RelationKeyParticipantPermissions.String(), bundle.RelationKeyParticipantStatus.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return Member{}
|
||||
return Member{}, ErrFailedGetMember
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return Member{}
|
||||
return Member{}, ErrMemberNotFound
|
||||
}
|
||||
|
||||
icon := util.GetIconFromEmojiOrImage(s.AccountInfo, "", resp.Records[0].Fields[bundle.RelationKeyIconImage.String()].GetStringValue())
|
||||
icon := util.GetIcon(s.AccountInfo, "", resp.Records[0].Fields[bundle.RelationKeyIconImage.String()].GetStringValue(), "", 0)
|
||||
|
||||
return Member{
|
||||
Type: "member",
|
||||
Object: "member",
|
||||
Id: resp.Records[0].Fields[bundle.RelationKeyId.String()].GetStringValue(),
|
||||
Name: resp.Records[0].Fields[bundle.RelationKeyName.String()].GetStringValue(),
|
||||
Icon: icon,
|
||||
Identity: resp.Records[0].Fields[bundle.RelationKeyIdentity.String()].GetStringValue(),
|
||||
GlobalName: resp.Records[0].Fields[bundle.RelationKeyGlobalName.String()].GetStringValue(),
|
||||
Role: model.ParticipantPermissions_name[int32(resp.Records[0].Fields[bundle.RelationKeyParticipantPermissions.String()].GetNumberValue())],
|
||||
}
|
||||
Status: strcase.ToSnake(model.ParticipantStatus_name[int32(resp.Records[0].Fields[bundle.RelationKeyParticipantStatus.String()].GetNumberValue())]),
|
||||
Role: s.mapMemberPermissions(model.ParticipantPermissions(resp.Records[0].Fields[bundle.RelationKeyParticipantPermissions.String()].GetNumberValue())),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getWorkspaceInfo returns the workspace info for the space with the given ID.
|
||||
func (s *SpaceService) getWorkspaceInfo(spaceId string) (space Space, err error) {
|
||||
// UpdateMember approves member with defined role or removes them
|
||||
func (s *SpaceService) UpdateMember(ctx context.Context, spaceId string, memberId string, request UpdateMemberRequest) (Member, error) {
|
||||
member, err := s.GetMember(ctx, spaceId, memberId)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
|
||||
if request.Status != "active" && request.Status != "removed" && request.Status != "declined" {
|
||||
return Member{}, ErrInvalidApproveMemberStatus
|
||||
}
|
||||
|
||||
switch request.Status {
|
||||
case "active":
|
||||
if request.Role != "viewer" && request.Role != "editor" {
|
||||
return Member{}, ErrInvalidApproveMemberRole
|
||||
}
|
||||
|
||||
if member.Status == "joining" {
|
||||
// Approve the member's join request.
|
||||
approveResp := s.mw.SpaceRequestApprove(ctx, &pb.RpcSpaceRequestApproveRequest{
|
||||
SpaceId: spaceId,
|
||||
Identity: memberId,
|
||||
Permissions: s.mapMemberRole(request.Role),
|
||||
})
|
||||
if approveResp.Error.Code != pb.RpcSpaceRequestApproveResponseError_NULL {
|
||||
return Member{}, ErrFailedUpdateMember
|
||||
}
|
||||
} else {
|
||||
// Update the member's role.
|
||||
resp := s.mw.SpaceParticipantPermissionsChange(ctx, &pb.RpcSpaceParticipantPermissionsChangeRequest{
|
||||
SpaceId: spaceId,
|
||||
Changes: []*model.ParticipantPermissionChange{{Identity: memberId, Perms: s.mapMemberRole(request.Role)}},
|
||||
})
|
||||
if resp.Error.Code != pb.RpcSpaceParticipantPermissionsChangeResponseError_NULL {
|
||||
return Member{}, ErrFailedUpdateMember
|
||||
}
|
||||
}
|
||||
case "declined":
|
||||
// Reject the member's join request.
|
||||
rejectResp := s.mw.SpaceRequestDecline(ctx, &pb.RpcSpaceRequestDeclineRequest{
|
||||
SpaceId: spaceId,
|
||||
Identity: memberId,
|
||||
})
|
||||
if rejectResp.Error.Code != pb.RpcSpaceRequestDeclineResponseError_NULL {
|
||||
return Member{}, ErrFailedUpdateMember
|
||||
}
|
||||
case "removed":
|
||||
// Remove the member from the space.
|
||||
removeResp := s.mw.SpaceParticipantRemove(ctx, &pb.RpcSpaceParticipantRemoveRequest{
|
||||
SpaceId: spaceId,
|
||||
Identities: []string{memberId},
|
||||
})
|
||||
if removeResp.Error.Code != pb.RpcSpaceParticipantRemoveResponseError_NULL {
|
||||
return Member{}, ErrFailedUpdateMember
|
||||
}
|
||||
default:
|
||||
return Member{}, ErrInvalidApproveMemberStatus
|
||||
}
|
||||
|
||||
member, err = s.GetMember(ctx, spaceId, memberId)
|
||||
if err != nil {
|
||||
return Member{}, err
|
||||
}
|
||||
|
||||
return member, nil
|
||||
}
|
||||
|
||||
// getSpaceInfo returns the workspace info for the space with the given ID.
|
||||
func (s *SpaceService) getSpaceInfo(spaceId string) (space Space, err error) {
|
||||
workspaceResponse := s.mw.WorkspaceOpen(context.Background(), &pb.RpcWorkspaceOpenRequest{
|
||||
SpaceId: spaceId,
|
||||
WithChat: true,
|
||||
SpaceId: spaceId,
|
||||
})
|
||||
|
||||
if workspaceResponse.Error.Code != pb.RpcWorkspaceOpenResponseError_NULL {
|
||||
if workspaceResponse.Error != nil && workspaceResponse.Error.Code != pb.RpcWorkspaceOpenResponseError_NULL {
|
||||
return Space{}, ErrFailedOpenWorkspace
|
||||
}
|
||||
|
||||
spaceResp := s.mw.ObjectShow(context.Background(), &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: workspaceResponse.Info.WorkspaceObjectId,
|
||||
})
|
||||
|
||||
if spaceResp.Error != nil && spaceResp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return Space{}, ErrFailedOpenSpace
|
||||
}
|
||||
|
||||
name := spaceResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyName.String()].GetStringValue()
|
||||
icon := util.GetIcon(s.AccountInfo, spaceResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconEmoji.String()].GetStringValue(), spaceResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyIconImage.String()].GetStringValue(), "", 0)
|
||||
description := spaceResp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyDescription.String()].GetStringValue()
|
||||
|
||||
return Space{
|
||||
Type: "space",
|
||||
Id: spaceId,
|
||||
HomeObjectId: workspaceResponse.Info.HomeObjectId,
|
||||
ArchiveObjectId: workspaceResponse.Info.ArchiveObjectId,
|
||||
ProfileObjectId: workspaceResponse.Info.ProfileObjectId,
|
||||
MarketplaceWorkspaceId: workspaceResponse.Info.MarketplaceWorkspaceId,
|
||||
WorkspaceObjectId: workspaceResponse.Info.WorkspaceObjectId,
|
||||
DeviceId: workspaceResponse.Info.DeviceId,
|
||||
AccountSpaceId: workspaceResponse.Info.AccountSpaceId,
|
||||
WidgetsId: workspaceResponse.Info.WidgetsId,
|
||||
SpaceViewId: workspaceResponse.Info.SpaceViewId,
|
||||
TechSpaceId: workspaceResponse.Info.TechSpaceId,
|
||||
GatewayUrl: workspaceResponse.Info.GatewayUrl,
|
||||
LocalStoragePath: workspaceResponse.Info.LocalStoragePath,
|
||||
Timezone: workspaceResponse.Info.TimeZone,
|
||||
AnalyticsId: workspaceResponse.Info.AnalyticsId,
|
||||
NetworkId: workspaceResponse.Info.NetworkId,
|
||||
Object: "space",
|
||||
Id: spaceId,
|
||||
Name: name,
|
||||
Icon: icon,
|
||||
Description: description,
|
||||
GatewayUrl: workspaceResponse.Info.GatewayUrl,
|
||||
NetworkId: workspaceResponse.Info.NetworkId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// mapMemberPermissions maps participant permissions to a role
|
||||
func (s *SpaceService) mapMemberPermissions(permissions model.ParticipantPermissions) string {
|
||||
switch permissions {
|
||||
case model.ParticipantPermissions_Reader:
|
||||
return "viewer"
|
||||
case model.ParticipantPermissions_Writer:
|
||||
return "editor"
|
||||
default:
|
||||
return strcase.ToSnake(model.ParticipantPermissions_name[int32(permissions)])
|
||||
}
|
||||
}
|
||||
|
||||
// mapMemberPermissions maps a role to participant permissions
|
||||
func (s *SpaceService) mapMemberRole(role string) model.ParticipantPermissions {
|
||||
switch role {
|
||||
case "viewer":
|
||||
return model.ParticipantPermissions_Reader
|
||||
case "editor":
|
||||
return model.ParticipantPermissions_Writer
|
||||
default:
|
||||
return model.ParticipantPermissions_Reader
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,13 +1,13 @@
|
|||
package pagination
|
||||
|
||||
type PaginationMeta struct {
|
||||
Total int `json:"total" example:"1024"` // the total number of items available on that endpoint
|
||||
Offset int `json:"offset" example:"0"` // the current offset
|
||||
Limit int `json:"limit" example:"100"` // the current limit
|
||||
HasMore bool `json:"has_more" example:"true"` // whether there are more items available
|
||||
Total int `json:"total" example:"1024"` // The total number of items available for the endpoint
|
||||
Offset int `json:"offset" example:"0"` // The number of items skipped before starting to collect the result set
|
||||
Limit int `json:"limit" example:"100"` // The maximum number of items returned in the result set
|
||||
HasMore bool `json:"has_more" example:"true"` // Indicates if there are more items available beyond the current result set
|
||||
}
|
||||
|
||||
type PaginatedResponse[T any] struct {
|
||||
Data []T `json:"data"`
|
||||
Pagination PaginationMeta `json:"pagination"`
|
||||
Data []T `json:"data"` // The list of items in the current result set
|
||||
Pagination PaginationMeta `json:"pagination"` // The pagination metadata for the response
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -10,9 +11,15 @@ import (
|
|||
"github.com/didip/tollbooth/v8/limiter"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/anytype/account"
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingAuthorizationHeader = errors.New("missing authorization header")
|
||||
ErrInvalidAuthorizationHeader = errors.New("invalid authorization header format")
|
||||
ErrInvalidToken = errors.New("invalid token")
|
||||
)
|
||||
|
||||
// rateLimit is a middleware that limits the number of requests per second.
|
||||
|
@ -26,7 +33,8 @@ func (s *Server) rateLimit(max float64) gin.HandlerFunc {
|
|||
return func(c *gin.Context) {
|
||||
httpError := tollbooth.LimitByRequest(lmt, c.Writer, c.Request)
|
||||
if httpError != nil {
|
||||
c.AbortWithStatusJSON(httpError.StatusCode, gin.H{"error": httpError.Message})
|
||||
apiErr := util.CodeToAPIError(httpError.StatusCode, httpError.Message)
|
||||
c.AbortWithStatusJSON(httpError.StatusCode, apiErr)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
|
@ -34,16 +42,18 @@ func (s *Server) rateLimit(max float64) gin.HandlerFunc {
|
|||
}
|
||||
|
||||
// ensureAuthenticated is a middleware that ensures the request is authenticated.
|
||||
func (s *Server) ensureAuthenticated(mw service.ClientCommandsServer) gin.HandlerFunc {
|
||||
func (s *Server) ensureAuthenticated(mw apicore.ClientCommands) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing Authorization header"})
|
||||
apiErr := util.CodeToAPIError(http.StatusUnauthorized, ErrMissingAuthorizationHeader.Error())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid Authorization header format"})
|
||||
apiErr := util.CodeToAPIError(http.StatusUnauthorized, ErrInvalidAuthorizationHeader.Error())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, apiErr)
|
||||
return
|
||||
}
|
||||
key := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
@ -57,7 +67,8 @@ func (s *Server) ensureAuthenticated(mw service.ClientCommandsServer) gin.Handle
|
|||
if !exists {
|
||||
response := mw.WalletCreateSession(context.Background(), &pb.RpcWalletCreateSessionRequest{Auth: &pb.RpcWalletCreateSessionRequestAuthOfAppKey{AppKey: key}})
|
||||
if response.Error.Code != pb.RpcWalletCreateSessionResponseError_NULL {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
apiErr := util.CodeToAPIError(http.StatusUnauthorized, ErrInvalidToken.Error())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, apiErr)
|
||||
return
|
||||
}
|
||||
token = response.Token
|
||||
|
@ -74,15 +85,15 @@ func (s *Server) ensureAuthenticated(mw service.ClientCommandsServer) gin.Handle
|
|||
}
|
||||
|
||||
// ensureAccountInfo is a middleware that ensures the account info is available in the services.
|
||||
func (s *Server) ensureAccountInfo(accountService account.Service) gin.HandlerFunc {
|
||||
func (s *Server) ensureAccountInfo(accountService apicore.AccountService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
accInfo, err := accountService.GetInfo(context.Background())
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get account info: %v", err)})
|
||||
apiErr := util.CodeToAPIError(http.StatusInternalServerError, fmt.Sprintf("failed to get account info: %v", err))
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
s.exportService.AccountInfo = accInfo
|
||||
s.objectService.AccountInfo = accInfo
|
||||
s.spaceService.AccountInfo = accInfo
|
||||
s.searchService.AccountInfo = accInfo
|
||||
|
@ -90,3 +101,11 @@ func (s *Server) ensureAccountInfo(accountService account.Service) gin.HandlerFu
|
|||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// ensureMetadataHeader is a middleware that ensures the metadata header is set.
|
||||
func (s *Server) ensureMetadataHeader() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Anytype-Version", "2025-03-17")
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
214
core/api/server/middleware_test.go
Normal file
214
core/api/server/middleware_test.go
Normal file
|
@ -0,0 +1,214 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
func TestEnsureMetadataHeader(t *testing.T) {
|
||||
t.Run("sets correct header", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
middleware := fx.ensureMetadataHeader()
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, "2025-03-17", w.Header().Get("Anytype-Version"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnsureAuthenticated(t *testing.T) {
|
||||
t.Run("missing auth header", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.KeyToToken = make(map[string]string)
|
||||
middleware := fx.ensureAuthenticated(fx.mwMock)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
c.Request = req
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
expectedJSON, err := json.Marshal(util.CodeToAPIError(http.StatusUnauthorized, ErrMissingAuthorizationHeader.Error()))
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(expectedJSON), w.Body.String())
|
||||
})
|
||||
|
||||
t.Run("invalid auth header format", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.KeyToToken = make(map[string]string)
|
||||
middleware := fx.ensureAuthenticated(fx.mwMock)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Authorization", "InvalidToken")
|
||||
c.Request = req
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
expectedJSON, err := json.Marshal(util.CodeToAPIError(http.StatusUnauthorized, ErrInvalidAuthorizationHeader.Error()))
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(expectedJSON), w.Body.String())
|
||||
})
|
||||
|
||||
t.Run("valid token creation", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.KeyToToken = make(map[string]string)
|
||||
tokenExpected := "valid-token"
|
||||
|
||||
fx.mwMock.
|
||||
On("WalletCreateSession", mock.Anything, &pb.RpcWalletCreateSessionRequest{
|
||||
Auth: &pb.RpcWalletCreateSessionRequestAuthOfAppKey{AppKey: "someAppKey"},
|
||||
}).
|
||||
Return(&pb.RpcWalletCreateSessionResponse{
|
||||
Token: tokenExpected,
|
||||
Error: &pb.RpcWalletCreateSessionResponseError{
|
||||
Code: pb.RpcWalletCreateSessionResponseError_NULL,
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
middleware := fx.ensureAuthenticated(fx.mwMock)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Authorization", "Bearer someAppKey")
|
||||
c.Request = req
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
token, exists := c.Get("token")
|
||||
require.True(t, exists)
|
||||
require.Equal(t, tokenExpected, token)
|
||||
})
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
fx.KeyToToken = make(map[string]string)
|
||||
middleware := fx.ensureAuthenticated(fx.mwMock)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Authorization", "Bearer invalidKey")
|
||||
c.Request = req
|
||||
|
||||
fx.mwMock.
|
||||
On("WalletCreateSession", mock.Anything, &pb.RpcWalletCreateSessionRequest{
|
||||
Auth: &pb.RpcWalletCreateSessionRequestAuthOfAppKey{AppKey: "invalidKey"},
|
||||
}).
|
||||
Return(&pb.RpcWalletCreateSessionResponse{
|
||||
Token: "",
|
||||
Error: &pb.RpcWalletCreateSessionResponseError{
|
||||
Code: pb.RpcWalletCreateSessionResponseError_UNKNOWN_ERROR,
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
expectedJSON, err := json.Marshal(util.CodeToAPIError(http.StatusUnauthorized, ErrInvalidToken.Error()))
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(expectedJSON), w.Body.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnsureAccountInfo(t *testing.T) {
|
||||
t.Run("successful account info", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
expectedInfo := &model.AccountInfo{
|
||||
GatewayUrl: "http://localhost:31006",
|
||||
}
|
||||
fx.accountService.On("GetInfo", mock.Anything).Return(expectedInfo, nil).Once()
|
||||
|
||||
// when
|
||||
middleware := fx.ensureAccountInfo(&fx.accountService)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
|
||||
// when
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, expectedInfo, fx.objectService.AccountInfo)
|
||||
require.Equal(t, expectedInfo, fx.spaceService.AccountInfo)
|
||||
require.Equal(t, expectedInfo, fx.searchService.AccountInfo)
|
||||
})
|
||||
|
||||
t.Run("error retrieving account info", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
expectedErr := errors.New("failed to get info")
|
||||
fx.accountService.On("GetInfo", mock.Anything).Return(nil, expectedErr).Once()
|
||||
|
||||
middleware := fx.ensureAccountInfo(&fx.accountService)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
middleware(c)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRateLimit(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
router := gin.New()
|
||||
router.GET("/", fx.rateLimit(1), func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "OK")
|
||||
})
|
||||
|
||||
t.Run("first request allowed", func(t *testing.T) {
|
||||
// given
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.RemoteAddr = "1.2.3.4:5678"
|
||||
|
||||
// when
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
t.Run("second request rate-limited", func(t *testing.T) {
|
||||
// given
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.RemoteAddr = "1.2.3.4:5678"
|
||||
|
||||
// when
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusTooManyRequests, w.Code)
|
||||
})
|
||||
}
|
|
@ -7,16 +7,16 @@ import (
|
|||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
_ "github.com/anyproto/anytype-heart/core/api/docs"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/anytype/account"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/auth"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/export"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/list"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/search"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/core/api/pagination"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,7 +28,7 @@ const (
|
|||
)
|
||||
|
||||
// NewRouter builds and returns a *gin.Engine with all routes configured.
|
||||
func (s *Server) NewRouter(accountService account.Service, mw service.ClientCommandsServer) *gin.Engine {
|
||||
func (s *Server) NewRouter(mw apicore.ClientCommands, accountService apicore.AccountService) *gin.Engine {
|
||||
debug := os.Getenv("ANYTYPE_API_DEBUG") == "1"
|
||||
if !debug {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
@ -36,6 +36,7 @@ func (s *Server) NewRouter(accountService account.Service, mw service.ClientComm
|
|||
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery())
|
||||
router.Use(s.ensureMetadataHeader())
|
||||
|
||||
if debug {
|
||||
router.Use(gin.Logger())
|
||||
|
@ -64,7 +65,13 @@ func (s *Server) NewRouter(accountService account.Service, mw service.ClientComm
|
|||
v1.Use(s.ensureAccountInfo(accountService))
|
||||
{
|
||||
// Export
|
||||
v1.POST("/spaces/:space_id/objects/:object_id/export/:format", export.GetObjectExportHandler(s.exportService))
|
||||
v1.GET("/spaces/:space_id/objects/:object_id/:format", export.GetObjectExportHandler(s.exportService))
|
||||
|
||||
// List
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/views", list.GetListViewsHandler(s.listService))
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/:view_id/objects", list.GetObjectsInListHandler(s.listService))
|
||||
v1.POST("/spaces/:space_id/lists/:list_id/objects", list.AddObjectsToListHandler(s.listService))
|
||||
v1.DELETE("/spaces/:space_id/lists/:list_id/objects/:object_id", s.rateLimit(maxWriteRequestsPerSecond), list.RemoveObjectFromListHandler(s.listService))
|
||||
|
||||
// Object
|
||||
v1.GET("/spaces/:space_id/objects", object.GetObjectsHandler(s.objectService))
|
||||
|
@ -78,7 +85,10 @@ func (s *Server) NewRouter(accountService account.Service, mw service.ClientComm
|
|||
|
||||
// Space
|
||||
v1.GET("/spaces", space.GetSpacesHandler(s.spaceService))
|
||||
v1.GET("/spaces/:space_id", space.GetSpaceHandler(s.spaceService))
|
||||
v1.GET("/spaces/:space_id/members", space.GetMembersHandler(s.spaceService))
|
||||
v1.GET("/spaces/:space_id/members/:member_id", space.GetMemberHandler(s.spaceService))
|
||||
// v1.PATCH("/spaces/:space_id/members/:member_id", s.rateLimit(maxWriteRequestsPerSecond), space.UpdateMemberHandler(s.spaceService))
|
||||
v1.POST("/spaces", s.rateLimit(maxWriteRequestsPerSecond), space.CreateSpaceHandler(s.spaceService))
|
||||
|
||||
// Type
|
||||
|
|
74
core/api/server/router_test.go
Normal file
74
core/api/server/router_test.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
func TestRouter_Unauthenticated(t *testing.T) {
|
||||
t.Run("GET /v1/spaces without auth returns 401", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
engine := fx.NewRouter(fx.mwMock, &fx.accountService)
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/v1/spaces", nil)
|
||||
|
||||
// when
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
// then
|
||||
require.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouter_AuthRoute(t *testing.T) {
|
||||
t.Run("POST /v1/auth/token is accessible without auth", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
engine := fx.NewRouter(fx.mwMock, &fx.accountService)
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/v1/auth/token", nil)
|
||||
|
||||
// when
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
// then
|
||||
require.NotEqual(t, http.StatusUnauthorized, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouter_MetadataHeader(t *testing.T) {
|
||||
t.Run("Response includes Anytype-Version header", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
engine := fx.NewRouter(fx.mwMock, &fx.accountService)
|
||||
fx.KeyToToken = map[string]string{"validKey": "dummyToken"}
|
||||
fx.accountService.On("GetInfo", mock.Anything).
|
||||
Return(&model.AccountInfo{
|
||||
GatewayUrl: "http://localhost:31006",
|
||||
}, nil).Once()
|
||||
fx.mwMock.On("ObjectSearch", mock.Anything, mock.Anything).
|
||||
Return(&pb.RpcObjectSearchResponse{
|
||||
Records: []*types.Struct{},
|
||||
Error: &pb.RpcObjectSearchResponseError{Code: pb.RpcObjectSearchResponseError_NULL},
|
||||
}, nil).Once()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/v1/spaces", nil)
|
||||
req.Header.Set("Authorization", "Bearer validKey")
|
||||
|
||||
// when
|
||||
engine.ServeHTTP(w, req)
|
||||
|
||||
// then
|
||||
require.Equal(t, "2025-03-17", w.Header().Get("Anytype-Version"))
|
||||
})
|
||||
}
|
|
@ -5,13 +5,13 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/anytype/account"
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/auth"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/export"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/list"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/object"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/search"
|
||||
"github.com/anyproto/anytype-heart/core/api/internal/space"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
)
|
||||
|
||||
// Server wraps the HTTP server and service logic.
|
||||
|
@ -20,6 +20,7 @@ type Server struct {
|
|||
|
||||
authService *auth.AuthService
|
||||
exportService *export.ExportService
|
||||
listService *list.ListService
|
||||
objectService *object.ObjectService
|
||||
spaceService *space.SpaceService
|
||||
searchService *search.SearchService
|
||||
|
@ -29,16 +30,17 @@ type Server struct {
|
|||
}
|
||||
|
||||
// NewServer constructs a new Server with default config and sets up the routes.
|
||||
func NewServer(accountService account.Service, mw service.ClientCommandsServer) *Server {
|
||||
func NewServer(mw apicore.ClientCommands, accountService apicore.AccountService, exportService apicore.ExportService) *Server {
|
||||
s := &Server{
|
||||
authService: auth.NewService(mw),
|
||||
exportService: export.NewService(mw),
|
||||
exportService: export.NewService(mw, exportService),
|
||||
spaceService: space.NewService(mw),
|
||||
}
|
||||
|
||||
s.objectService = object.NewService(mw, s.spaceService)
|
||||
s.listService = list.NewService(mw, s.objectService)
|
||||
s.searchService = search.NewService(mw, s.spaceService, s.objectService)
|
||||
s.engine = s.NewRouter(accountService, mw)
|
||||
s.engine = s.NewRouter(mw, accountService)
|
||||
s.KeyToToken = make(map[string]string)
|
||||
|
||||
return s
|
||||
|
|
63
core/api/server/server_test.go
Normal file
63
core/api/server/server_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore/mock_apicore"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*Server
|
||||
accountService mock_apicore.MockAccountService
|
||||
exportService mock_apicore.MockExportService
|
||||
mwMock *mock_apicore.MockClientCommands
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
mwMock := mock_apicore.NewMockClientCommands(t)
|
||||
accountService := mock_apicore.NewMockAccountService(t)
|
||||
exportService := mock_apicore.NewMockExportService(t)
|
||||
server := NewServer(mwMock, accountService, exportService)
|
||||
|
||||
return &fixture{
|
||||
Server: server,
|
||||
accountService: *accountService,
|
||||
exportService: *exportService,
|
||||
mwMock: mwMock,
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
t.Run("returns valid server", func(t *testing.T) {
|
||||
// when
|
||||
s := newFixture(t)
|
||||
|
||||
// then
|
||||
require.NotNil(t, s)
|
||||
require.NotNil(t, s.engine)
|
||||
require.NotNil(t, s.KeyToToken)
|
||||
|
||||
require.NotNil(t, s.authService)
|
||||
require.NotNil(t, s.exportService)
|
||||
require.NotNil(t, s.spaceService)
|
||||
require.NotNil(t, s.objectService)
|
||||
require.NotNil(t, s.listService)
|
||||
require.NotNil(t, s.searchService)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Engine(t *testing.T) {
|
||||
t.Run("Engine returns same engine instance", func(t *testing.T) {
|
||||
// given
|
||||
s := newFixture(t)
|
||||
|
||||
// when
|
||||
engine := s.Engine()
|
||||
|
||||
// then
|
||||
require.Equal(t, s.engine, engine)
|
||||
})
|
||||
}
|
|
@ -12,8 +12,9 @@ import (
|
|||
|
||||
"github.com/anyproto/anytype-heart/core/anytype/account"
|
||||
"github.com/anyproto/anytype-heart/core/anytype/config"
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/core/api/server"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/core/block/export"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,7 +23,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
mwSrv service.ClientCommandsServer
|
||||
mwSrv apicore.ClientCommands
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
|
@ -33,8 +34,9 @@ type Service interface {
|
|||
type apiService struct {
|
||||
srv *server.Server
|
||||
httpSrv *http.Server
|
||||
mw service.ClientCommandsServer
|
||||
accountService account.Service
|
||||
mw apicore.ClientCommands
|
||||
accountService apicore.AccountService
|
||||
exportService apicore.ExportService
|
||||
listenAddr string
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
@ -49,23 +51,24 @@ func (s *apiService) Name() (name string) {
|
|||
|
||||
// Init initializes the API service.
|
||||
//
|
||||
// @title Anytype API
|
||||
// @version 1.0
|
||||
// @description This API allows interaction with Anytype resources such as spaces, objects and types.
|
||||
// @termsOfService https://anytype.io/terms_of_use
|
||||
// @contact.name Anytype Support
|
||||
// @contact.url https://anytype.io/contact
|
||||
// @contact.email support@anytype.io
|
||||
// @license.name Any Source Available License 1.0
|
||||
// @license.url https://github.com/anyproto/anytype-ts/blob/main/LICENSE.md
|
||||
// @host localhost:31009
|
||||
// @BasePath /v1
|
||||
// @securityDefinitions.basic BasicAuth
|
||||
// @externalDocs.description OpenAPI
|
||||
// @externalDocs.url https://swagger.io/resources/open-api/
|
||||
// @title Anytype API
|
||||
// @version 2025-03-17
|
||||
// @description This API allows interaction with Anytype resources such as spaces, objects and types.
|
||||
// @termsOfService https://anytype.io/terms_of_use
|
||||
// @contact.name Anytype Support
|
||||
// @contact.url https://anytype.io/contact
|
||||
// @contact.email support@anytype.io
|
||||
// @license.name Any Source Available License 1.0
|
||||
// @license.url https://github.com/anyproto/anytype-api/blob/main/LICENSE.md
|
||||
// @host http://localhost:31009
|
||||
// @BasePath /v1
|
||||
// @securitydefinitions.bearerauth BearerAuth
|
||||
// @externalDocs.description OpenAPI
|
||||
// @externalDocs.url https://swagger.io/resources/open-api/
|
||||
func (s *apiService) Init(a *app.App) (err error) {
|
||||
s.listenAddr = a.MustComponent(config.CName).(*config.Config).JsonApiListenAddr
|
||||
s.accountService = a.MustComponent(account.CName).(account.Service)
|
||||
s.exportService = a.MustComponent(export.CName).(apicore.ExportService)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -86,7 +89,7 @@ func (s *apiService) runServer() {
|
|||
return
|
||||
}
|
||||
|
||||
s.srv = server.NewServer(s.accountService, s.mw)
|
||||
s.srv = server.NewServer(s.mw, s.accountService, s.exportService)
|
||||
s.httpSrv = &http.Server{
|
||||
Addr: s.listenAddr,
|
||||
Handler: s.srv.Engine(),
|
||||
|
@ -130,6 +133,6 @@ func (s *apiService) ReassignAddress(ctx context.Context, listenAddr string) (er
|
|||
return nil
|
||||
}
|
||||
|
||||
func SetMiddlewareParams(mw service.ClientCommandsServer) {
|
||||
func SetMiddlewareParams(mw apicore.ClientCommands) {
|
||||
mwSrv = mw
|
||||
}
|
||||
|
|
|
@ -5,38 +5,52 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
// 400
|
||||
// ValidationError is a struct for 400 errors
|
||||
type ValidationError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Bad request"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// 401
|
||||
// UnauthorizedError is a struct for 401 errors
|
||||
type UnauthorizedError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Unauthorized"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// 403
|
||||
// ForbiddenError is a struct for 403 errors
|
||||
type ForbiddenError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Forbidden"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// 404
|
||||
// NotFoundError is a struct for 404 errors
|
||||
type NotFoundError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Resource not found"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// 500
|
||||
// GoneError is a struct for 410 errors
|
||||
type GoneError struct {
|
||||
Error struct {
|
||||
Message string `json:"message" example:"Resource is gone"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// RateLimitError is a struct for 423 errors
|
||||
type RateLimitError struct {
|
||||
Error struct {
|
||||
Message string `json:"message" example:"Rate limit exceeded"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// ServerError is a struct for 500 errors
|
||||
type ServerError struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Internal server error"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
|
@ -72,10 +86,11 @@ func MapErrorCode(err error, mappings ...errCodeMapping) int {
|
|||
// for the given HTTP code, embedding the supplied message.
|
||||
func CodeToAPIError(code int, message string) any {
|
||||
switch code {
|
||||
case http.StatusNotFound:
|
||||
return NotFoundError{
|
||||
|
||||
case http.StatusBadRequest:
|
||||
return ValidationError{
|
||||
Error: struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Bad request"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
|
@ -84,16 +99,34 @@ func CodeToAPIError(code int, message string) any {
|
|||
case http.StatusUnauthorized:
|
||||
return UnauthorizedError{
|
||||
Error: struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Unauthorized"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
|
||||
case http.StatusBadRequest:
|
||||
return ValidationError{
|
||||
case http.StatusForbidden:
|
||||
return ForbiddenError{
|
||||
Error: struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Forbidden"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
|
||||
case http.StatusNotFound:
|
||||
return NotFoundError{
|
||||
Error: struct {
|
||||
Message string `json:"message" example:"Resource not found"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
|
||||
case http.StatusTooManyRequests:
|
||||
return RateLimitError{
|
||||
Error: struct {
|
||||
Message string `json:"message" example:"Rate limit exceeded"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
|
@ -102,7 +135,7 @@ func CodeToAPIError(code int, message string) any {
|
|||
default:
|
||||
return ServerError{
|
||||
Error: struct {
|
||||
Message string `json:"message"`
|
||||
Message string `json:"message" example:"Internal server error"`
|
||||
}{
|
||||
Message: message,
|
||||
},
|
||||
|
|
|
@ -4,68 +4,84 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/api/apicore"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pb/service"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedSearchType = errors.New("failed to search for type")
|
||||
ErrorTypeNotFound = errors.New("type not found")
|
||||
ErrFailedSearchType = errors.New("failed to search for type")
|
||||
ErrorTypeNotFound = errors.New("type not found")
|
||||
ErrFailedSearchProperty = errors.New("failed to search for property")
|
||||
ErrorPropertyNotFound = errors.New("property not found")
|
||||
)
|
||||
|
||||
// GetIconFromEmojiOrImage returns the icon to use for the object, which can be either an emoji or an image url
|
||||
func GetIconFromEmojiOrImage(accountInfo *model.AccountInfo, iconEmoji string, iconImage string) string {
|
||||
var iconOptionToColor = map[float64]string{
|
||||
1: "grey",
|
||||
2: "yellow",
|
||||
3: "orange",
|
||||
4: "red",
|
||||
5: "pink",
|
||||
6: "purple",
|
||||
7: "blue",
|
||||
8: "ice",
|
||||
9: "teal",
|
||||
10: "lime",
|
||||
}
|
||||
|
||||
type IconFormat string
|
||||
|
||||
const (
|
||||
IconFormatEmoji IconFormat = "emoji"
|
||||
IconFormatFile IconFormat = "file"
|
||||
IconFormatIcon IconFormat = "icon"
|
||||
)
|
||||
|
||||
type Icon struct {
|
||||
Format IconFormat `json:"format" enums:"emoji,file,icon" example:"emoji"` // The type of the icon
|
||||
Emoji *string `json:"emoji,omitempty" example:"📄"` // The emoji of the icon
|
||||
File *string `json:"file,omitempty" example:"http://127.0.0.1:31006/image/bafybeieptz5hvcy6txplcvphjbbh5yjc2zqhmihs3owkh5oab4ezauzqay"` // The file of the icon
|
||||
Name *string `json:"name,omitempty" example:"document"` // The name of the icon
|
||||
Color *string `json:"color,omitempty" example:"red"` // The color of the icon
|
||||
}
|
||||
|
||||
// StringPtr returns a pointer to the string
|
||||
func StringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// GetIcon returns the icon to use for the object, which can be builtin icon, emoji or file
|
||||
func GetIcon(accountInfo *model.AccountInfo, iconEmoji string, iconImage string, iconName string, iconOption float64) Icon {
|
||||
if iconName != "" {
|
||||
return Icon{
|
||||
Format: "icon",
|
||||
Name: &iconName,
|
||||
Color: StringPtr(iconOptionToColor[iconOption]),
|
||||
}
|
||||
}
|
||||
|
||||
if iconEmoji != "" {
|
||||
return iconEmoji
|
||||
return Icon{
|
||||
Format: "emoji",
|
||||
Emoji: &iconEmoji,
|
||||
}
|
||||
}
|
||||
|
||||
if iconImage != "" {
|
||||
return fmt.Sprintf("%s/image/%s", accountInfo.GatewayUrl, iconImage)
|
||||
return Icon{
|
||||
Format: "file",
|
||||
File: StringPtr(fmt.Sprintf("%s/image/%s", accountInfo.GatewayUrl, iconImage)),
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
return Icon{}
|
||||
}
|
||||
|
||||
// ResolveTypeToName resolves the type ID to the name of the type, e.g. "ot-page" to "Page" or "bafyreigyb6l5szohs32ts26ku2j42yd65e6hqy2u3gtzgdwqv6hzftsetu" to "Custom Type"
|
||||
func ResolveTypeToName(mw service.ClientCommandsServer, spaceId string, typeId string) (typeName string, err error) {
|
||||
// Can't look up preinstalled types based on relation key, therefore need to use unique key
|
||||
relKey := bundle.RelationKeyId.String()
|
||||
if strings.HasPrefix(typeId, "ot-") {
|
||||
relKey = bundle.RelationKeyUniqueKey.String()
|
||||
}
|
||||
|
||||
// Call ObjectSearch for object of specified type and return the name
|
||||
resp := mw.ObjectSearch(context.Background(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
Operator: model.BlockContentDataviewFilter_No,
|
||||
RelationKey: relKey,
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(typeId),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyName.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return "", ErrFailedSearchType
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return "", ErrorTypeNotFound
|
||||
}
|
||||
|
||||
return resp.Records[0].Fields[bundle.RelationKeyName.String()].GetStringValue(), nil
|
||||
}
|
||||
|
||||
func ResolveUniqueKeyToTypeId(mw service.ClientCommandsServer, spaceId string, uniqueKey string) (typeId string, err error) {
|
||||
// Call ObjectSearch for type with unique key and return the type's ID
|
||||
// ResolveUniqueKeyToTypeId resolves the unique key to the type's ID
|
||||
func ResolveUniqueKeyToTypeId(mw apicore.ClientCommands, spaceId string, uniqueKey string) (typeId string, err error) {
|
||||
resp := mw.ObjectSearch(context.Background(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
|
@ -78,13 +94,59 @@ func ResolveUniqueKeyToTypeId(mw service.ClientCommandsServer, spaceId string, u
|
|||
Keys: []string{bundle.RelationKeyId.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return "", ErrFailedSearchType
|
||||
}
|
||||
if resp.Error != nil {
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return "", ErrFailedSearchType
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return "", ErrorTypeNotFound
|
||||
if len(resp.Records) == 0 {
|
||||
return "", ErrorTypeNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return resp.Records[0].Fields[bundle.RelationKeyId.String()].GetStringValue(), nil
|
||||
}
|
||||
|
||||
// ResolveIdtoUniqueKey resolves the type's ID to the unique key
|
||||
func ResolveIdtoUniqueKey(mw apicore.ClientCommands, spaceId string, typeId string) (uniqueKey string, err error) {
|
||||
resp := mw.ObjectShow(context.Background(), &pb.RpcObjectShowRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectId: typeId,
|
||||
})
|
||||
|
||||
if resp.Error != nil && resp.Error.Code != pb.RpcObjectShowResponseError_NULL {
|
||||
return "", ErrorTypeNotFound
|
||||
}
|
||||
|
||||
return resp.ObjectView.Details[0].Details.Fields[bundle.RelationKeyUniqueKey.String()].GetStringValue(), nil
|
||||
}
|
||||
|
||||
// ResolveRelationKeyToPropertyName resolves the property key to the property's name
|
||||
func ResolveRelationKeyToPropertyName(mw apicore.ClientCommands, spaceId string, relationKey string) (property string, err error) {
|
||||
resp := mw.ObjectSearch(context.Background(), &pb.RpcObjectSearchRequest{
|
||||
SpaceId: spaceId,
|
||||
Filters: []*model.BlockContentDataviewFilter{
|
||||
{
|
||||
RelationKey: bundle.RelationKeyRelationKey.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.String(relationKey),
|
||||
},
|
||||
{
|
||||
RelationKey: bundle.RelationKeyResolvedLayout.String(),
|
||||
Condition: model.BlockContentDataviewFilter_Equal,
|
||||
Value: pbtypes.Int64(int64(model.ObjectType_relation)),
|
||||
},
|
||||
},
|
||||
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String(), bundle.RelationKeyResolvedLayout.String()},
|
||||
})
|
||||
|
||||
if resp.Error.Code != pb.RpcObjectSearchResponseError_NULL {
|
||||
return "", ErrFailedSearchProperty
|
||||
}
|
||||
|
||||
if len(resp.Records) == 0 {
|
||||
return "", ErrorPropertyNotFound
|
||||
}
|
||||
|
||||
return resp.Records[0].Fields[bundle.RelationKeyName.String()].GetStringValue(), nil
|
||||
}
|
||||
|
|
|
@ -267,12 +267,15 @@ func (s *service) createFavoriteWidget(spc clientspace.Space) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("get widget details: %w", err)
|
||||
}
|
||||
if widgetDetails.GetBool(bundle.RelationKeyAutoWidgetDisabled) {
|
||||
return nil
|
||||
}
|
||||
targetIds := widgetDetails.GetStringList(bundle.RelationKeyAutoWidgetTargets)
|
||||
if slices.Contains(targetIds, widget.DefaultWidgetFavorite) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cache.DoState(s.objectGetter, widgetObjectId, func(st *state.State, w widget.Widget) (err error) {
|
||||
return w.AddAutoWidget(st, widget.DefaultWidgetFavorite, widget.DefaultWidgetFavorite, "", model.BlockContentWidget_CompactList)
|
||||
return w.AddAutoWidget(st, widget.DefaultWidgetFavorite, widget.DefaultWidgetFavorite, "", model.BlockContentWidget_CompactList, widget.DefaultWidgetFavoriteEventName)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ func (p *Archive) autoInstallBinWidget() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if widgetDetails.GetBool(bundle.RelationKeyAutoWidgetDisabled) {
|
||||
return nil
|
||||
}
|
||||
keys := widgetDetails.Get(bundle.RelationKeyAutoWidgetTargets).StringList()
|
||||
if slices.Contains(keys, widget.DefaultWidgetBin) {
|
||||
// cache to avoid unnecessary objectstore requests
|
||||
|
@ -100,7 +103,7 @@ func (p *Archive) autoInstallBinWidget() error {
|
|||
st := sb.NewState()
|
||||
if w, ok := sb.(widget.Widget); ok {
|
||||
// We rely on AddAutoWidget to check if the widget was already installed/removed before
|
||||
err = w.AddAutoWidget(st, widget.DefaultWidgetBin, widget.DefaultWidgetBin, "", model.BlockContentWidget_Link)
|
||||
err = w.AddAutoWidget(st, widget.DefaultWidgetBin, widget.DefaultWidgetBin, "", model.BlockContentWidget_Link, widget.DefaultWidgetBinEventName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/core/block/simple"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/core/session"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
|
||||
|
@ -56,6 +57,7 @@ func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migra
|
|||
return migration.Migration{
|
||||
Version: 2,
|
||||
Proc: func(st *state.State) {
|
||||
// we purposefully do not add the ALl Objects widget here(as in migration3), because for new users we don't want to auto-create it
|
||||
template.InitTemplate(st,
|
||||
template.WithEmpty,
|
||||
template.WithObjectTypes([]domain.TypeKey{bundle.TypeKeyDashboard}),
|
||||
|
@ -112,6 +114,28 @@ func (w *WidgetObject) StateMigrations() migration.Migrations {
|
|||
|
||||
},
|
||||
},
|
||||
{
|
||||
Version: 3,
|
||||
Proc: func(s *state.State) {
|
||||
// add All Objects widget for existing spaces
|
||||
_, err := w.CreateBlock(s, &pb.RpcBlockCreateWidgetRequest{
|
||||
ContextId: s.RootId(),
|
||||
WidgetLayout: model.BlockContentWidget_Link,
|
||||
Position: model.Block_InnerFirst,
|
||||
TargetId: s.RootId(),
|
||||
ViewId: "",
|
||||
Block: &model.Block{
|
||||
Id: "allObjects",
|
||||
Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{
|
||||
TargetBlockId: widget.DefaultWidgetAll,
|
||||
}},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("all objects migration failed: %s", err.Error())
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/core/block/editor/state"
|
||||
"github.com/anyproto/anytype-heart/core/block/simple"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/core/event"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
|
@ -19,15 +20,20 @@ const (
|
|||
DefaultWidgetRecent = "recent"
|
||||
DefaultWidgetCollection = "collection"
|
||||
DefaultWidgetBin = "bin"
|
||||
DefaultWidgetAll = "allObjects"
|
||||
DefaultWidgetRecentOpen = "recentOpen"
|
||||
autoWidgetBlockIdPrefix = "auto_" // in case blockId is specifically provided to avoid bad tree merges
|
||||
autoWidgetBlockSuffix = "-wrapper" // in case blockId is specifically provided to avoid bad tree merges
|
||||
|
||||
DefaultWidgetFavoriteEventName = "Favorite"
|
||||
DefaultWidgetBinEventName = "Bin"
|
||||
)
|
||||
|
||||
type Widget interface {
|
||||
CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest) (string, error)
|
||||
// AddAutoWidget adds a widget block. If widget with the same targetId was installed/removed before, it will not be added again.
|
||||
// blockId is optional and used to protect from multi-device conflicts.
|
||||
AddAutoWidget(s *state.State, targetId, blockId, viewId string, layout model.BlockContentWidgetLayout) error
|
||||
// if eventName is empty no event is produced
|
||||
AddAutoWidget(s *state.State, targetId, blockId, viewId string, layout model.BlockContentWidgetLayout, eventName string) error
|
||||
}
|
||||
|
||||
type widget struct {
|
||||
|
@ -71,7 +77,11 @@ func NewWidget(sb smartblock.SmartBlock) Widget {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *widget) AddAutoWidget(st *state.State, targetId, widgetBlockId, viewId string, layout model.BlockContentWidgetLayout) error {
|
||||
func (w *widget) AddAutoWidget(st *state.State, targetId, widgetBlockId, viewId string, layout model.BlockContentWidgetLayout, eventName string) error {
|
||||
isDisabled := st.Details().Get(bundle.RelationKeyAutoWidgetDisabled).Bool()
|
||||
if isDisabled {
|
||||
return nil
|
||||
}
|
||||
targets := st.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
|
||||
if slices.Contains(targets, targetId) {
|
||||
return nil
|
||||
|
@ -125,7 +135,7 @@ func (w *widget) AddAutoWidget(st *state.State, targetId, widgetBlockId, viewId
|
|||
position = model.Block_Bottom
|
||||
}
|
||||
|
||||
_, err = w.CreateBlock(st, &pb.RpcBlockCreateWidgetRequest{
|
||||
_, err = w.createBlock(st, &pb.RpcBlockCreateWidgetRequest{
|
||||
ContextId: st.RootId(),
|
||||
ObjectLimit: 6,
|
||||
WidgetLayout: layout,
|
||||
|
@ -138,11 +148,30 @@ func (w *widget) AddAutoWidget(st *state.State, targetId, widgetBlockId, viewId
|
|||
TargetBlockId: targetId,
|
||||
}},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eventName != "" {
|
||||
msg := event.NewMessage(w.SpaceID(), &pb.EventMessageValueOfSpaceAutoWidgetAdded{
|
||||
SpaceAutoWidgetAdded: &pb.EventSpaceAutoWidgetAdded{
|
||||
TargetId: targetId,
|
||||
TargetName: eventName,
|
||||
WidgetBlockId: widgetBlockId,
|
||||
},
|
||||
})
|
||||
w.SendEvent([]*pb.EventMessage{msg})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *widget) CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest) (string, error) {
|
||||
return w.createBlock(s, req, false)
|
||||
}
|
||||
|
||||
func (w *widget) createBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest, isAutoAdded bool) (string, error) {
|
||||
if req.Block.Content == nil {
|
||||
return "", fmt.Errorf("block has no content")
|
||||
}
|
||||
|
@ -158,8 +187,8 @@ func (w *widget) CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest
|
|||
}
|
||||
|
||||
var wrapperBlockId string
|
||||
if b.Model().Id != "" {
|
||||
wrapperBlockId = autoWidgetBlockIdPrefix + b.Model().Id
|
||||
if b.Model().Id != "" && isAutoAdded {
|
||||
wrapperBlockId = b.Model().Id + autoWidgetBlockSuffix
|
||||
}
|
||||
|
||||
wrapper := simple.New(&model.Block{
|
||||
|
@ -169,9 +198,10 @@ func (w *widget) CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest
|
|||
},
|
||||
Content: &model.BlockContentOfWidget{
|
||||
Widget: &model.BlockContentWidget{
|
||||
Layout: req.WidgetLayout,
|
||||
Limit: req.ObjectLimit,
|
||||
ViewId: req.ViewId,
|
||||
Layout: req.WidgetLayout,
|
||||
Limit: req.ObjectLimit,
|
||||
ViewId: req.ViewId,
|
||||
AutoAdded: isAutoAdded,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -3,8 +3,10 @@ package export
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
@ -39,6 +41,7 @@ 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/database"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/gateway"
|
||||
"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/logging"
|
||||
|
@ -63,12 +66,15 @@ const (
|
|||
|
||||
FilesObjects = "filesObjects"
|
||||
Files = "files"
|
||||
|
||||
defaultFileName = "untitled"
|
||||
)
|
||||
|
||||
var log = logging.Logger("anytype-mw-export")
|
||||
|
||||
type Export interface {
|
||||
Export(ctx context.Context, req pb.RpcObjectListExportRequest) (path string, succeed int, err error)
|
||||
ExportSingleInMemory(ctx context.Context, spaceId string, objectId string, format model.ExportFormat) (res string, err error)
|
||||
app.Component
|
||||
}
|
||||
|
||||
|
@ -81,6 +87,7 @@ type export struct {
|
|||
accountService account.Service
|
||||
notificationService notifications.Notifications
|
||||
processService process.Service
|
||||
gatewayService gateway.Gateway
|
||||
}
|
||||
|
||||
func New() Export {
|
||||
|
@ -96,6 +103,7 @@ func (e *export) Init(a *app.App) (err error) {
|
|||
e.spaceService = app.MustComponent[space.Service](a)
|
||||
e.accountService = app.MustComponent[account.Service](a)
|
||||
e.notificationService = app.MustComponent[notifications.Notifications](a)
|
||||
e.gatewayService, _ = app.GetComponent[gateway.Gateway](a)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -118,6 +126,20 @@ func (e *export) Export(ctx context.Context, req pb.RpcObjectListExportRequest)
|
|||
return exportCtx.exportObjects(ctx, queue)
|
||||
}
|
||||
|
||||
func (e *export) ExportSingleInMemory(ctx context.Context, spaceId string, objectId string, format model.ExportFormat) (res string, err error) {
|
||||
req := pb.RpcObjectListExportRequest{
|
||||
SpaceId: spaceId,
|
||||
ObjectIds: []string{objectId},
|
||||
IncludeFiles: true,
|
||||
Format: format,
|
||||
IncludeNested: true,
|
||||
IncludeArchived: true,
|
||||
}
|
||||
|
||||
exportCtx := newExportContext(e, req)
|
||||
return exportCtx.exportObject(ctx, objectId)
|
||||
}
|
||||
|
||||
func (e *export) finishWithNotification(spaceId string, exportFormat model.ExportFormat, queue process.Queue, err error) {
|
||||
errCode := model.NotificationExport_NULL
|
||||
if err != nil {
|
||||
|
@ -168,7 +190,7 @@ type exportContext struct {
|
|||
relations map[string]struct{}
|
||||
setOfList map[string]struct{}
|
||||
objectTypes map[string]struct{}
|
||||
|
||||
gatewayUrl string
|
||||
*export
|
||||
}
|
||||
|
||||
|
@ -190,8 +212,10 @@ func newExportContext(e *export, req pb.RpcObjectListExportRequest) *exportConte
|
|||
setOfList: make(map[string]struct{}),
|
||||
objectTypes: make(map[string]struct{}),
|
||||
relations: make(map[string]struct{}),
|
||||
|
||||
export: e,
|
||||
export: e,
|
||||
}
|
||||
if e.gatewayService != nil {
|
||||
ec.gatewayUrl = "http://" + e.gatewayService.Addr()
|
||||
}
|
||||
return ec
|
||||
}
|
||||
|
@ -224,6 +248,41 @@ func (e *exportContext) getStateFilters(id string) *state.Filters {
|
|||
return nil
|
||||
}
|
||||
|
||||
// exportObject synchronously exports a single object and return the bytes slice
|
||||
func (e *exportContext) exportObject(ctx context.Context, objectId string) (string, error) {
|
||||
e.reqIds = []string{objectId}
|
||||
e.includeArchive = true
|
||||
err := e.docsForExport(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var docNamer Namer
|
||||
if e.format == model.Export_Markdown && e.gatewayUrl != "" {
|
||||
u, err := url.Parse(e.gatewayUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
docNamer = &deepLinkNamer{gatewayUrl: *u}
|
||||
} else {
|
||||
docNamer = newNamer()
|
||||
}
|
||||
inMemoryWriter := &InMemoryWriter{fn: docNamer}
|
||||
err = e.writeDoc(ctx, inMemoryWriter, objectId, e.docs.transformToDetailsMap())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, v := range inMemoryWriter.data {
|
||||
if e.format == model.Export_Protobuf {
|
||||
return base64.StdEncoding.EncodeToString(v), nil
|
||||
}
|
||||
return string(v), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to find data in writer")
|
||||
}
|
||||
|
||||
func (e *exportContext) exportObjects(ctx context.Context, queue process.Queue) (string, int, error) {
|
||||
var (
|
||||
err error
|
||||
|
@ -1258,6 +1317,9 @@ func (fn *namer) Get(path, hash, title, ext string) (name string) {
|
|||
title = slug.Make(strings.TrimSuffix(title, ext))
|
||||
name = text.TruncateEllipsized(title, fileLenLimit)
|
||||
name = strings.TrimSuffix(name, text.TruncateEllipsis)
|
||||
if name == "" {
|
||||
name = defaultFileName
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
b = 36
|
||||
|
|
|
@ -4,22 +4,28 @@ import (
|
|||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/mill"
|
||||
"github.com/anyproto/anytype-heart/util/anyerror"
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
Path() string
|
||||
Namer() *namer
|
||||
Namer() Namer
|
||||
WriteFile(filename string, r io.Reader, lastModifiedDate int64) (err error)
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
type Namer interface {
|
||||
Get(path, hash, title, ext string) (name string)
|
||||
}
|
||||
|
||||
func uniqName() string {
|
||||
return time.Now().Format("Anytype.20060102.150405.99")
|
||||
}
|
||||
|
@ -44,7 +50,7 @@ type dirWriter struct {
|
|||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (d *dirWriter) Namer() *namer {
|
||||
func (d *dirWriter) Namer() Namer {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fn == nil {
|
||||
|
@ -108,7 +114,7 @@ type zipWriter struct {
|
|||
fn *namer
|
||||
}
|
||||
|
||||
func (d *zipWriter) Namer() *namer {
|
||||
func (d *zipWriter) Namer() Namer {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fn == nil {
|
||||
|
@ -149,3 +155,66 @@ func (d *zipWriter) Close() (err error) {
|
|||
func getZipName(path string) string {
|
||||
return filepath.Join(path, uniqName()+".zip")
|
||||
}
|
||||
|
||||
type InMemoryWriter struct {
|
||||
data map[string][]byte
|
||||
fn Namer
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) Namer() Namer {
|
||||
return d.fn
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) Path() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) WriteFile(filename string, r io.Reader, lastModifiedDate int64) (err error) {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.data == nil {
|
||||
d.data = make(map[string][]byte)
|
||||
}
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d.data[filename] = b
|
||||
return
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) Close() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) GetData(id string) []byte {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
return d.data[id]
|
||||
}
|
||||
|
||||
// deepLinkNamer used to render a single-object export, in md format
|
||||
type deepLinkNamer struct {
|
||||
gatewayUrl url.URL
|
||||
}
|
||||
|
||||
func (fn *deepLinkNamer) Get(path, hash, title, ext string) (name string) {
|
||||
if ext == ".md" {
|
||||
// object links via deeplink to the app
|
||||
return "anytype://object?objectId=" + hash
|
||||
}
|
||||
|
||||
// files links via gateway
|
||||
if fn.gatewayUrl.Host == "" {
|
||||
return "anytype://object?objectId=" + hash
|
||||
}
|
||||
u := fn.gatewayUrl
|
||||
if mill.IsImageExt(ext) {
|
||||
u.Path = "image/" + hash
|
||||
} else {
|
||||
u.Path = "file/" + hash
|
||||
}
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
|
|
@ -75,3 +75,22 @@ func TestZipWriter_WriteFile(t *testing.T) {
|
|||
}
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestZipWriter_Get(t *testing.T) {
|
||||
t.Run("file without name", func(t *testing.T) {
|
||||
// given
|
||||
path, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(path)
|
||||
|
||||
wr, err := newZipWriter(path, uniqName()+".zip")
|
||||
require.NoError(t, err)
|
||||
|
||||
// when
|
||||
name := wr.Namer().Get(Files, "hash", "", "")
|
||||
|
||||
// then
|
||||
require.NoError(t, wr.Close())
|
||||
assert.Equal(t, filepath.Join(Files, defaultFileName), name)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/anyproto/anytype-heart/core/block/simple"
|
||||
simpleDataview "github.com/anyproto/anytype-heart/core/block/simple/dataview"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
sb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
|
@ -22,25 +21,52 @@ type ImportCollectionSetting struct {
|
|||
collectionName string
|
||||
targetObjects []string
|
||||
icon string
|
||||
fileKeys []*pb.ChangeFileKeys
|
||||
needToAddDate, shouldBeFavorite, shouldAddRelations bool
|
||||
}
|
||||
|
||||
func MakeImportCollectionSetting(
|
||||
collectionName string,
|
||||
targetObjects []string,
|
||||
icon string,
|
||||
fileKeys []*pb.ChangeFileKeys,
|
||||
needToAddDate, shouldBeFavorite, shouldAddRelations bool,
|
||||
) *ImportCollectionSetting {
|
||||
return &ImportCollectionSetting{
|
||||
collectionName: collectionName,
|
||||
targetObjects: targetObjects,
|
||||
icon: icon,
|
||||
fileKeys: fileKeys,
|
||||
needToAddDate: needToAddDate,
|
||||
shouldBeFavorite: shouldBeFavorite,
|
||||
shouldAddRelations: shouldAddRelations,
|
||||
type ImportCollectionOption func(*ImportCollectionSetting)
|
||||
|
||||
func NewImportCollectionSetting(opts ...ImportCollectionOption) *ImportCollectionSetting {
|
||||
s := &ImportCollectionSetting{}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func WithCollectionName(name string) ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.collectionName = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithTargetObjects(objs []string) ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.targetObjects = objs
|
||||
}
|
||||
}
|
||||
|
||||
func WithIcon(icon string) ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.icon = icon
|
||||
}
|
||||
}
|
||||
|
||||
func WithAddDate() ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.needToAddDate = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithFavorite() ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.shouldBeFavorite = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithRelations() ImportCollectionOption {
|
||||
return func(s *ImportCollectionSetting) {
|
||||
s.shouldAddRelations = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,14 +101,13 @@ func (r *ImportCollection) MakeImportCollection(req *ImportCollectionSetting) (*
|
|||
detailsStruct = st.CombinedDetails().Merge(detailsStruct)
|
||||
st.UpdateStoreSlice(template.CollectionStoreKey, req.targetObjects)
|
||||
|
||||
return r.getRootCollectionSnapshot(req.collectionName, st, detailsStruct, req.fileKeys), nil
|
||||
return r.getRootCollectionSnapshot(req.collectionName, st, detailsStruct), nil
|
||||
}
|
||||
|
||||
func (r *ImportCollection) getRootCollectionSnapshot(
|
||||
collectionName string,
|
||||
st *state.State,
|
||||
detailsStruct *domain.Details,
|
||||
fileKeys []*pb.ChangeFileKeys,
|
||||
) *Snapshot {
|
||||
if detailsStruct == nil {
|
||||
detailsStruct = domain.NewDetails()
|
||||
|
@ -100,7 +125,6 @@ func (r *ImportCollection) getRootCollectionSnapshot(
|
|||
RelationLinks: st.GetRelationLinks(),
|
||||
Collections: st.Store(),
|
||||
},
|
||||
FileKeys: fileKeys,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
61
core/block/import/common/collection_test.go
Normal file
61
core/block/import/common/collection_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/block/collection"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
)
|
||||
|
||||
func TestMakeImportCollection(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
needToAddDate bool
|
||||
shouldBeFavorite bool
|
||||
shouldAddRelation bool
|
||||
}{
|
||||
{"all false", false, false, false},
|
||||
{"add date", true, false, false},
|
||||
{"add favorite", false, true, false},
|
||||
{"add relations", false, false, true},
|
||||
{"all True", true, true, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
importer := NewImportCollection(collection.New())
|
||||
|
||||
req := NewImportCollectionSetting(
|
||||
WithCollectionName("My Collection"),
|
||||
WithTargetObjects([]string{"obj1", "obj2"}),
|
||||
WithIcon("icon.png"),
|
||||
)
|
||||
|
||||
req.needToAddDate = tt.needToAddDate
|
||||
req.shouldBeFavorite = tt.shouldBeFavorite
|
||||
req.shouldAddRelations = tt.shouldAddRelation
|
||||
|
||||
root, err := importer.MakeImportCollection(req)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, root)
|
||||
|
||||
if tt.needToAddDate {
|
||||
assert.Contains(t, root.FileName, time.Now().Format("2006"))
|
||||
} else {
|
||||
assert.Equal(t, "My Collection", root.FileName)
|
||||
}
|
||||
|
||||
if tt.shouldBeFavorite {
|
||||
assert.Equal(t, domain.Bool(true), root.Snapshot.Data.Details.Get(bundle.RelationKeyIsFavorite))
|
||||
} else {
|
||||
assert.Equal(t, domain.Bool(false), root.Snapshot.Data.Details.Get(bundle.RelationKeyIsFavorite))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
|
@ -67,7 +67,12 @@ func (c *CSV) GetSnapshots(ctx context.Context, req *pb.RpcObjectImportRequest,
|
|||
return nil, allErrors
|
||||
}
|
||||
rootCollection := common.NewImportCollection(c.collectionService)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, result.objectIDs, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(result.objectIDs),
|
||||
common.WithAddDate(),
|
||||
common.WithRelations(),
|
||||
)
|
||||
rootCol, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
allErrors.Add(err)
|
||||
|
|
|
@ -66,7 +66,12 @@ func (h *HTML) GetSnapshots(ctx context.Context, req *pb.RpcObjectImportRequest,
|
|||
return nil, allErrors
|
||||
}
|
||||
rootCollection := common.NewImportCollection(h.collectionService)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, targetObjects, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(targetObjects),
|
||||
common.WithAddDate(),
|
||||
common.WithRelations(),
|
||||
)
|
||||
rootCollectionSnapshot, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
allErrors.Add(err)
|
||||
|
|
|
@ -70,6 +70,7 @@ type Import struct {
|
|||
|
||||
importCtx context.Context
|
||||
importCtxCancel context.CancelFunc
|
||||
spaceService space.Service
|
||||
}
|
||||
|
||||
func New() Importer {
|
||||
|
@ -82,6 +83,7 @@ func (i *Import) Init(a *app.App) (err error) {
|
|||
i.s = app.MustComponent[*block.Service](a)
|
||||
accountService := app.MustComponent[account.Service](a)
|
||||
spaceService := app.MustComponent[space.Service](a)
|
||||
i.spaceService = spaceService
|
||||
col := app.MustComponent[*collection.Service](a)
|
||||
i.tempDirProvider = app.MustComponent[core.TempDirProvider](a)
|
||||
converters := []common.Converter{
|
||||
|
@ -180,10 +182,33 @@ func (i *Import) importObjects(ctx context.Context, importRequest *ImportRequest
|
|||
func (i *Import) onImportFinish(res *ImportResponse, req *ImportRequest, importId string) {
|
||||
i.finishImportProcess(res.Err, req)
|
||||
i.sendFileEvents(res.Err)
|
||||
if res.RootCollectionId != "" {
|
||||
i.addRootCollectionWidget(res, req)
|
||||
}
|
||||
i.recordEvent(&metrics.ImportFinishedEvent{ID: importId, ImportType: req.Type.String()})
|
||||
i.sendImportFinishEventToClient(res.RootCollectionId, req.IsSync, res.ObjectsCount, req.Type)
|
||||
}
|
||||
|
||||
func (i *Import) addRootCollectionWidget(res *ImportResponse, req *ImportRequest) {
|
||||
spc, err := i.spaceService.Get(i.importCtx, req.SpaceId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create widget from root collection, error: %s", err.Error())
|
||||
} else {
|
||||
_, err = i.s.CreateWidgetBlock(nil, &pb.RpcBlockCreateWidgetRequest{
|
||||
ContextId: spc.DerivedIDs().Widgets,
|
||||
WidgetLayout: model.BlockContentWidget_CompactList,
|
||||
Block: &model.Block{
|
||||
Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{
|
||||
TargetBlockId: res.RootCollectionId,
|
||||
}},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to create widget from root collection, error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Import) sendFileEvents(returnedErr error) {
|
||||
if returnedErr == nil {
|
||||
i.fileSync.SendImportEvents()
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/core/block/import/web/parsers/mock_parsers"
|
||||
"github.com/anyproto/anytype-heart/core/block/process"
|
||||
"github.com/anyproto/anytype-heart/core/domain"
|
||||
"github.com/anyproto/anytype-heart/space/mock_space"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/block/import/common"
|
||||
"github.com/anyproto/anytype-heart/core/block/import/common/mock_common"
|
||||
|
@ -971,6 +972,9 @@ func Test_ImportRootCollectionInResponse(t *testing.T) {
|
|||
fileSync.EXPECT().SendImportEvents().Return().Times(1)
|
||||
fileSync.EXPECT().ClearImportEvents().Return().Times(1)
|
||||
i.fileSync = fileSync
|
||||
mockSpaceService := mock_space.NewMockService(t)
|
||||
mockSpaceService.EXPECT().Get(nil, "space1").Return(nil, fmt.Errorf("not found"))
|
||||
i.spaceService = mockSpaceService
|
||||
|
||||
// when
|
||||
importRequest := &ImportRequest{
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/gogo/protobuf/types"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/mill"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
@ -98,7 +99,6 @@ func ConvertTextToFile(filePath string) *model.BlockContentOfFile {
|
|||
return nil
|
||||
}
|
||||
|
||||
imageFormats := []string{"jpg", "jpeg", "png", "gif", "webp"}
|
||||
videoFormats := []string{"mp4", "m4v", "mov"}
|
||||
audioFormats := []string{"mp3", "ogg", "wav", "m4a", "flac"}
|
||||
pdfFormat := "pdf"
|
||||
|
@ -107,11 +107,8 @@ func ConvertTextToFile(filePath string) *model.BlockContentOfFile {
|
|||
fileExt := filepath.Ext(filePath)
|
||||
if fileExt != "" {
|
||||
fileExt = fileExt[1:]
|
||||
for _, ext := range imageFormats {
|
||||
if strings.EqualFold(fileExt, ext) {
|
||||
fileType = model.BlockContentFile_Image
|
||||
break
|
||||
}
|
||||
if mill.IsImageExt(fileExt) {
|
||||
fileType = model.BlockContentFile_Image
|
||||
}
|
||||
|
||||
for _, ext := range videoFormats {
|
||||
|
|
|
@ -102,7 +102,12 @@ func (m *Markdown) processFiles(req *pb.RpcObjectImportRequest, progress process
|
|||
|
||||
func (m *Markdown) createRootCollection(allSnapshots []*common.Snapshot, allRootObjectsIds []string) ([]*common.Snapshot, string, error) {
|
||||
rootCollection := common.NewImportCollection(m.service)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, allRootObjectsIds, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(allRootObjectsIds),
|
||||
common.WithAddDate(),
|
||||
common.WithRelations(),
|
||||
)
|
||||
rootCol, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
@ -424,7 +429,10 @@ func (m *Markdown) createSnapshots(
|
|||
|
||||
func (m *Markdown) addCollectionSnapshot(fileName string, file *FileInfo, snapshots []*common.Snapshot) ([]*common.Snapshot, error) {
|
||||
c := common.NewImportCollection(m.service)
|
||||
settings := common.MakeImportCollectionSetting(file.Title, file.CollectionsObjectsIds, "", nil, false, false, false)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(file.Title),
|
||||
common.WithTargetObjects(file.CollectionsObjectsIds),
|
||||
)
|
||||
csvCollection, err := c.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -351,7 +351,12 @@ func (ds *Service) AddObjectsToNotionCollection(notionContext *api.NotionImportC
|
|||
allObjects := ds.filterObjects(notionContext, notionDB, notionPages)
|
||||
|
||||
rootCollection := common.NewImportCollection(ds.collectionService)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, allObjects, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(allObjects),
|
||||
common.WithAddDate(),
|
||||
common.WithRelations(),
|
||||
)
|
||||
rootCol, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -2,7 +2,6 @@ package pb
|
|||
|
||||
import (
|
||||
"github.com/globalsign/mgo/bson"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/anyproto/anytype-heart/core/block/collection"
|
||||
"github.com/anyproto/anytype-heart/core/block/editor/state"
|
||||
|
@ -40,13 +39,9 @@ func (g *GalleryImport) ProvideCollection(snapshots []*common.Snapshot,
|
|||
if widget != nil {
|
||||
widgetObjects = g.getObjectsFromWidgets(widget)
|
||||
}
|
||||
var (
|
||||
icon string
|
||||
fileKeys []*pb.ChangeFileKeys
|
||||
)
|
||||
var icon string
|
||||
if workspaceSnapshot != nil { // we use space icon for import collection
|
||||
icon = workspaceSnapshot.Snapshot.Data.Details.GetString(bundle.RelationKeyIconImage)
|
||||
fileKeys = lo.Filter(workspaceSnapshot.Snapshot.FileKeys, func(item *pb.ChangeFileKeys, index int) bool { return item.Hash == icon })
|
||||
}
|
||||
collectionName := params.GetCollectionTitle() // collection name should be the name of experience
|
||||
if collectionName == "" {
|
||||
|
@ -54,13 +49,18 @@ func (g *GalleryImport) ProvideCollection(snapshots []*common.Snapshot,
|
|||
}
|
||||
rootCollection := common.NewImportCollection(g.service)
|
||||
if len(widgetObjects) > 0 {
|
||||
collectionsSnapshots, err = g.getWidgetsCollection(collectionName, rootCollection, widgetObjects, icon, fileKeys, widget, collectionsSnapshots)
|
||||
collectionsSnapshots, err = g.getWidgetsCollection(collectionName, rootCollection, widgetObjects, icon, widget, collectionsSnapshots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
objectsIDs := g.getObjectsIDs(snapshots)
|
||||
settings := common.MakeImportCollectionSetting(collectionName, objectsIDs, icon, fileKeys, false, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(collectionName),
|
||||
common.WithTargetObjects(objectsIDs),
|
||||
common.WithIcon(icon),
|
||||
common.WithRelations(),
|
||||
)
|
||||
objectsCollection, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -73,12 +73,16 @@ func (g *GalleryImport) getWidgetsCollection(collectionName string,
|
|||
rootCollection *common.ImportCollection,
|
||||
widgetObjects []string,
|
||||
icon string,
|
||||
fileKeys []*pb.ChangeFileKeys,
|
||||
widget *common.Snapshot,
|
||||
collectionsSnapshots []*common.Snapshot,
|
||||
) ([]*common.Snapshot, error) {
|
||||
widgetCollectionName := collectionName + widgetCollectionPattern
|
||||
settings := common.MakeImportCollectionSetting(widgetCollectionName, widgetObjects, icon, fileKeys, false, false, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(widgetCollectionName),
|
||||
common.WithTargetObjects(widgetObjects),
|
||||
common.WithIcon(icon),
|
||||
common.WithRelations(),
|
||||
)
|
||||
widgetsCollectionSnapshot, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -56,7 +56,12 @@ func (s *SpaceImport) ProvideCollection(snapshots []*common.Snapshot,
|
|||
})
|
||||
}
|
||||
rootCollection := common.NewImportCollection(s.service)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, rootObjects, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(rootObjects),
|
||||
common.WithRelations(),
|
||||
common.WithAddDate(),
|
||||
)
|
||||
rootCollectionSnapshot, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -56,7 +56,12 @@ func (t *TXT) GetSnapshots(ctx context.Context, req *pb.RpcObjectImportRequest,
|
|||
return nil, allErrors
|
||||
}
|
||||
rootCollection := common.NewImportCollection(t.service)
|
||||
settings := common.MakeImportCollectionSetting(rootCollectionName, targetObjects, "", nil, true, true, true)
|
||||
settings := common.NewImportCollectionSetting(
|
||||
common.WithCollectionName(rootCollectionName),
|
||||
common.WithTargetObjects(targetObjects),
|
||||
common.WithRelations(),
|
||||
common.WithAddDate(),
|
||||
)
|
||||
rootCol, err := rootCollection.MakeImportCollection(settings)
|
||||
if err != nil {
|
||||
allErrors.Add(err)
|
||||
|
|
|
@ -95,8 +95,12 @@ func (s *Service) CreateTypeWidgetIfMissing(ctx context.Context, spaceId string,
|
|||
return err
|
||||
}
|
||||
widgetObjectId := space.DerivedIDs().Widgets
|
||||
widgetDetails, err := s.objectStore.SpaceIndex(space.Id()).GetDetails(widgetObjectId)
|
||||
spaceIndex := s.objectStore.SpaceIndex(space.Id())
|
||||
widgetDetails, err := spaceIndex.GetDetails(widgetObjectId)
|
||||
if err == nil {
|
||||
if widgetDetails.GetBool(bundle.RelationKeyAutoWidgetDisabled) {
|
||||
return nil
|
||||
}
|
||||
keys := widgetDetails.Get(bundle.RelationKeyAutoWidgetTargets).StringList()
|
||||
if slices.Contains(keys, typeId) {
|
||||
// widget was created before
|
||||
|
@ -104,7 +108,7 @@ func (s *Service) CreateTypeWidgetIfMissing(ctx context.Context, spaceId string,
|
|||
}
|
||||
}
|
||||
// this is not optimal, maybe it should be some cheaper way
|
||||
records, err := s.objectStore.SpaceIndex(space.Id()).QueryRaw(&database.Filters{FilterObj: database.FiltersAnd{
|
||||
records, err := spaceIndex.QueryRaw(&database.Filters{FilterObj: database.FiltersAnd{
|
||||
database.FilterEq{
|
||||
Key: bundle.RelationKeyType,
|
||||
Cond: model.BlockContentDataviewFilter_Equal,
|
||||
|
@ -128,7 +132,16 @@ func (s *Service) CreateTypeWidgetIfMissing(ctx context.Context, spaceId string,
|
|||
// only create widget if this was the first object of this type created
|
||||
return nil
|
||||
}
|
||||
|
||||
var targetName string
|
||||
typeDetails, err := spaceIndex.GetDetails(typeId)
|
||||
if err == nil {
|
||||
targetName = typeDetails.Get(bundle.RelationKeyPluralName).String()
|
||||
if targetName == "" {
|
||||
targetName = typeDetails.Get(bundle.RelationKeyName).String()
|
||||
}
|
||||
}
|
||||
return cache.DoState(s, widgetObjectId, func(st *state.State, w widget.Widget) (err error) {
|
||||
return w.AddAutoWidget(st, typeId, key.String(), addr.ObjectTypeAllViewId, model.BlockContentWidget_View)
|
||||
return w.AddAutoWidget(st, typeId, key.String(), addr.ObjectTypeAllViewId, model.BlockContentWidget_View, targetName)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ func (c config) GetYamux() yamux.Config {
|
|||
|
||||
func (c config) GetQuic() quic.Config {
|
||||
return quic.Config{
|
||||
WriteTimeoutSec: 60,
|
||||
WriteTimeoutSec: 1200,
|
||||
DialTimeoutSec: 60,
|
||||
KeepAlivePeriodSec: 120,
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ func (es *GrpcSender) sendEvent(server SessionServer, event *pb.Event) {
|
|||
if s, ok := status.FromError(err); ok && s.Code() == codes.Unavailable {
|
||||
es.shutdownCh <- server.Token
|
||||
}
|
||||
if strings.Contains(err.Error(), "rpc error: code = Unavailable desc = transport is closing") {
|
||||
if strings.Contains(err.Error(), "transport is closing") {
|
||||
log.Errorf("failed to send event: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,3 +37,31 @@ func (mw *Middleware) ObjectListExport(cctx context.Context, req *pb.RpcObjectLi
|
|||
})
|
||||
return response(path, succeed, err)
|
||||
}
|
||||
|
||||
func (mw *Middleware) ObjectExport(cctx context.Context, req *pb.RpcObjectExportRequest) *pb.RpcObjectExportResponse {
|
||||
response := func(result string, err error) (res *pb.RpcObjectExportResponse) {
|
||||
res = &pb.RpcObjectExportResponse{
|
||||
Error: &pb.RpcObjectExportResponseError{
|
||||
Code: pb.RpcObjectExportResponseError_NULL,
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
res.Error.Code = pb.RpcObjectExportResponseError_UNKNOWN_ERROR
|
||||
res.Error.Description = getErrorDescription(err)
|
||||
return
|
||||
} else {
|
||||
res.Result = result
|
||||
}
|
||||
return res
|
||||
}
|
||||
var (
|
||||
result string
|
||||
err error
|
||||
)
|
||||
err = mw.doBlockService(func(_ *block.Service) error {
|
||||
es := mw.applicationService.GetApp().MustComponent(export.CName).(export.Export)
|
||||
result, err = es.ExportSingleInMemory(cctx, req.SpaceId, req.ObjectId, req.Format)
|
||||
return err
|
||||
})
|
||||
return response(result, err)
|
||||
}
|
||||
|
|
|
@ -210,6 +210,9 @@ func logIndexLoop(err error) {
|
|||
if errors.Is(err, rpcstore.ErrNoConnectionToAnyFileClient) {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, files.FailedProtoUnmarshallError) {
|
||||
return
|
||||
}
|
||||
log.Errorf("index loop: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,10 @@ const (
|
|||
CName = "files"
|
||||
)
|
||||
|
||||
var log = logging.Logger("anytype-files")
|
||||
var (
|
||||
log = logging.Logger("anytype-files")
|
||||
FailedProtoUnmarshallError = errors.New("failed proto unmarshall")
|
||||
)
|
||||
|
||||
var _ Service = (*service)(nil)
|
||||
|
||||
|
@ -306,7 +309,7 @@ func (s *service) fileInfoFromPath(ctx context.Context, spaceId string, fileId d
|
|||
}
|
||||
err = proto.Unmarshal(b, &file)
|
||||
if err != nil || file.Hash == "" {
|
||||
return nil, fmt.Errorf("failed to unmarshal not-encrypted file info: %w", err)
|
||||
return nil, fmt.Errorf("failed to unmarshal not-encrypted file info: %w", FailedProtoUnmarshallError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -349,7 +349,6 @@ func (s *fileSync) uploadFile(ctx context.Context, spaceID string, fileId domain
|
|||
if err != nil {
|
||||
log.Warn("delete limit reached error logged", zap.String("fileId", fileId.String()), zap.Error(err))
|
||||
}
|
||||
log.Warn("done upload", zap.String("fileId", fileId.String()), zap.Int("bytesToUpload", blocksAvailability.bytesToUpload), zap.Int("bytesUploaded", totalBytesUploaded))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ func uniqName() string {
|
|||
return time.Now().Format("Anytype.WebPublish.20060102.150405.99")
|
||||
}
|
||||
|
||||
func (s *service) exportToDir(ctx context.Context, spaceId, pageId string) (dirEntries []fs.DirEntry, exportPath string, err error) {
|
||||
func (s *service) exportToDir(ctx context.Context, spaceId, pageId string, includeSpaceInfo bool) (dirEntries []fs.DirEntry, exportPath string, err error) {
|
||||
tempDir := os.TempDir()
|
||||
exportPath, _, err = s.exportService.Export(ctx, pb.RpcObjectListExportRequest{
|
||||
SpaceId: spaceId,
|
||||
|
@ -137,7 +137,7 @@ func (s *service) exportToDir(ctx context.Context, spaceId, pageId string) (dirE
|
|||
NoProgress: true,
|
||||
IncludeNested: true,
|
||||
IncludeBacklinks: true,
|
||||
IncludeSpace: true,
|
||||
IncludeSpace: includeSpaceInfo,
|
||||
LinksStateFilters: &pb.RpcObjectListExportStateFilters{
|
||||
RelationsWhiteList: relationsWhiteListToPbModel(),
|
||||
RemoveBlocks: true,
|
||||
|
@ -155,7 +155,12 @@ func (s *service) exportToDir(ctx context.Context, spaceId, pageId string) (dirE
|
|||
}
|
||||
|
||||
func (s *service) publishToPublishServer(ctx context.Context, spaceId, pageId, uri, globalName string, joinSpace bool) (err error) {
|
||||
dirEntries, exportPath, err := s.exportToDir(ctx, spaceId, pageId)
|
||||
spc, err := s.spaceService.Get(ctx, spaceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includeInviteLinkAndSpaceInfo := joinSpace && !spc.IsPersonal()
|
||||
dirEntries, exportPath, err := s.exportToDir(ctx, spaceId, pageId, includeInviteLinkAndSpaceInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -177,12 +182,7 @@ func (s *service) publishToPublishServer(ctx context.Context, spaceId, pageId, u
|
|||
return err
|
||||
}
|
||||
|
||||
spc, err := s.spaceService.Get(ctx, spaceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.applyInviteLink(ctx, spc, &uberSnapshot, joinSpace)
|
||||
err = s.applyInviteLink(ctx, spaceId, &uberSnapshot, includeInviteLinkAndSpaceInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -210,12 +210,18 @@ func (s *service) publishToPublishServer(ctx context.Context, spaceId, pageId, u
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *service) applyInviteLink(ctx context.Context, spc clientspace.Space, snapshot *PublishingUberSnapshot, joinSpace bool) error {
|
||||
inviteLink, err := s.extractInviteLink(ctx, spc.Id(), joinSpace, spc.IsPersonal())
|
||||
func (s *service) applyInviteLink(ctx context.Context, spaceId string, snapshot *PublishingUberSnapshot, includeInviteLink bool) error {
|
||||
if !includeInviteLink {
|
||||
return nil
|
||||
}
|
||||
inviteInfo, err := s.inviteService.GetCurrent(ctx, spaceId)
|
||||
if err != nil && errors.Is(err, inviteservice.ErrInviteNotExists) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot.Meta.InviteLink = inviteLink
|
||||
snapshot.Meta.InviteLink = fmt.Sprintf(inviteLinkUrlTemplate, inviteInfo.InviteFileCid, inviteInfo.InviteFileKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -366,21 +372,6 @@ func (s *service) publishToServer(ctx context.Context, spaceId, pageId, uri, ver
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *service) extractInviteLink(ctx context.Context, spaceId string, joinSpace, isPersonal bool) (string, error) {
|
||||
var inviteLink string
|
||||
if joinSpace && !isPersonal {
|
||||
inviteInfo, err := s.inviteService.GetCurrent(ctx, spaceId)
|
||||
if err != nil && errors.Is(err, inviteservice.ErrInviteNotExists) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inviteLink = fmt.Sprintf(inviteLinkUrlTemplate, inviteInfo.InviteFileCid, inviteInfo.InviteFileKey)
|
||||
}
|
||||
return inviteLink, nil
|
||||
}
|
||||
|
||||
func (s *service) evaluateDocumentVersion(ctx context.Context, spc clientspace.Space, pageId string, joinSpace bool) (string, error) {
|
||||
treeStorage, err := spc.Storage().TreeStorage(ctx, pageId)
|
||||
if err != nil {
|
||||
|
|
|
@ -139,7 +139,9 @@ func TestPublish(t *testing.T) {
|
|||
t.Run("success", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := true
|
||||
spaceService, err := prepareSpaceService(t, isPersonal)
|
||||
includeSpaceInfo := false
|
||||
|
||||
spaceService, err := prepareSpaceService(t, isPersonal, includeSpaceInfo)
|
||||
|
||||
objectTypeId := "customObjectType"
|
||||
expectedUri := "test"
|
||||
|
@ -159,7 +161,7 @@ func TestPublish(t *testing.T) {
|
|||
identityService := mock_identity.NewMockService(t)
|
||||
identityService.EXPECT().GetMyProfileDetails(context.Background()).Return("identity", nil, domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{}))
|
||||
|
||||
exp := prepareExporter(t, objectTypeId, spaceService)
|
||||
exp := prepareExporter(t, objectTypeId, spaceService, false)
|
||||
|
||||
svc := &service{
|
||||
spaceService: spaceService,
|
||||
|
@ -169,7 +171,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, false)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
|
@ -182,7 +184,9 @@ func TestPublish(t *testing.T) {
|
|||
t.Run("success with space sharing", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := false
|
||||
spaceService, err := prepareSpaceService(t, isPersonal)
|
||||
includeSpaceInfo := true
|
||||
|
||||
spaceService, err := prepareSpaceService(t, isPersonal, includeSpaceInfo)
|
||||
|
||||
objectTypeId := "customObjectType"
|
||||
expectedUri := "test"
|
||||
|
@ -206,7 +210,7 @@ func TestPublish(t *testing.T) {
|
|||
identityService := mock_identity.NewMockService(t)
|
||||
identityService.EXPECT().GetMyProfileDetails(context.Background()).Return("identity", nil, domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{}))
|
||||
|
||||
exp := prepareExporter(t, objectTypeId, spaceService)
|
||||
exp := prepareExporter(t, objectTypeId, spaceService, includeSpaceInfo)
|
||||
|
||||
inviteService := mock_inviteservice.NewMockInviteService(t)
|
||||
inviteService.EXPECT().GetCurrent(context.Background(), "spaceId").Return(domain.InviteInfo{
|
||||
|
@ -223,7 +227,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
|
@ -235,7 +239,9 @@ func TestPublish(t *testing.T) {
|
|||
})
|
||||
t.Run("success with space sharing - invite not exists", func(t *testing.T) {
|
||||
isPersonal := false
|
||||
spaceService, err := prepareSpaceService(t, isPersonal)
|
||||
includeSpaceInfo := true
|
||||
|
||||
spaceService, err := prepareSpaceService(t, isPersonal, includeSpaceInfo)
|
||||
|
||||
objectTypeId := "customObjectType"
|
||||
expectedUri := "test"
|
||||
|
@ -256,7 +262,7 @@ func TestPublish(t *testing.T) {
|
|||
identityService := mock_identity.NewMockService(t)
|
||||
identityService.EXPECT().GetMyProfileDetails(context.Background()).Return("identity", nil, domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{}))
|
||||
|
||||
exp := prepareExporter(t, objectTypeId, spaceService)
|
||||
exp := prepareExporter(t, objectTypeId, spaceService, includeSpaceInfo)
|
||||
|
||||
inviteService := mock_inviteservice.NewMockInviteService(t)
|
||||
inviteService.EXPECT().GetCurrent(context.Background(), "spaceId").Return(domain.InviteInfo{}, inviteservice.ErrInviteNotExists)
|
||||
|
@ -270,7 +276,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
|
@ -283,7 +289,8 @@ func TestPublish(t *testing.T) {
|
|||
t.Run("success for member", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := false
|
||||
spaceService, err := prepareSpaceService(t, isPersonal)
|
||||
includeSpaceInfo := true
|
||||
spaceService, err := prepareSpaceService(t, isPersonal, includeSpaceInfo)
|
||||
|
||||
objectTypeId := "customObjectType"
|
||||
expectedUri := "test"
|
||||
|
@ -311,7 +318,7 @@ func TestPublish(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
exp := prepareExporter(t, objectTypeId, spaceService)
|
||||
exp := prepareExporter(t, objectTypeId, spaceService, includeSpaceInfo)
|
||||
|
||||
inviteService := mock_inviteservice.NewMockInviteService(t)
|
||||
inviteService.EXPECT().GetCurrent(context.Background(), "spaceId").Return(domain.InviteInfo{
|
||||
|
@ -328,7 +335,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
|
@ -341,7 +348,9 @@ func TestPublish(t *testing.T) {
|
|||
t.Run("internal error", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := true
|
||||
spaceService, err := prepareSpaceService(t, isPersonal)
|
||||
includeSpaceInfo := true
|
||||
|
||||
spaceService, err := prepareSpaceService(t, isPersonal, includeSpaceInfo)
|
||||
|
||||
objectTypeId := "customObjectType"
|
||||
expectedUri := "test"
|
||||
|
@ -363,7 +372,7 @@ func TestPublish(t *testing.T) {
|
|||
expectedErr: fmt.Errorf("internal error"),
|
||||
}
|
||||
|
||||
exp := prepareExporter(t, objectTypeId, spaceService)
|
||||
exp := prepareExporter(t, objectTypeId, spaceService, false)
|
||||
|
||||
svc := &service{
|
||||
spaceService: spaceService,
|
||||
|
@ -373,7 +382,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.Error(t, err)
|
||||
|
@ -385,7 +394,14 @@ func TestPublish(t *testing.T) {
|
|||
})
|
||||
t.Run("limit error for members", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := false
|
||||
includeSpaceInfo := true
|
||||
|
||||
spaceService := mock_space.NewMockService(t)
|
||||
space := mock_clientspace.NewMockSpace(t)
|
||||
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
|
||||
space.EXPECT().IsPersonal().Return(isPersonal)
|
||||
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
|
||||
|
||||
expectedUri := "test"
|
||||
testFile := "test"
|
||||
|
@ -415,7 +431,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.Error(t, err)
|
||||
|
@ -424,7 +440,14 @@ func TestPublish(t *testing.T) {
|
|||
})
|
||||
t.Run("default limit error", func(t *testing.T) {
|
||||
// given
|
||||
isPersonal := false
|
||||
includeSpaceInfo := true
|
||||
|
||||
spaceService := mock_space.NewMockService(t)
|
||||
space := mock_clientspace.NewMockSpace(t)
|
||||
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
|
||||
space.EXPECT().IsPersonal().Return(isPersonal)
|
||||
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
|
||||
|
||||
expectedUri := "test"
|
||||
testFile := "test"
|
||||
|
@ -451,7 +474,7 @@ func TestPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// when
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, true)
|
||||
publish, err := svc.Publish(context.Background(), spaceId, objectId, expectedUri, includeSpaceInfo)
|
||||
|
||||
// then
|
||||
assert.Error(t, err)
|
||||
|
@ -666,24 +689,27 @@ func TestService_PublishingList(t *testing.T) {
|
|||
|
||||
var ctx = context.Background()
|
||||
|
||||
func prepareSpaceService(t *testing.T, isPersonal bool) (*mock_space.MockService, error) {
|
||||
func prepareSpaceService(t *testing.T, isPersonal bool, includeSpaceInfo bool) (*mock_space.MockService, error) {
|
||||
spaceService := mock_space.NewMockService(t)
|
||||
space := mock_clientspace.NewMockSpace(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
space.EXPECT().IsPersonal().Return(isPersonal)
|
||||
space.EXPECT().Id().Return(spaceId)
|
||||
|
||||
st := mock_anystorage.NewMockClientSpaceStorage(t)
|
||||
mockSt := mock_objecttree.NewMockStorage(ctrl)
|
||||
st.EXPECT().TreeStorage(mock.Anything, mock.Anything).Return(mockSt, nil)
|
||||
mockSt.EXPECT().Heads(gomock.Any()).Return([]string{"heads"}, nil)
|
||||
space.EXPECT().Storage().Return(st)
|
||||
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
|
||||
if includeSpaceInfo && !isPersonal {
|
||||
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
|
||||
}
|
||||
if includeSpaceInfo {
|
||||
space.EXPECT().IsPersonal().Return(isPersonal)
|
||||
}
|
||||
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
|
||||
return spaceService, nil
|
||||
}
|
||||
|
||||
func prepareExporter(t *testing.T, objectTypeId string, spaceService *mock_space.MockService) export.Export {
|
||||
func prepareExporter(t *testing.T, objectTypeId string, spaceService *mock_space.MockService, includeSpaceInfo bool) export.Export {
|
||||
storeFixture := objectstore.NewStoreFixture(t)
|
||||
objectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, objectTypeId)
|
||||
assert.Nil(t, err)
|
||||
|
@ -740,23 +766,25 @@ func prepareExporter(t *testing.T, objectTypeId string, spaceService *mock_space
|
|||
objectType.Doc = objectTypeDoc
|
||||
objectType.SetType(smartblock.SmartBlockTypeObjectType)
|
||||
|
||||
workspaceTest := smarttest.New(workspaceId)
|
||||
workspaceDoc := workspaceTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
|
||||
bundle.RelationKeyId: domain.String(workspaceId),
|
||||
bundle.RelationKeyType: domain.String(objectTypeId),
|
||||
}))
|
||||
workspaceDoc.AddRelationLinks(&model.RelationLink{
|
||||
Key: bundle.RelationKeyId.String(),
|
||||
Format: model.RelationFormat_longtext,
|
||||
}, &model.RelationLink{
|
||||
Key: bundle.RelationKeyType.String(),
|
||||
Format: model.RelationFormat_longtext,
|
||||
})
|
||||
workspaceTest.Doc = workspaceDoc
|
||||
if includeSpaceInfo {
|
||||
workspaceTest := smarttest.New(workspaceId)
|
||||
workspaceDoc := workspaceTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
|
||||
bundle.RelationKeyId: domain.String(workspaceId),
|
||||
bundle.RelationKeyType: domain.String(objectTypeId),
|
||||
}))
|
||||
workspaceDoc.AddRelationLinks(&model.RelationLink{
|
||||
Key: bundle.RelationKeyId.String(),
|
||||
Format: model.RelationFormat_longtext,
|
||||
}, &model.RelationLink{
|
||||
Key: bundle.RelationKeyType.String(),
|
||||
Format: model.RelationFormat_longtext,
|
||||
})
|
||||
workspaceTest.Doc = workspaceDoc
|
||||
objectGetter.EXPECT().GetObject(context.Background(), workspaceId).Return(workspaceTest, nil)
|
||||
}
|
||||
|
||||
objectGetter.EXPECT().GetObject(context.Background(), objectId).Return(smartBlockTest, nil)
|
||||
objectGetter.EXPECT().GetObject(context.Background(), objectTypeId).Return(objectType, nil)
|
||||
objectGetter.EXPECT().GetObject(context.Background(), workspaceId).Return(workspaceTest, nil)
|
||||
|
||||
a := &app.App{}
|
||||
mockSender := mock_event.NewMockSender(t)
|
||||
|
|
|
@ -909,6 +909,10 @@
|
|||
- [Rpc.Object.Duplicate.Request](#anytype-Rpc-Object-Duplicate-Request)
|
||||
- [Rpc.Object.Duplicate.Response](#anytype-Rpc-Object-Duplicate-Response)
|
||||
- [Rpc.Object.Duplicate.Response.Error](#anytype-Rpc-Object-Duplicate-Response-Error)
|
||||
- [Rpc.Object.Export](#anytype-Rpc-Object-Export)
|
||||
- [Rpc.Object.Export.Request](#anytype-Rpc-Object-Export-Request)
|
||||
- [Rpc.Object.Export.Response](#anytype-Rpc-Object-Export-Response)
|
||||
- [Rpc.Object.Export.Response.Error](#anytype-Rpc-Object-Export-Response-Error)
|
||||
- [Rpc.Object.Graph](#anytype-Rpc-Object-Graph)
|
||||
- [Rpc.Object.Graph.Edge](#anytype-Rpc-Object-Graph-Edge)
|
||||
- [Rpc.Object.Graph.Request](#anytype-Rpc-Object-Graph-Request)
|
||||
|
@ -1557,6 +1561,7 @@
|
|||
- [Rpc.Object.CrossSpaceSearchUnsubscribe.Response.Error.Code](#anytype-Rpc-Object-CrossSpaceSearchUnsubscribe-Response-Error-Code)
|
||||
- [Rpc.Object.DateByTimestamp.Response.Error.Code](#anytype-Rpc-Object-DateByTimestamp-Response-Error-Code)
|
||||
- [Rpc.Object.Duplicate.Response.Error.Code](#anytype-Rpc-Object-Duplicate-Response-Error-Code)
|
||||
- [Rpc.Object.Export.Response.Error.Code](#anytype-Rpc-Object-Export-Response-Error-Code)
|
||||
- [Rpc.Object.Graph.Edge.Type](#anytype-Rpc-Object-Graph-Edge-Type)
|
||||
- [Rpc.Object.Graph.Response.Error.Code](#anytype-Rpc-Object-Graph-Response-Error-Code)
|
||||
- [Rpc.Object.GroupsSubscribe.Response.Error.Code](#anytype-Rpc-Object-GroupsSubscribe-Response-Error-Code)
|
||||
|
@ -1859,6 +1864,7 @@
|
|||
- [Event.Process.New](#anytype-Event-Process-New)
|
||||
- [Event.Process.Update](#anytype-Event-Process-Update)
|
||||
- [Event.Space](#anytype-Event-Space)
|
||||
- [Event.Space.AutoWidgetAdded](#anytype-Event-Space-AutoWidgetAdded)
|
||||
- [Event.Space.SyncStatus](#anytype-Event-Space-SyncStatus)
|
||||
- [Event.Space.SyncStatus.Update](#anytype-Event-Space-SyncStatus-Update)
|
||||
- [Event.Status](#anytype-Event-Status)
|
||||
|
@ -2190,6 +2196,7 @@
|
|||
| ObjectUndo | [Rpc.Object.Undo.Request](#anytype-Rpc-Object-Undo-Request) | [Rpc.Object.Undo.Response](#anytype-Rpc-Object-Undo-Response) | |
|
||||
| ObjectRedo | [Rpc.Object.Redo.Request](#anytype-Rpc-Object-Redo-Request) | [Rpc.Object.Redo.Response](#anytype-Rpc-Object-Redo-Response) | |
|
||||
| ObjectListExport | [Rpc.Object.ListExport.Request](#anytype-Rpc-Object-ListExport-Request) | [Rpc.Object.ListExport.Response](#anytype-Rpc-Object-ListExport-Response) | |
|
||||
| ObjectExport | [Rpc.Object.Export.Request](#anytype-Rpc-Object-Export-Request) | [Rpc.Object.Export.Response](#anytype-Rpc-Object-Export-Response) | |
|
||||
| ObjectBookmarkFetch | [Rpc.Object.BookmarkFetch.Request](#anytype-Rpc-Object-BookmarkFetch-Request) | [Rpc.Object.BookmarkFetch.Response](#anytype-Rpc-Object-BookmarkFetch-Response) | |
|
||||
| ObjectToBookmark | [Rpc.Object.ToBookmark.Request](#anytype-Rpc-Object-ToBookmark-Request) | [Rpc.Object.ToBookmark.Response](#anytype-Rpc-Object-ToBookmark-Response) | |
|
||||
| ObjectImport | [Rpc.Object.Import.Request](#anytype-Rpc-Object-Import-Request) | [Rpc.Object.Import.Response](#anytype-Rpc-Object-Import-Response) | |
|
||||
|
@ -15542,6 +15549,66 @@ Get the info for page alongside with info for all inbound and outbound links fro
|
|||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Export"></a>
|
||||
|
||||
### Rpc.Object.Export
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Export-Request"></a>
|
||||
|
||||
### Rpc.Object.Export.Request
|
||||
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| spaceId | [string](#string) | | |
|
||||
| objectId | [string](#string) | | ids of documents for export, when empty - will export all available docs |
|
||||
| format | [model.Export.Format](#anytype-model-Export-Format) | | export format |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Export-Response"></a>
|
||||
|
||||
### Rpc.Object.Export.Response
|
||||
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| error | [Rpc.Object.Export.Response.Error](#anytype-Rpc-Object-Export-Response-Error) | | |
|
||||
| result | [string](#string) | | |
|
||||
| event | [ResponseEvent](#anytype-ResponseEvent) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Export-Response-Error"></a>
|
||||
|
||||
### Rpc.Object.Export.Response.Error
|
||||
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| code | [Rpc.Object.Export.Response.Error.Code](#anytype-Rpc-Object-Export-Response-Error-Code) | | |
|
||||
| description | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Graph"></a>
|
||||
|
||||
### Rpc.Object.Graph
|
||||
|
@ -24808,6 +24875,19 @@ Middleware-to-front-end response, that can contain a NULL error or a non-NULL er
|
|||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Export-Response-Error-Code"></a>
|
||||
|
||||
### Rpc.Object.Export.Response.Error.Code
|
||||
|
||||
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| NULL | 0 | |
|
||||
| UNKNOWN_ERROR | 1 | |
|
||||
| BAD_INPUT | 2 | ... |
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Rpc-Object-Graph-Edge-Type"></a>
|
||||
|
||||
### Rpc.Object.Graph.Edge.Type
|
||||
|
@ -28889,6 +28969,7 @@ Precondition: user A opened a block
|
|||
| payloadBroadcast | [Event.Payload.Broadcast](#anytype-Event-Payload-Broadcast) | | |
|
||||
| membershipUpdate | [Event.Membership.Update](#anytype-Event-Membership-Update) | | |
|
||||
| spaceSyncStatusUpdate | [Event.Space.SyncStatus.Update](#anytype-Event-Space-SyncStatus-Update) | | |
|
||||
| spaceAutoWidgetAdded | [Event.Space.AutoWidgetAdded](#anytype-Event-Space-AutoWidgetAdded) | | |
|
||||
| p2pStatusUpdate | [Event.P2PStatus.Update](#anytype-Event-P2PStatus-Update) | | |
|
||||
| importFinish | [Event.Import.Finish](#anytype-Event-Import-Finish) | | |
|
||||
| chatAdd | [Event.Chat.Add](#anytype-Event-Chat-Add) | | |
|
||||
|
@ -29356,6 +29437,23 @@ Removes document from subscription
|
|||
|
||||
|
||||
|
||||
<a name="anytype-Event-Space-AutoWidgetAdded"></a>
|
||||
|
||||
### Event.Space.AutoWidgetAdded
|
||||
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| targetId | [string](#string) | | |
|
||||
| targetName | [string](#string) | | pluralName (if exists) for types, fallback to name. Special cases for "bin" and "favorites" |
|
||||
| widgetBlockId | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="anytype-Event-Space-SyncStatus"></a>
|
||||
|
||||
### Event.Space.SyncStatus
|
||||
|
@ -30720,6 +30818,7 @@ Link: block to link some content from an external sources.
|
|||
| layout | [Block.Content.Widget.Layout](#anytype-model-Block-Content-Widget-Layout) | | |
|
||||
| limit | [int32](#int32) | | |
|
||||
| viewId | [string](#string) | | |
|
||||
| autoAdded | [bool](#bool) | | |
|
||||
|
||||
|
||||
|
||||
|
|
20
go.mod
20
go.mod
|
@ -52,6 +52,7 @@ require (
|
|||
github.com/hashicorp/yamux v0.1.2
|
||||
github.com/hbagdi/go-unsplash v0.0.0-20230414214043-474fc02c9119
|
||||
github.com/huandu/skiplist v1.2.1
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/improbable-eng/grpc-web v0.15.0
|
||||
github.com/ipfs/boxo v0.29.1
|
||||
github.com/ipfs/go-block-format v0.2.0
|
||||
|
@ -95,7 +96,7 @@ require (
|
|||
github.com/stretchr/testify v1.10.0
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.4
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/valyala/fastjson v1.6.4
|
||||
github.com/vektra/mockery/v2 v2.47.0
|
||||
|
@ -109,7 +110,7 @@ require (
|
|||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||
golang.org/x/image v0.25.0
|
||||
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/text v0.23.0
|
||||
|
@ -128,8 +129,6 @@ require (
|
|||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
|
||||
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
|
@ -170,10 +169,10 @@ require (
|
|||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.9 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
|
@ -199,7 +198,6 @@ require (
|
|||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/holiman/uint256 v1.2.4 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
|
@ -219,7 +217,7 @@ require (
|
|||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/libp2p/go-libp2p v0.41.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
|
@ -262,6 +260,8 @@ require (
|
|||
github.com/spf13/viper v1.15.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/sv-tools/openapi v0.2.1 // indirect
|
||||
github.com/swaggo/swag v1.16.4 // indirect
|
||||
github.com/tetratelabs/wazero v1.8.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
|
|
39
go.sum
39
go.sum
|
@ -56,10 +56,6 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
|||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8=
|
||||
github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
|
@ -84,12 +80,10 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk
|
|||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/anyproto/any-store v0.1.12 h1:VgTyfxM4BvFnYMbwWxV9FQmXWNlwp9xriWFh6VOjcm0=
|
||||
github.com/anyproto/any-store v0.1.12/go.mod h1:T6WNuCHcuXIRiaZ+QAcBHdxIbPbgNCMIf1u3P9jvAyU=
|
||||
github.com/anyproto/any-sync v0.6.10-0.20250328101049-7b02c1945e6d h1:AU7TEdUV4NZSNilcWfAhGVdZUsZutfDx3Q0cJ11S4A4=
|
||||
github.com/anyproto/any-sync v0.6.10-0.20250328101049-7b02c1945e6d/go.mod h1:TSKgCoTV40Bt8AfCh3RxPUUAfYGrhc8Mzh8/AiVlvX4=
|
||||
github.com/anyproto/any-sync v0.6.10 h1:TV3yLkp5NK7FkddUvVtfxT01t/Xi+DlKbvisGEQwmC0=
|
||||
github.com/anyproto/any-sync v0.6.10/go.mod h1:TSKgCoTV40Bt8AfCh3RxPUUAfYGrhc8Mzh8/AiVlvX4=
|
||||
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a h1:ZZM+0OUCQMWSLSflpkf0ZMVo3V76qEDDIXPpQOClNs0=
|
||||
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a/go.mod h1:4fkueCZcGniSMXkrwESO8zzERrh/L7WHimRNWecfGM0=
|
||||
github.com/anyproto/anytype-push-server/pushclient v0.0.0-20250319134422-fd3f261ea023 h1:c7zj0NKKxFxqmK2PjNofpNB2yRCOzbA0gMLCiaEuLeY=
|
||||
github.com/anyproto/anytype-push-server/pushclient v0.0.0-20250319134422-fd3f261ea023/go.mod h1:IjVnEyh6QiLsPPfZinTTnOtxe79uJUKZyC1z65b2Yuc=
|
||||
github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580 h1:Ba80IlCCxkZ9H1GF+7vFu/TSpPvbpDCxXJ5ogc4euYc=
|
||||
github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580/go.mod h1:T/uWAYxrXdaXw64ihI++9RMbKTCpKd/yE9+saARew7k=
|
||||
github.com/anyproto/go-chash v0.1.0 h1:I9meTPjXFRfXZHRJzjOHC/XF7Q5vzysKkiT/grsogXY=
|
||||
|
@ -351,15 +345,18 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
|
|||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
|
||||
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw=
|
||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
|
@ -674,6 +671,7 @@ github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnp
|
|||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -714,8 +712,9 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a
|
|||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matishsiao/goInfo v0.0.0-20240924010139-10388a85396f h1:XDrsC/9hdgiU9ecceSmYsS2E3fBtFiYc34dAMFgegnM=
|
||||
github.com/matishsiao/goInfo v0.0.0-20240924010139-10388a85396f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -1048,12 +1047,16 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/sv-tools/openapi v0.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA=
|
||||
github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550=
|
||||
github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||
|
@ -1290,7 +1293,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -1305,8 +1307,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -1396,7 +1398,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
|
@ -9,3 +9,9 @@ lint:
|
|||
@npx prettier --check "./**" 2> /dev/null || true
|
||||
@echo 'Linting with golint...'
|
||||
@golint `go list ./... | grep -v /vendor/`
|
||||
|
||||
swagger:
|
||||
@echo 'Generating swagger docs...'
|
||||
@swag init --v3.1 -q -d core/api -g service.go -o core/api/docs
|
||||
@echo 'Formatting swagger docs...'
|
||||
@swag fmt -d core/api
|
||||
|
|
4580
pb/commands.pb.go
4580
pb/commands.pb.go
File diff suppressed because it is too large
Load diff
1633
pb/events.pb.go
1633
pb/events.pb.go
File diff suppressed because it is too large
Load diff
|
@ -2908,6 +2908,35 @@ message Rpc {
|
|||
|
||||
}
|
||||
|
||||
message Export {
|
||||
message Request {
|
||||
string spaceId = 10;
|
||||
// ids of documents for export, when empty - will export all available docs
|
||||
string objectId = 2;
|
||||
// export format
|
||||
anytype.model.Export.Format format = 3;
|
||||
}
|
||||
|
||||
message Response {
|
||||
Error error = 1;
|
||||
string result = 2;
|
||||
ResponseEvent event = 3;
|
||||
|
||||
message Error {
|
||||
Code code = 1;
|
||||
string description = 2;
|
||||
|
||||
enum Code {
|
||||
NULL = 0;
|
||||
UNKNOWN_ERROR = 1;
|
||||
BAD_INPUT = 2;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
message Import {
|
||||
message Request {
|
||||
option (no_auth) = true;
|
||||
|
|
|
@ -108,6 +108,7 @@ message Event {
|
|||
Membership.Update membershipUpdate = 117;
|
||||
|
||||
Space.SyncStatus.Update spaceSyncStatusUpdate = 119;
|
||||
Space.AutoWidgetAdded spaceAutoWidgetAdded = 122;
|
||||
|
||||
P2PStatus.Update p2pStatusUpdate = 120;
|
||||
|
||||
|
@ -1155,6 +1156,12 @@ message Event {
|
|||
IncompatibleVersion = 2;
|
||||
NetworkError = 3;
|
||||
}
|
||||
|
||||
message AutoWidgetAdded {
|
||||
string targetId = 1;
|
||||
string targetName = 2; // pluralName (if exists) for types, fallback to name. Special cases for "bin" and "favorites"
|
||||
string widgetBlockId = 3;
|
||||
}
|
||||
}
|
||||
message P2PStatus {
|
||||
message Update {
|
||||
|
|
|
@ -127,6 +127,7 @@ service ClientCommands {
|
|||
rpc ObjectUndo (anytype.Rpc.Object.Undo.Request) returns (anytype.Rpc.Object.Undo.Response);
|
||||
rpc ObjectRedo (anytype.Rpc.Object.Redo.Request) returns (anytype.Rpc.Object.Redo.Response);
|
||||
rpc ObjectListExport (anytype.Rpc.Object.ListExport.Request) returns (anytype.Rpc.Object.ListExport.Response);
|
||||
rpc ObjectExport (anytype.Rpc.Object.Export.Request) returns (anytype.Rpc.Object.Export.Response);
|
||||
rpc ObjectBookmarkFetch (anytype.Rpc.Object.BookmarkFetch.Request) returns (anytype.Rpc.Object.BookmarkFetch.Response);
|
||||
rpc ObjectToBookmark (anytype.Rpc.Object.ToBookmark.Request) returns (anytype.Rpc.Object.ToBookmark.Response);
|
||||
rpc ObjectImport (anytype.Rpc.Object.Import.Request) returns (anytype.Rpc.Object.Import.Response);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -26,366 +26,365 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
|||
func init() { proto.RegisterFile("pb/protos/service/service.proto", fileDescriptor_93a29dc403579097) }
|
||||
|
||||
var fileDescriptor_93a29dc403579097 = []byte{
|
||||
// 5733 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0x5f, 0x6f, 0x1d, 0x49,
|
||||
0x56, 0xc0, 0xc7, 0x2f, 0x0c, 0xf4, 0xb2, 0x03, 0xdc, 0x81, 0x61, 0x76, 0xd8, 0x4d, 0x32, 0x99,
|
||||
0xc4, 0x4e, 0xe2, 0xf8, 0x3a, 0x93, 0xcc, 0x3f, 0x76, 0x91, 0xe0, 0xc6, 0x8e, 0x3d, 0x77, 0xc7,
|
||||
0x76, 0x8c, 0xef, 0x75, 0x22, 0x46, 0x42, 0xa2, 0x7d, 0xbb, 0x7c, 0xdd, 0xb8, 0x6f, 0x57, 0x6f,
|
||||
0x77, 0x5d, 0x27, 0x77, 0x11, 0x08, 0x04, 0x02, 0x81, 0x16, 0xb1, 0xe2, 0x9f, 0xe0, 0x09, 0x89,
|
||||
0x4f, 0xc0, 0xc7, 0xe0, 0x71, 0x1f, 0x79, 0x44, 0x33, 0xdf, 0x03, 0xa1, 0xae, 0xaa, 0xae, 0x3f,
|
||||
0xa7, 0xcf, 0xa9, 0x6e, 0xcf, 0x3e, 0x25, 0xf2, 0xf9, 0x9d, 0x73, 0xaa, 0xba, 0x4e, 0x55, 0x9d,
|
||||
0xaa, 0xae, 0xae, 0x1b, 0xdd, 0x2c, 0xce, 0xb6, 0x8b, 0x92, 0x0b, 0x5e, 0x6d, 0x57, 0xac, 0xbc,
|
||||
0x4a, 0x67, 0xac, 0xf9, 0x77, 0x28, 0xff, 0x3c, 0x78, 0x33, 0xce, 0x57, 0x62, 0x55, 0xb0, 0xf7,
|
||||
0xde, 0xb5, 0xe4, 0x8c, 0x2f, 0x16, 0x71, 0x9e, 0x54, 0x0a, 0x79, 0xef, 0x1d, 0x2b, 0x61, 0x57,
|
||||
0x2c, 0x17, 0xfa, 0xef, 0x8f, 0x7f, 0xf2, 0x7f, 0x6b, 0xd1, 0x5b, 0x3b, 0x59, 0xca, 0x72, 0xb1,
|
||||
0xa3, 0x35, 0x06, 0x5f, 0x46, 0xdf, 0x1e, 0x15, 0xc5, 0x3e, 0x13, 0x2f, 0x58, 0x59, 0xa5, 0x3c,
|
||||
0x1f, 0x7c, 0x30, 0xd4, 0x0e, 0x86, 0x27, 0xc5, 0x6c, 0x38, 0x2a, 0x8a, 0xa1, 0x15, 0x0e, 0x4f,
|
||||
0xd8, 0x8f, 0x96, 0xac, 0x12, 0xef, 0xdd, 0x09, 0x43, 0x55, 0xc1, 0xf3, 0x8a, 0x0d, 0xce, 0xa3,
|
||||
0x5f, 0x1b, 0x15, 0xc5, 0x84, 0x89, 0x5d, 0x56, 0x57, 0x60, 0x22, 0x62, 0xc1, 0x06, 0x1b, 0x2d,
|
||||
0x55, 0x1f, 0x30, 0x3e, 0xee, 0x75, 0x83, 0xda, 0xcf, 0x34, 0xfa, 0x56, 0xed, 0xe7, 0x62, 0x29,
|
||||
0x12, 0xfe, 0x2a, 0x1f, 0xbc, 0xdf, 0x56, 0xd4, 0x22, 0x63, 0xfb, 0x76, 0x08, 0xd1, 0x56, 0x5f,
|
||||
0x46, 0xbf, 0xfc, 0x32, 0xce, 0x32, 0x26, 0x76, 0x4a, 0x56, 0x17, 0xdc, 0xd7, 0x51, 0xa2, 0xa1,
|
||||
0x92, 0x19, 0xbb, 0x1f, 0x04, 0x19, 0x6d, 0xf8, 0xcb, 0xe8, 0xdb, 0x4a, 0x72, 0xc2, 0x66, 0xfc,
|
||||
0x8a, 0x95, 0x03, 0x54, 0x4b, 0x0b, 0x89, 0x47, 0xde, 0x82, 0xa0, 0xed, 0x1d, 0x9e, 0x5f, 0xb1,
|
||||
0x52, 0xe0, 0xb6, 0xb5, 0x30, 0x6c, 0xdb, 0x42, 0xda, 0xf6, 0xdf, 0xae, 0x45, 0xdf, 0x1d, 0xcd,
|
||||
0x66, 0x7c, 0x99, 0x8b, 0x03, 0x3e, 0x8b, 0xb3, 0x83, 0x34, 0xbf, 0x3c, 0x62, 0xaf, 0x76, 0x2e,
|
||||
0x6a, 0x3e, 0x9f, 0xb3, 0xc1, 0x13, 0xff, 0xa9, 0x2a, 0x74, 0x68, 0xd8, 0xa1, 0x0b, 0x1b, 0xdf,
|
||||
0x1f, 0x5d, 0x4f, 0x49, 0x97, 0xe5, 0x1f, 0xd6, 0xa2, 0x1b, 0xb0, 0x2c, 0x13, 0x9e, 0x5d, 0x31,
|
||||
0x5b, 0x9a, 0x8f, 0x3b, 0x0c, 0xfb, 0xb8, 0x29, 0xcf, 0x27, 0xd7, 0x55, 0xd3, 0x25, 0xca, 0xa2,
|
||||
0xb7, 0xdd, 0x70, 0x99, 0xb0, 0x4a, 0x76, 0xa7, 0xfb, 0x74, 0x44, 0x68, 0xc4, 0x78, 0x7e, 0xd0,
|
||||
0x07, 0xd5, 0xde, 0xd2, 0x68, 0xa0, 0xbd, 0x65, 0xbc, 0x32, 0xce, 0xee, 0xa1, 0x16, 0x1c, 0xc2,
|
||||
0xf8, 0xba, 0xdf, 0x83, 0xd4, 0xae, 0xfe, 0x28, 0xfa, 0x95, 0x97, 0xbc, 0xbc, 0xac, 0x8a, 0x78,
|
||||
0xc6, 0x74, 0x57, 0xb8, 0xeb, 0x6b, 0x37, 0x52, 0xd8, 0x1b, 0xd6, 0xbb, 0x30, 0x27, 0x68, 0x1b,
|
||||
0xe1, 0xf3, 0x82, 0xc1, 0x31, 0xc8, 0x2a, 0xd6, 0x42, 0x2a, 0x68, 0x21, 0xa4, 0x6d, 0x5f, 0x46,
|
||||
0x03, 0x6b, 0xfb, 0xec, 0x8f, 0xd9, 0x4c, 0x8c, 0x92, 0x04, 0xb6, 0x8a, 0xd5, 0x95, 0xc4, 0x70,
|
||||
0x94, 0x24, 0x54, 0xab, 0xe0, 0xa8, 0x76, 0xf6, 0x2a, 0x7a, 0x07, 0x38, 0x3b, 0x48, 0x2b, 0xe9,
|
||||
0x70, 0x2b, 0x6c, 0x45, 0x63, 0xc6, 0xe9, 0xb0, 0x2f, 0xae, 0x1d, 0xff, 0xf9, 0x5a, 0xf4, 0x1d,
|
||||
0xc4, 0xf3, 0x09, 0x5b, 0xf0, 0x2b, 0x36, 0x78, 0xd4, 0x6d, 0x4d, 0x91, 0xc6, 0xff, 0x87, 0xd7,
|
||||
0xd0, 0x40, 0xc2, 0x64, 0xc2, 0x32, 0x36, 0x13, 0x64, 0x98, 0x28, 0x71, 0x67, 0x98, 0x18, 0xcc,
|
||||
0xe9, 0x61, 0x8d, 0x70, 0x9f, 0x89, 0x9d, 0x65, 0x59, 0xb2, 0x5c, 0x90, 0x6d, 0x69, 0x91, 0xce,
|
||||
0xb6, 0xf4, 0x50, 0xa4, 0x3e, 0xfb, 0x4c, 0x8c, 0xb2, 0x8c, 0xac, 0x8f, 0x12, 0x77, 0xd6, 0xc7,
|
||||
0x60, 0xda, 0xc3, 0x2c, 0xfa, 0x55, 0xe7, 0x89, 0x89, 0x71, 0x7e, 0xce, 0x07, 0xf4, 0xb3, 0x90,
|
||||
0x72, 0xe3, 0x63, 0xa3, 0x93, 0x43, 0xaa, 0xf1, 0xec, 0x75, 0xc1, 0x4b, 0xba, 0x59, 0x94, 0xb8,
|
||||
0xb3, 0x1a, 0x06, 0xd3, 0x1e, 0xfe, 0x30, 0x7a, 0x4b, 0x8f, 0x92, 0xcd, 0x7c, 0x76, 0x07, 0x1d,
|
||||
0x42, 0xe1, 0x84, 0x76, 0xb7, 0x83, 0x6a, 0x99, 0x3f, 0x4c, 0xe7, 0x65, 0x3d, 0xfa, 0xe0, 0xe6,
|
||||
0xb5, 0xb4, 0xc3, 0xbc, 0xa5, 0xb4, 0x79, 0x1e, 0xfd, 0xba, 0x6f, 0x7e, 0x27, 0xce, 0x67, 0x2c,
|
||||
0x1b, 0x3c, 0x08, 0xa9, 0x2b, 0xc6, 0xb8, 0xda, 0xec, 0xc5, 0xda, 0xc1, 0x4e, 0x13, 0x7a, 0x30,
|
||||
0xfd, 0x00, 0xd5, 0x06, 0x43, 0xe9, 0x9d, 0x30, 0xd4, 0xb2, 0xbd, 0xcb, 0x32, 0x46, 0xda, 0x56,
|
||||
0xc2, 0x0e, 0xdb, 0x06, 0xd2, 0xb6, 0xcb, 0xe8, 0x37, 0x4c, 0x33, 0xd7, 0x79, 0x81, 0x94, 0xd7,
|
||||
0x93, 0xce, 0x26, 0xd1, 0x8e, 0x2e, 0x64, 0x7c, 0x3d, 0xec, 0x07, 0xb7, 0xea, 0xa3, 0x47, 0x14,
|
||||
0xbc, 0x3e, 0x60, 0x3c, 0xb9, 0x13, 0x86, 0xb4, 0xed, 0xbf, 0x5b, 0x8b, 0xbe, 0xa7, 0x65, 0xcf,
|
||||
0xf2, 0xf8, 0x2c, 0x63, 0x72, 0x8a, 0x3f, 0x62, 0xe2, 0x15, 0x2f, 0x2f, 0x27, 0xab, 0x7c, 0x46,
|
||||
0xa4, 0x33, 0x38, 0xdc, 0x91, 0xce, 0x90, 0x4a, 0xba, 0x30, 0x7f, 0x12, 0xbd, 0xdb, 0x04, 0xc5,
|
||||
0x45, 0x9c, 0xcf, 0xd9, 0x0f, 0x2b, 0x9e, 0x8f, 0x8a, 0x74, 0x94, 0x24, 0xe5, 0x60, 0x88, 0x37,
|
||||
0x3d, 0xe4, 0x4c, 0x09, 0xb6, 0x7b, 0xf3, 0x4e, 0xfa, 0xac, 0x9f, 0xb2, 0xe0, 0x05, 0x4c, 0x9f,
|
||||
0x9b, 0xc7, 0x27, 0x78, 0x41, 0xa5, 0xcf, 0x3e, 0xd2, 0xb2, 0x7a, 0x58, 0xcf, 0x41, 0xb8, 0xd5,
|
||||
0x43, 0x77, 0xd2, 0xb9, 0x1d, 0x42, 0xec, 0x1c, 0xd0, 0x3c, 0x28, 0x9e, 0x9f, 0xa7, 0xf3, 0xd3,
|
||||
0x22, 0xa9, 0xfb, 0xd0, 0x7d, 0xbc, 0xce, 0x0e, 0x42, 0xcc, 0x01, 0x04, 0xaa, 0xbd, 0xfd, 0xbd,
|
||||
0xcd, 0x32, 0xf5, 0xb8, 0xb4, 0x57, 0xf2, 0xc5, 0x01, 0x9b, 0xc7, 0xb3, 0x95, 0x1e, 0x4c, 0x3f,
|
||||
0x0a, 0x8d, 0x62, 0x90, 0x36, 0x85, 0xf8, 0xf8, 0x9a, 0x5a, 0xba, 0x3c, 0xff, 0xb1, 0x16, 0xdd,
|
||||
0xf1, 0xe2, 0x44, 0x07, 0x93, 0x2a, 0xfd, 0x28, 0x4f, 0x4e, 0x58, 0x25, 0xe2, 0x52, 0x0c, 0xbe,
|
||||
0x1f, 0x88, 0x01, 0x42, 0xc7, 0x94, 0xed, 0x07, 0xdf, 0x48, 0xd7, 0xb6, 0xfa, 0xa4, 0x9e, 0x25,
|
||||
0xf4, 0xf8, 0xe3, 0xb7, 0xba, 0x94, 0xc0, 0xd1, 0xe7, 0x76, 0x08, 0xb1, 0xad, 0x2e, 0x05, 0xe3,
|
||||
0xfc, 0x2a, 0x15, 0x6c, 0x9f, 0xe5, 0xac, 0x6c, 0xb7, 0xba, 0x52, 0xf5, 0x11, 0xa2, 0xd5, 0x09,
|
||||
0xd4, 0x8e, 0x74, 0x9e, 0x37, 0x93, 0x69, 0x6c, 0x06, 0x8c, 0xb4, 0x72, 0x8d, 0x87, 0xfd, 0x60,
|
||||
0xa2, 0x86, 0x62, 0xbf, 0x36, 0x12, 0xac, 0xa1, 0x42, 0x7a, 0xd5, 0xd0, 0xa0, 0x76, 0x61, 0xee,
|
||||
0x78, 0x3b, 0x61, 0x57, 0xfc, 0x12, 0x2e, 0xcc, 0x5d, 0x03, 0x0a, 0x20, 0x16, 0xe6, 0x28, 0x68,
|
||||
0x93, 0x0f, 0xc7, 0xcf, 0x8b, 0x94, 0xbd, 0x02, 0xc9, 0x87, 0xab, 0x5c, 0x8b, 0x89, 0xe4, 0x03,
|
||||
0xc1, 0xb4, 0x87, 0xa3, 0xe8, 0x97, 0xa4, 0xf0, 0x87, 0x3c, 0xcd, 0x07, 0x37, 0x11, 0xa5, 0x5a,
|
||||
0x60, 0xac, 0xde, 0xa2, 0x01, 0x50, 0xe2, 0xfa, 0xaf, 0x3a, 0x13, 0xb8, 0x4b, 0x28, 0x81, 0x24,
|
||||
0x60, 0xbd, 0x0b, 0xb3, 0x59, 0x9f, 0x14, 0xd6, 0xa3, 0xe5, 0xe4, 0x22, 0x2e, 0xd3, 0x7c, 0x3e,
|
||||
0xc0, 0x74, 0x1d, 0x39, 0x91, 0xf5, 0x61, 0x1c, 0x08, 0x27, 0xad, 0x38, 0x2a, 0x8a, 0xb2, 0x1e,
|
||||
0x84, 0xb1, 0x70, 0xf2, 0x91, 0x60, 0x38, 0xb5, 0x50, 0xdc, 0xdb, 0x2e, 0x9b, 0x65, 0x69, 0x1e,
|
||||
0xf4, 0xa6, 0x91, 0x3e, 0xde, 0x2c, 0x0a, 0x82, 0xf7, 0x80, 0xc5, 0x57, 0xac, 0xa9, 0x19, 0xf6,
|
||||
0x64, 0x5c, 0x20, 0x18, 0xbc, 0x00, 0xb4, 0x4b, 0x6c, 0x29, 0x3e, 0x8c, 0x2f, 0x59, 0xfd, 0x80,
|
||||
0x59, 0x3d, 0x85, 0x0f, 0x30, 0x7d, 0x8f, 0x20, 0x96, 0xd8, 0x38, 0xa9, 0x5d, 0x2d, 0xa3, 0x77,
|
||||
0xa4, 0xfc, 0x38, 0x2e, 0x45, 0x3a, 0x4b, 0x8b, 0x38, 0x6f, 0x96, 0x6e, 0xd8, 0x28, 0xd2, 0xa2,
|
||||
0x8c, 0xcb, 0xad, 0x9e, 0xb4, 0x76, 0xfb, 0xaf, 0x6b, 0xd1, 0xfb, 0xd0, 0xef, 0x31, 0x2b, 0x17,
|
||||
0xa9, 0xdc, 0x01, 0xa8, 0xd4, 0x90, 0x3f, 0xf8, 0x34, 0x6c, 0xb4, 0xa5, 0x60, 0x4a, 0xf3, 0xd9,
|
||||
0xf5, 0x15, 0x6d, 0xde, 0x37, 0xd1, 0xab, 0xa2, 0xe7, 0x65, 0xd2, 0xda, 0x21, 0x9b, 0x34, 0x4b,
|
||||
0x1d, 0x29, 0x24, 0xf2, 0xbe, 0x16, 0x04, 0x7a, 0xf8, 0x69, 0x5e, 0x35, 0xd6, 0xb1, 0x1e, 0x6e,
|
||||
0xc5, 0xc1, 0x1e, 0xee, 0x61, 0xb6, 0x87, 0x1f, 0x2f, 0xcf, 0xb2, 0xb4, 0xba, 0x48, 0xf3, 0xb9,
|
||||
0x4e, 0xf2, 0x7d, 0x5d, 0x2b, 0x86, 0x79, 0xfe, 0x46, 0x27, 0x87, 0x39, 0xd1, 0xc1, 0x42, 0x3a,
|
||||
0x01, 0x61, 0xb2, 0xd1, 0xc9, 0xd9, 0xb5, 0x97, 0x95, 0xd6, 0x8b, 0x7e, 0xb0, 0xf6, 0x72, 0x54,
|
||||
0x6b, 0x29, 0xb1, 0xf6, 0x6a, 0x53, 0x76, 0xed, 0xe5, 0xd6, 0xa1, 0xe2, 0xd9, 0x15, 0x3b, 0x2d,
|
||||
0x53, 0xb0, 0xf6, 0xf2, 0xca, 0xd7, 0x30, 0xc4, 0xda, 0x8b, 0x62, 0xed, 0x40, 0x65, 0x89, 0x7d,
|
||||
0x26, 0x26, 0x22, 0x16, 0xcb, 0x0a, 0x0c, 0x54, 0x8e, 0x0d, 0x83, 0x10, 0x03, 0x15, 0x81, 0x6a,
|
||||
0x6f, 0xbf, 0x1f, 0x45, 0x6a, 0xbf, 0x44, 0xee, 0x69, 0xf9, 0x73, 0x8f, 0xde, 0x48, 0xf1, 0x36,
|
||||
0xb4, 0xde, 0x0f, 0x10, 0x36, 0xbd, 0x52, 0x7f, 0x97, 0x5b, 0x75, 0x03, 0x54, 0x43, 0x8a, 0x88,
|
||||
0xf4, 0x0a, 0x20, 0xb0, 0xa0, 0x93, 0x0b, 0xfe, 0x0a, 0x2f, 0x68, 0x2d, 0x09, 0x17, 0x54, 0x13,
|
||||
0x76, 0xf3, 0x5c, 0x17, 0x14, 0xdb, 0x3c, 0x6f, 0x8a, 0x11, 0xda, 0x3c, 0x87, 0x8c, 0x8d, 0x19,
|
||||
0xd7, 0xf0, 0x53, 0xce, 0x2f, 0x17, 0x71, 0x79, 0x09, 0x62, 0xc6, 0x53, 0x6e, 0x18, 0x22, 0x66,
|
||||
0x28, 0xd6, 0xc6, 0x8c, 0xeb, 0xb0, 0x4e, 0xce, 0x4f, 0xcb, 0x0c, 0xc4, 0x8c, 0x67, 0x43, 0x23,
|
||||
0x44, 0xcc, 0x10, 0xa8, 0x1d, 0x9d, 0x5c, 0x6f, 0x13, 0x06, 0xb7, 0x6b, 0x3c, 0xf5, 0x09, 0xa3,
|
||||
0xb6, 0x6b, 0x10, 0x0c, 0x86, 0xd0, 0x7e, 0x19, 0x17, 0x17, 0x78, 0x08, 0x49, 0x51, 0x38, 0x84,
|
||||
0x1a, 0x04, 0xb6, 0xf7, 0x84, 0xc5, 0xe5, 0xec, 0x02, 0x6f, 0x6f, 0x25, 0x0b, 0xb7, 0xb7, 0x61,
|
||||
0x60, 0x7b, 0x2b, 0xc1, 0xcb, 0x54, 0x5c, 0x1c, 0x32, 0x11, 0xe3, 0xed, 0xed, 0x33, 0xe1, 0xf6,
|
||||
0x6e, 0xb1, 0x36, 0xfb, 0x77, 0x1d, 0x4e, 0x96, 0x67, 0xd5, 0xac, 0x4c, 0xcf, 0xd8, 0x20, 0x60,
|
||||
0xc5, 0x40, 0x44, 0xf6, 0x4f, 0xc2, 0xda, 0xe7, 0x4f, 0xd7, 0xa2, 0x9b, 0x4d, 0xb3, 0xf3, 0xaa,
|
||||
0xd2, 0x73, 0x9f, 0xef, 0xfe, 0x63, 0xbc, 0x7d, 0x09, 0x9c, 0x78, 0x9d, 0xd1, 0x43, 0xcd, 0xc9,
|
||||
0x0d, 0xf0, 0x22, 0x9d, 0xe6, 0x95, 0x29, 0xd4, 0xa7, 0x7d, 0xac, 0x3b, 0x0a, 0x44, 0x6e, 0xd0,
|
||||
0x4b, 0xd1, 0xa6, 0x65, 0xba, 0x7d, 0x1a, 0xd9, 0x38, 0xa9, 0x40, 0x5a, 0xd6, 0x3c, 0x6f, 0x87,
|
||||
0x20, 0xd2, 0x32, 0x9c, 0x84, 0xa1, 0xb0, 0x5f, 0xf2, 0x65, 0x51, 0x75, 0x84, 0x02, 0x80, 0xc2,
|
||||
0xa1, 0xd0, 0x86, 0xb5, 0xcf, 0xd7, 0xd1, 0x6f, 0xba, 0xe1, 0xe7, 0x3e, 0xec, 0x2d, 0x3a, 0xa6,
|
||||
0xb0, 0x47, 0x3c, 0xec, 0x8b, 0xdb, 0x8c, 0xa2, 0xf1, 0x2c, 0x76, 0x99, 0x88, 0xd3, 0xac, 0x1a,
|
||||
0xac, 0xe3, 0x36, 0x1a, 0x39, 0x91, 0x51, 0x60, 0x1c, 0x1c, 0xdf, 0x76, 0x97, 0x45, 0x96, 0xce,
|
||||
0xda, 0x2f, 0x93, 0xb4, 0xae, 0x11, 0x87, 0xc7, 0x37, 0x17, 0x83, 0xe3, 0x75, 0x9d, 0xfa, 0xc9,
|
||||
0xff, 0x4c, 0x57, 0x05, 0xc3, 0xc7, 0x6b, 0x0f, 0x09, 0x8f, 0xd7, 0x10, 0x85, 0xf5, 0x99, 0x30,
|
||||
0x71, 0x10, 0xaf, 0xf8, 0x92, 0x18, 0xaf, 0x8d, 0x38, 0x5c, 0x1f, 0x17, 0xb3, 0x6b, 0x03, 0xe3,
|
||||
0x61, 0x9c, 0x0b, 0x56, 0xe6, 0x71, 0xb6, 0x97, 0xc5, 0xf3, 0x6a, 0x40, 0x8c, 0x31, 0x3e, 0x45,
|
||||
0xac, 0x0d, 0x68, 0x1a, 0x79, 0x8c, 0xe3, 0x6a, 0x2f, 0xbe, 0xe2, 0x65, 0x2a, 0xe8, 0xc7, 0x68,
|
||||
0x91, 0xce, 0xc7, 0xe8, 0xa1, 0xa8, 0xb7, 0x51, 0x39, 0xbb, 0x48, 0xaf, 0x58, 0x12, 0xf0, 0xd6,
|
||||
0x20, 0x3d, 0xbc, 0x39, 0x28, 0xd2, 0x68, 0x13, 0xbe, 0x2c, 0x67, 0x8c, 0x6c, 0x34, 0x25, 0xee,
|
||||
0x6c, 0x34, 0x83, 0x69, 0x0f, 0x7f, 0xb5, 0x16, 0xfd, 0x96, 0x92, 0xba, 0x6f, 0x78, 0x76, 0xe3,
|
||||
0xea, 0xe2, 0x8c, 0xc7, 0x65, 0x32, 0xf8, 0x10, 0xb3, 0x83, 0xa2, 0xc6, 0xf5, 0xe3, 0xeb, 0xa8,
|
||||
0xc0, 0xc7, 0x5a, 0xe7, 0xdd, 0xb6, 0xc7, 0xa1, 0x8f, 0xd5, 0x43, 0xc2, 0x8f, 0x15, 0xa2, 0x70,
|
||||
0x00, 0x91, 0x72, 0xb5, 0x01, 0xb8, 0x4e, 0xea, 0xfb, 0xbb, 0x80, 0x1b, 0x9d, 0x1c, 0x1c, 0x1f,
|
||||
0x6b, 0xa1, 0x1f, 0x2d, 0x5b, 0x94, 0x0d, 0x3c, 0x62, 0x86, 0x7d, 0x71, 0xd2, 0xb3, 0xe9, 0x15,
|
||||
0x61, 0xcf, 0xad, 0x9e, 0x31, 0xec, 0x8b, 0x13, 0x9e, 0x9d, 0x61, 0x2d, 0xe4, 0x19, 0x19, 0xda,
|
||||
0x86, 0x7d, 0x71, 0x98, 0x7d, 0x69, 0xa6, 0x99, 0x17, 0x1e, 0x04, 0xec, 0xc0, 0xb9, 0x61, 0xb3,
|
||||
0x17, 0xab, 0x1d, 0xfe, 0xcd, 0x5a, 0xf4, 0x5d, 0xeb, 0xf1, 0x90, 0x27, 0xe9, 0xf9, 0x4a, 0x41,
|
||||
0x2f, 0xe2, 0x6c, 0xc9, 0xaa, 0xc1, 0x63, 0xca, 0x5a, 0x9b, 0x35, 0x25, 0x78, 0x72, 0x2d, 0x1d,
|
||||
0xd8, 0x77, 0x46, 0x45, 0x91, 0xad, 0xa6, 0x6c, 0x51, 0x64, 0x64, 0xdf, 0xf1, 0x90, 0x70, 0xdf,
|
||||
0x81, 0x28, 0xcc, 0xca, 0xa7, 0xbc, 0xce, 0xf9, 0xd1, 0xac, 0x5c, 0x8a, 0xc2, 0x59, 0x79, 0x83,
|
||||
0xc0, 0x5c, 0x69, 0xca, 0x77, 0x78, 0x96, 0xb1, 0x99, 0x68, 0x9f, 0x12, 0x31, 0x9a, 0x96, 0x08,
|
||||
0xe7, 0x4a, 0x80, 0xb4, 0xbb, 0x72, 0xcd, 0x1a, 0x32, 0x2e, 0xd9, 0xd3, 0xd5, 0x41, 0x9a, 0x5f,
|
||||
0x0e, 0xf0, 0xb4, 0xc0, 0x02, 0xc4, 0xae, 0x1c, 0x0a, 0xc2, 0xb5, 0xea, 0x69, 0x9e, 0x70, 0x7c,
|
||||
0xad, 0x5a, 0x4b, 0xc2, 0x6b, 0x55, 0x4d, 0x40, 0x93, 0x27, 0x8c, 0x32, 0x59, 0x4b, 0xc2, 0x26,
|
||||
0x35, 0x81, 0x0d, 0x85, 0xfa, 0x4d, 0x11, 0x39, 0x14, 0x82, 0x77, 0x43, 0x1b, 0x9d, 0x1c, 0x8c,
|
||||
0xd0, 0x66, 0xd1, 0xba, 0xc7, 0xc4, 0xec, 0x02, 0x8f, 0x50, 0x0f, 0x09, 0x47, 0x28, 0x44, 0x61,
|
||||
0x95, 0xa6, 0xdc, 0x2c, 0xba, 0xd7, 0xf1, 0xf8, 0x68, 0x2d, 0xb8, 0x37, 0x3a, 0x39, 0xb8, 0x8c,
|
||||
0x1c, 0x2f, 0xe4, 0x33, 0x43, 0x83, 0x5c, 0xc9, 0xc2, 0xcb, 0x48, 0xc3, 0xc0, 0xd2, 0x2b, 0x81,
|
||||
0xdc, 0xcb, 0x5a, 0xa7, 0x15, 0xbd, 0xdd, 0xac, 0x8d, 0x4e, 0x4e, 0x3b, 0xf9, 0x67, 0xb3, 0x8c,
|
||||
0x53, 0xd2, 0x23, 0x5e, 0xf7, 0x91, 0x17, 0x71, 0x96, 0x26, 0xb1, 0x60, 0x53, 0x7e, 0xc9, 0x72,
|
||||
0x7c, 0xc5, 0xa4, 0x4b, 0xab, 0xf8, 0xa1, 0xa7, 0x10, 0x5e, 0x31, 0x85, 0x15, 0x61, 0x9c, 0x28,
|
||||
0xfa, 0xb4, 0x62, 0x3b, 0x71, 0x45, 0x8c, 0x64, 0x1e, 0x12, 0x8e, 0x13, 0x88, 0xc2, 0x7c, 0x55,
|
||||
0xc9, 0x9f, 0xbd, 0x2e, 0x58, 0x99, 0xb2, 0x7c, 0xc6, 0xf0, 0x7c, 0x15, 0x52, 0xe1, 0x7c, 0x15,
|
||||
0xa1, 0xe1, 0x5a, 0x6d, 0x37, 0x16, 0xec, 0xe9, 0x6a, 0x9a, 0x2e, 0x58, 0x25, 0xe2, 0x45, 0x81,
|
||||
0xaf, 0xd5, 0x00, 0x14, 0x5e, 0xab, 0xb5, 0xe1, 0xd6, 0xd6, 0x90, 0x19, 0x10, 0xdb, 0x87, 0xcb,
|
||||
0x20, 0x11, 0x38, 0x5c, 0x46, 0xa0, 0xf0, 0xc1, 0x5a, 0x00, 0x7d, 0x49, 0xd0, 0xb2, 0x12, 0x7c,
|
||||
0x49, 0x40, 0xd3, 0xad, 0x0d, 0x37, 0xc3, 0x4c, 0xea, 0xae, 0xd9, 0x51, 0xf4, 0x89, 0xdb, 0x45,
|
||||
0x37, 0x7b, 0xb1, 0xf8, 0x0e, 0xdf, 0x09, 0xcb, 0x62, 0x39, 0x6d, 0x05, 0xb6, 0xd1, 0x1a, 0xa6,
|
||||
0xcf, 0x0e, 0x9f, 0xc3, 0x6a, 0x87, 0x7f, 0xb1, 0x16, 0xbd, 0x87, 0x79, 0x7c, 0x5e, 0x48, 0xbf,
|
||||
0x8f, 0xba, 0x6d, 0x29, 0x92, 0x38, 0x3d, 0x17, 0xd6, 0xb0, 0x07, 0x40, 0x1a, 0x91, 0x3d, 0x5c,
|
||||
0xa7, 0x0b, 0xe0, 0x27, 0x6d, 0xa6, 0xfc, 0x90, 0x23, 0x0e, 0x80, 0x84, 0x78, 0xbb, 0x1e, 0xf2,
|
||||
0xcb, 0x55, 0x81, 0xf5, 0x90, 0xb1, 0xa1, 0xc5, 0xc4, 0x7a, 0x08, 0xc1, 0x6c, 0xef, 0x74, 0xab,
|
||||
0xf7, 0x32, 0x15, 0x17, 0x32, 0xdf, 0x02, 0xbd, 0xd3, 0x2b, 0xab, 0x81, 0x88, 0xde, 0x49, 0xc2,
|
||||
0x30, 0x23, 0x69, 0xc0, 0xba, 0x6f, 0x62, 0x63, 0xb9, 0x31, 0xe4, 0xf6, 0xcc, 0x7b, 0xdd, 0x20,
|
||||
0x8c, 0xd7, 0x46, 0xac, 0x97, 0x3e, 0x0f, 0x42, 0x16, 0xc0, 0xf2, 0x67, 0xb3, 0x17, 0xab, 0x1d,
|
||||
0xfe, 0x59, 0xf4, 0x9d, 0x56, 0xc5, 0xf6, 0x58, 0x2c, 0x96, 0x25, 0x4b, 0x06, 0xdb, 0x1d, 0xe5,
|
||||
0x6e, 0x40, 0xe3, 0xfa, 0x51, 0x7f, 0x85, 0x56, 0x8e, 0xde, 0x70, 0x2a, 0xac, 0x4c, 0x19, 0x1e,
|
||||
0x87, 0x4c, 0xfa, 0x6c, 0x30, 0x47, 0xa7, 0x75, 0x5a, 0xcb, 0x6c, 0x37, 0xba, 0x46, 0x57, 0x71,
|
||||
0x9a, 0xc9, 0x97, 0xb5, 0x1f, 0x86, 0x8c, 0x7a, 0x68, 0x70, 0x99, 0x4d, 0xaa, 0xb4, 0x46, 0x66,
|
||||
0xd9, 0xc7, 0x9d, 0xe5, 0xd9, 0x43, 0x7a, 0x24, 0x40, 0x56, 0x67, 0x5b, 0x3d, 0x69, 0xed, 0x56,
|
||||
0x34, 0x53, 0x5e, 0xfd, 0x67, 0x37, 0xc8, 0x31, 0xaf, 0x5a, 0x15, 0x89, 0xf4, 0xad, 0x9e, 0xb4,
|
||||
0xf6, 0xfa, 0xa7, 0xd1, 0xbb, 0x6d, 0xaf, 0x7a, 0x22, 0xda, 0xee, 0x34, 0x05, 0xe6, 0xa2, 0x47,
|
||||
0xfd, 0x15, 0xb4, 0xfb, 0x7f, 0x33, 0xfb, 0xd2, 0xca, 0xff, 0x8c, 0x2f, 0x16, 0x2c, 0x4f, 0x58,
|
||||
0xd2, 0x68, 0x54, 0xf5, 0xfa, 0xe9, 0x33, 0xda, 0xae, 0x51, 0x18, 0xba, 0x1a, 0xa6, 0x44, 0xbf,
|
||||
0xfd, 0x0d, 0x34, 0x75, 0xd1, 0xfe, 0x6b, 0x2d, 0xba, 0x8f, 0x16, 0xad, 0x09, 0x5c, 0xaf, 0x88,
|
||||
0xbf, 0xd7, 0xc7, 0x11, 0xa6, 0x69, 0x8a, 0x3a, 0xfa, 0x39, 0x2c, 0xe8, 0x22, 0xff, 0xfb, 0x5a,
|
||||
0x74, 0xdb, 0x2a, 0xd6, 0xe1, 0xbd, 0xc3, 0xf3, 0xf3, 0x2c, 0x9d, 0x09, 0xf9, 0x46, 0x56, 0xab,
|
||||
0xd0, 0x8f, 0x93, 0xd2, 0xe8, 0x7e, 0x9c, 0x01, 0x4d, 0xbb, 0x78, 0xfd, 0x3c, 0xad, 0x04, 0x2f,
|
||||
0x57, 0x93, 0x0b, 0xfe, 0xaa, 0xf9, 0x3c, 0xc9, 0x1f, 0x97, 0x35, 0x30, 0x74, 0x08, 0x62, 0xf1,
|
||||
0x8a, 0x93, 0x2d, 0x57, 0xf6, 0x33, 0xa6, 0x8a, 0x70, 0xe5, 0x10, 0x1d, 0xae, 0x7c, 0xd2, 0xce,
|
||||
0x4a, 0x4d, 0xad, 0xec, 0x37, 0x57, 0x1b, 0x78, 0x51, 0xdb, 0xdf, 0x5d, 0xdd, 0xeb, 0x06, 0x6d,
|
||||
0x6e, 0xaa, 0xc5, 0xbb, 0xe9, 0xf9, 0xb9, 0xa9, 0x13, 0x5e, 0x52, 0x17, 0x21, 0x72, 0x53, 0x02,
|
||||
0xb5, 0xcb, 0xab, 0xbd, 0x34, 0x63, 0xf2, 0xdd, 0xcd, 0xf3, 0xf3, 0xf3, 0x8c, 0xc7, 0x09, 0x58,
|
||||
0x5e, 0xd5, 0xe2, 0xa1, 0x2b, 0x27, 0x96, 0x57, 0x18, 0x67, 0x4f, 0x85, 0xd4, 0xd2, 0x3a, 0xba,
|
||||
0xf3, 0x59, 0x9a, 0xc1, 0xd3, 0xcd, 0x52, 0xd3, 0x08, 0x89, 0x53, 0x21, 0x2d, 0xc8, 0xa6, 0x40,
|
||||
0xb5, 0xa8, 0x8e, 0xca, 0xa6, 0xfc, 0x77, 0xdb, 0x8a, 0x8e, 0x98, 0x48, 0x81, 0x10, 0xcc, 0xee,
|
||||
0x32, 0xd4, 0xc2, 0xd3, 0x42, 0x1a, 0xbf, 0xd5, 0xd6, 0x52, 0x12, 0x62, 0x97, 0xc1, 0x27, 0xec,
|
||||
0x6a, 0xb9, 0xfe, 0xfb, 0x2e, 0x7f, 0x95, 0x4b, 0xa3, 0xb7, 0xdb, 0x2a, 0x8d, 0x8c, 0x58, 0x2d,
|
||||
0x43, 0x46, 0x1b, 0xfe, 0x22, 0xfa, 0x45, 0x69, 0xb8, 0xe4, 0xc5, 0xe0, 0x06, 0xa2, 0x50, 0x3a,
|
||||
0x67, 0x81, 0x6f, 0x92, 0x72, 0x7b, 0x88, 0xc4, 0xc4, 0xc6, 0x69, 0x15, 0xcf, 0xe1, 0x01, 0x7e,
|
||||
0xdb, 0xe2, 0x52, 0x4a, 0x1c, 0x22, 0x69, 0x53, 0x7e, 0x54, 0x1c, 0xf1, 0x44, 0x5b, 0x47, 0x6a,
|
||||
0x68, 0x84, 0xa1, 0xa8, 0x70, 0x21, 0x9b, 0xb6, 0x1e, 0xc5, 0x57, 0xe9, 0xdc, 0xa4, 0x16, 0x6a,
|
||||
0x00, 0xab, 0x40, 0xda, 0x6a, 0x99, 0xa1, 0x03, 0x11, 0x69, 0x2b, 0x09, 0x6b, 0x9f, 0xff, 0xb4,
|
||||
0x16, 0xdd, 0xb2, 0xcc, 0x7e, 0xb3, 0x2f, 0x3b, 0xce, 0xcf, 0x79, 0x9d, 0xe4, 0x1e, 0xa4, 0xf9,
|
||||
0x65, 0x35, 0xf8, 0x84, 0x32, 0x89, 0xf3, 0xa6, 0x28, 0x9f, 0x5e, 0x5b, 0xcf, 0xae, 0x4f, 0x9a,
|
||||
0x4d, 0x4b, 0x7b, 0x72, 0x41, 0x69, 0x80, 0xf5, 0x89, 0xd9, 0xdb, 0x84, 0x1c, 0xb1, 0x3e, 0x09,
|
||||
0xf1, 0xb6, 0x89, 0x8d, 0xf3, 0x8c, 0xe7, 0xb0, 0x89, 0xad, 0x85, 0x5a, 0x48, 0x34, 0x71, 0x0b,
|
||||
0xb2, 0xe3, 0x71, 0x23, 0x52, 0xfb, 0x6b, 0xa3, 0x2c, 0x03, 0xe3, 0xb1, 0x51, 0x35, 0x00, 0x31,
|
||||
0x1e, 0xa3, 0xa0, 0xf6, 0x73, 0x12, 0x7d, 0xab, 0x7e, 0xa4, 0xc7, 0x25, 0xbb, 0x4a, 0x19, 0x3c,
|
||||
0x64, 0xe3, 0x48, 0x88, 0xfe, 0xef, 0x13, 0xb6, 0x67, 0x9d, 0xe6, 0x55, 0x91, 0xc5, 0xd5, 0x85,
|
||||
0x3e, 0x76, 0xe1, 0xd7, 0xb9, 0x11, 0xc2, 0x83, 0x17, 0x77, 0x3b, 0x28, 0x3b, 0xa8, 0x37, 0x32,
|
||||
0x33, 0xc4, 0xac, 0xe3, 0xaa, 0xad, 0x61, 0x66, 0xa3, 0x93, 0xb3, 0xef, 0x36, 0xf6, 0xe3, 0x2c,
|
||||
0x63, 0xe5, 0xaa, 0x91, 0x1d, 0xc6, 0x79, 0x7a, 0xce, 0x2a, 0x01, 0xde, 0x6d, 0x68, 0x6a, 0x08,
|
||||
0x31, 0xe2, 0xdd, 0x46, 0x00, 0xb7, 0xeb, 0x36, 0xe0, 0x79, 0x9c, 0x27, 0xec, 0x35, 0x58, 0xb7,
|
||||
0x41, 0x3b, 0x92, 0x21, 0xd6, 0x6d, 0x14, 0x6b, 0xf7, 0xf8, 0x9f, 0x66, 0x7c, 0x76, 0xa9, 0xa7,
|
||||
0x00, 0xbf, 0x81, 0xa5, 0x04, 0xce, 0x01, 0xb7, 0x43, 0x88, 0x9d, 0x04, 0xa4, 0xe0, 0x84, 0x15,
|
||||
0x59, 0x3c, 0x83, 0x27, 0xad, 0x94, 0x8e, 0x96, 0x11, 0x93, 0x00, 0x64, 0x40, 0x71, 0xf5, 0x09,
|
||||
0x2e, 0xac, 0xb8, 0xe0, 0x00, 0xd7, 0xed, 0x10, 0x62, 0xa7, 0x41, 0x29, 0x98, 0x14, 0x59, 0x2a,
|
||||
0x40, 0x37, 0x50, 0x1a, 0x52, 0x42, 0x74, 0x03, 0x9f, 0x00, 0x26, 0x0f, 0x59, 0x39, 0x67, 0xa8,
|
||||
0x49, 0x29, 0x09, 0x9a, 0x6c, 0x08, 0x7b, 0xac, 0x5c, 0xd5, 0x9d, 0x17, 0x2b, 0x70, 0xac, 0x5c,
|
||||
0x57, 0x8b, 0x17, 0x2b, 0xe2, 0x58, 0xb9, 0x07, 0x80, 0x22, 0x1e, 0xc7, 0x95, 0xc0, 0x8b, 0x28,
|
||||
0x25, 0xc1, 0x22, 0x36, 0x84, 0x9d, 0xa3, 0x55, 0x11, 0x97, 0x02, 0xcc, 0xd1, 0xba, 0x00, 0xce,
|
||||
0x59, 0x83, 0x9b, 0xa4, 0xdc, 0x8e, 0x24, 0xaa, 0x55, 0x98, 0xd8, 0x4b, 0x59, 0x96, 0x54, 0x60,
|
||||
0x24, 0xd1, 0xcf, 0xbd, 0x91, 0x12, 0x23, 0x49, 0x9b, 0x02, 0xa1, 0xa4, 0xdf, 0x84, 0x60, 0xb5,
|
||||
0x03, 0x2f, 0x41, 0x6e, 0x87, 0x10, 0x3b, 0x3e, 0x35, 0x85, 0xde, 0x89, 0xcb, 0x32, 0xad, 0x27,
|
||||
0xff, 0x75, 0xbc, 0x40, 0x8d, 0x9c, 0x18, 0x9f, 0x30, 0x0e, 0x74, 0xaf, 0x66, 0xe0, 0xc6, 0x0a,
|
||||
0x06, 0x87, 0xee, 0x0f, 0x82, 0x8c, 0xcd, 0x38, 0xa5, 0xc4, 0x79, 0x59, 0x8e, 0x3d, 0x4d, 0xe4,
|
||||
0x5d, 0xf9, 0x7a, 0x17, 0xe6, 0x7c, 0xe1, 0x66, 0x5c, 0x1c, 0xf2, 0x2b, 0x36, 0xe5, 0xcf, 0x5e,
|
||||
0xa7, 0x55, 0xbd, 0xdc, 0xd2, 0x33, 0xf7, 0x13, 0xc2, 0x12, 0x06, 0x13, 0x5f, 0xb8, 0x75, 0x2a,
|
||||
0xd9, 0x04, 0x02, 0x94, 0xe5, 0x88, 0xbd, 0x42, 0x13, 0x08, 0x68, 0xd1, 0x70, 0x44, 0x02, 0x11,
|
||||
0xe2, 0xed, 0x8e, 0x99, 0x71, 0xae, 0xaf, 0x35, 0x98, 0xf2, 0x26, 0x97, 0xa3, 0xac, 0x41, 0x90,
|
||||
0xd8, 0xb4, 0x08, 0x2a, 0xd8, 0xf5, 0xa5, 0xf1, 0x6f, 0xbb, 0xd8, 0x3d, 0xc2, 0x4e, 0xbb, 0x9b,
|
||||
0xdd, 0xef, 0x41, 0x22, 0xae, 0xec, 0x89, 0x0f, 0xca, 0x55, 0xfb, 0xc0, 0xc7, 0xfd, 0x1e, 0xa4,
|
||||
0xb3, 0xfb, 0xe6, 0x56, 0xeb, 0x69, 0x3c, 0xbb, 0x9c, 0x97, 0x7c, 0x99, 0x27, 0x3b, 0x3c, 0xe3,
|
||||
0x25, 0xd8, 0x7d, 0xf3, 0x4a, 0x0d, 0x50, 0x62, 0xf7, 0xad, 0x43, 0xc5, 0x66, 0x70, 0x6e, 0x29,
|
||||
0x46, 0x59, 0x3a, 0x87, 0x2b, 0x6a, 0xcf, 0x90, 0x04, 0x88, 0x0c, 0x0e, 0x05, 0x91, 0x20, 0x52,
|
||||
0x2b, 0x6e, 0x91, 0xce, 0xe2, 0x4c, 0xf9, 0xdb, 0xa6, 0xcd, 0x78, 0x60, 0x67, 0x10, 0x21, 0x0a,
|
||||
0x48, 0x3d, 0xa7, 0xcb, 0x32, 0x1f, 0xe7, 0x82, 0x93, 0xf5, 0x6c, 0x80, 0xce, 0x7a, 0x3a, 0x20,
|
||||
0x18, 0x56, 0xa7, 0xec, 0x75, 0x5d, 0x9a, 0xfa, 0x1f, 0x6c, 0x58, 0xad, 0xff, 0x3e, 0xd4, 0xf2,
|
||||
0xd0, 0xb0, 0x0a, 0x38, 0x50, 0x19, 0xed, 0x44, 0x05, 0x4c, 0x40, 0xdb, 0x0f, 0x93, 0x7b, 0xdd,
|
||||
0x20, 0xee, 0x67, 0x22, 0x56, 0x19, 0x0b, 0xf9, 0x91, 0x40, 0x1f, 0x3f, 0x0d, 0x68, 0xb7, 0x5b,
|
||||
0xbc, 0xfa, 0x5c, 0xb0, 0xd9, 0x65, 0xeb, 0x00, 0x9b, 0x5f, 0x50, 0x85, 0x10, 0xdb, 0x2d, 0x04,
|
||||
0x8a, 0x37, 0xd1, 0x78, 0xc6, 0xf3, 0x50, 0x13, 0xd5, 0xf2, 0x3e, 0x4d, 0xa4, 0x39, 0xbb, 0xf8,
|
||||
0x35, 0x52, 0x1d, 0x99, 0xaa, 0x99, 0x36, 0x09, 0x0b, 0x2e, 0x44, 0x2c, 0x7e, 0x49, 0xd8, 0xe6,
|
||||
0xe4, 0xd0, 0xe7, 0x61, 0xfb, 0x74, 0x7f, 0xcb, 0xca, 0x21, 0x7d, 0xba, 0x9f, 0x62, 0xe9, 0x4a,
|
||||
0xaa, 0x18, 0xe9, 0xb0, 0xe2, 0xc7, 0xc9, 0xc3, 0x7e, 0xb0, 0x5d, 0xf2, 0x78, 0x3e, 0x77, 0x32,
|
||||
0x16, 0x97, 0xca, 0xeb, 0x56, 0xc0, 0x90, 0xc5, 0x88, 0x25, 0x4f, 0x00, 0x07, 0x43, 0x98, 0xe7,
|
||||
0x79, 0x87, 0xe7, 0x82, 0xe5, 0x02, 0x1b, 0xc2, 0x7c, 0x63, 0x1a, 0x0c, 0x0d, 0x61, 0x94, 0x02,
|
||||
0x88, 0x5b, 0xb9, 0x1f, 0xc4, 0xc4, 0x51, 0xbc, 0x40, 0x33, 0x36, 0xb5, 0xd7, 0xa3, 0xe4, 0xa1,
|
||||
0xb8, 0x05, 0x9c, 0xf3, 0x3a, 0xd7, 0xf5, 0x32, 0x8d, 0xcb, 0xb9, 0xd9, 0xdd, 0x48, 0x06, 0x8f,
|
||||
0x68, 0x3b, 0x3e, 0x49, 0xbc, 0xce, 0x0d, 0x6b, 0x80, 0x61, 0x67, 0xbc, 0x88, 0xe7, 0xa6, 0xa6,
|
||||
0x48, 0x0d, 0xa4, 0xbc, 0x55, 0xd5, 0x7b, 0xdd, 0x20, 0xf0, 0xf3, 0x22, 0x4d, 0x18, 0x0f, 0xf8,
|
||||
0x91, 0xf2, 0x3e, 0x7e, 0x20, 0x08, 0xb2, 0xb7, 0xba, 0xde, 0x6a, 0x45, 0x37, 0xca, 0x13, 0xbd,
|
||||
0x8e, 0x1d, 0x12, 0x8f, 0x07, 0x70, 0xa1, 0xec, 0x8d, 0xe0, 0x41, 0x1f, 0x6d, 0x36, 0x68, 0x43,
|
||||
0x7d, 0xd4, 0xec, 0xbf, 0xf6, 0xe9, 0xa3, 0x18, 0xac, 0x7d, 0xfe, 0x58, 0xf7, 0xd1, 0xdd, 0x58,
|
||||
0xc4, 0x75, 0xde, 0xfe, 0x22, 0x65, 0xaf, 0xf4, 0x42, 0x18, 0xa9, 0x6f, 0x43, 0x0d, 0xe5, 0xc7,
|
||||
0xc9, 0x60, 0x55, 0xbc, 0xdd, 0x9b, 0x0f, 0xf8, 0xd6, 0x2b, 0x84, 0x4e, 0xdf, 0x60, 0xa9, 0xb0,
|
||||
0xdd, 0x9b, 0x0f, 0xf8, 0xd6, 0x77, 0x2c, 0x74, 0xfa, 0x06, 0x17, 0x2d, 0x6c, 0xf7, 0xe6, 0xb5,
|
||||
0xef, 0xbf, 0x6c, 0x3a, 0xae, 0xeb, 0xbc, 0xce, 0xc3, 0x66, 0x22, 0xbd, 0x62, 0x58, 0x3a, 0xe9,
|
||||
0xdb, 0x33, 0x68, 0x28, 0x9d, 0xa4, 0x55, 0x9c, 0x5b, 0xce, 0xb0, 0x52, 0x1c, 0xf3, 0x2a, 0x95,
|
||||
0xc7, 0x31, 0x9e, 0xf4, 0x30, 0xda, 0xc0, 0xa1, 0x45, 0x53, 0x48, 0xc9, 0xbe, 0x58, 0xf6, 0x50,
|
||||
0x7b, 0x5e, 0xfd, 0x61, 0xc0, 0x5e, 0xfb, 0xd8, 0xfa, 0x56, 0x4f, 0xda, 0xbe, 0xe2, 0xf5, 0x98,
|
||||
0xe6, 0xe5, 0xdc, 0x84, 0xa1, 0xb3, 0x84, 0x31, 0x65, 0x5e, 0xda, 0xba, 0x6f, 0x29, 0x1f, 0xf5,
|
||||
0x57, 0xe8, 0x70, 0x3f, 0x4a, 0x92, 0x7e, 0xee, 0xdd, 0xb7, 0xdb, 0x8f, 0xfa, 0x2b, 0x68, 0xf7,
|
||||
0x7f, 0xdd, 0x2c, 0x6b, 0xa0, 0x7f, 0xdd, 0x07, 0x1f, 0xf7, 0xb1, 0x08, 0xfa, 0xe1, 0x93, 0x6b,
|
||||
0xe9, 0xe8, 0x82, 0xfc, 0xa4, 0x59, 0xbf, 0x37, 0xa8, 0xfc, 0x68, 0x48, 0x7e, 0x6c, 0xac, 0xbb,
|
||||
0x64, 0x28, 0xaa, 0x2c, 0x0c, 0x3b, 0xe6, 0xc7, 0xd7, 0xd4, 0x72, 0xae, 0xdc, 0xf3, 0x60, 0xfd,
|
||||
0x71, 0xab, 0x53, 0x9e, 0x90, 0x65, 0x87, 0x86, 0x05, 0xfa, 0xe4, 0xba, 0x6a, 0x54, 0x57, 0x75,
|
||||
0x60, 0x79, 0xe9, 0xcc, 0x93, 0x9e, 0x86, 0xbd, 0x6b, 0x68, 0x3e, 0xba, 0x9e, 0x92, 0x2e, 0xcb,
|
||||
0x7f, 0xae, 0x45, 0x77, 0x3d, 0xd6, 0xbe, 0xce, 0x00, 0x9b, 0x2e, 0x3f, 0x08, 0xd8, 0xa7, 0x94,
|
||||
0x4c, 0xe1, 0x7e, 0xe7, 0x9b, 0x29, 0xdb, 0xfb, 0xe9, 0x3c, 0x95, 0xbd, 0x34, 0x13, 0xac, 0x6c,
|
||||
0xdf, 0x4f, 0xe7, 0xdb, 0x55, 0xd4, 0x90, 0xbe, 0x9f, 0x2e, 0x80, 0x3b, 0xf7, 0xd3, 0x21, 0x9e,
|
||||
0xd1, 0xfb, 0xe9, 0x50, 0x6b, 0xc1, 0xfb, 0xe9, 0xc2, 0x1a, 0xd4, 0xec, 0xd2, 0x14, 0x41, 0x6d,
|
||||
0x9b, 0xf7, 0xb2, 0xe8, 0xef, 0xa2, 0x3f, 0xbe, 0x8e, 0x0a, 0x31, 0xbf, 0x2a, 0x4e, 0x1e, 0xa8,
|
||||
0xec, 0xf1, 0x4c, 0xbd, 0x43, 0x95, 0xdb, 0xbd, 0x79, 0xed, 0xfb, 0x47, 0x7a, 0x71, 0x65, 0x66,
|
||||
0x13, 0x5e, 0xca, 0xbb, 0x09, 0x37, 0x43, 0xb3, 0x43, 0x6d, 0xc1, 0x6d, 0xf9, 0x87, 0xfd, 0x60,
|
||||
0xa2, 0xba, 0x35, 0xa1, 0x1b, 0x7d, 0xd8, 0x65, 0x08, 0x34, 0xf9, 0x76, 0x6f, 0x9e, 0x98, 0x46,
|
||||
0x94, 0x6f, 0xd5, 0xda, 0x3d, 0x8c, 0xf9, 0x6d, 0xfd, 0xa8, 0xbf, 0x82, 0x76, 0x7f, 0xa5, 0xb3,
|
||||
0x56, 0xd7, 0xbd, 0x6c, 0xe7, 0xad, 0x2e, 0x53, 0x13, 0xaf, 0x99, 0x87, 0x7d, 0xf1, 0x50, 0xfe,
|
||||
0xe2, 0x4e, 0xa1, 0x5d, 0xf9, 0x0b, 0x3a, 0x8d, 0x7e, 0x74, 0x3d, 0x25, 0x5d, 0x96, 0x7f, 0x5c,
|
||||
0x8b, 0x6e, 0x92, 0x65, 0xd1, 0x71, 0xf0, 0x49, 0x5f, 0xcb, 0x20, 0x1e, 0x3e, 0xbd, 0xb6, 0x9e,
|
||||
0x2e, 0xd4, 0xbf, 0xac, 0x45, 0xb7, 0x02, 0x85, 0x52, 0x01, 0x72, 0x0d, 0xeb, 0x7e, 0xa0, 0x7c,
|
||||
0x76, 0x7d, 0x45, 0x6a, 0xba, 0x77, 0xf1, 0x49, 0xfb, 0xae, 0xb1, 0x80, 0xed, 0x09, 0x7d, 0xd7,
|
||||
0x58, 0xb7, 0x16, 0xdc, 0x63, 0x8a, 0xcf, 0x9a, 0x35, 0x1f, 0xba, 0xc7, 0x24, 0x8f, 0x42, 0x06,
|
||||
0x6f, 0x31, 0xc1, 0x38, 0xcc, 0xc9, 0xb3, 0xd7, 0x45, 0x9c, 0x27, 0xb4, 0x13, 0x25, 0xef, 0x76,
|
||||
0x62, 0x38, 0xb8, 0x37, 0x57, 0x4b, 0x4f, 0x78, 0xb3, 0x8e, 0xbb, 0x4f, 0xe9, 0x1b, 0x24, 0xb8,
|
||||
0x37, 0xd7, 0x42, 0x09, 0x6f, 0x3a, 0x6b, 0x0c, 0x79, 0x03, 0xc9, 0xe2, 0x83, 0x3e, 0x28, 0x58,
|
||||
0x21, 0x18, 0x6f, 0x66, 0xcb, 0xff, 0x61, 0xc8, 0x4a, 0x6b, 0xdb, 0x7f, 0xab, 0x27, 0x4d, 0xb8,
|
||||
0x9d, 0x30, 0xf1, 0x39, 0x8b, 0x13, 0x56, 0x06, 0xdd, 0x1a, 0xaa, 0x97, 0x5b, 0x97, 0xc6, 0xdc,
|
||||
0xee, 0xf0, 0x6c, 0xb9, 0xc8, 0x75, 0x63, 0x92, 0x6e, 0x5d, 0xaa, 0xdb, 0x2d, 0xa0, 0xe1, 0xae,
|
||||
0xa4, 0x75, 0x2b, 0xd3, 0xcb, 0x07, 0x61, 0x33, 0x5e, 0x56, 0xb9, 0xd9, 0x8b, 0xa5, 0xeb, 0xa9,
|
||||
0xc3, 0xa8, 0xa3, 0x9e, 0x20, 0x92, 0xb6, 0x7a, 0xd2, 0x70, 0x7b, 0xd0, 0x71, 0x6b, 0xe2, 0x69,
|
||||
0xbb, 0xc3, 0x56, 0x2b, 0xa4, 0x1e, 0xf5, 0x57, 0x80, 0x9b, 0xb1, 0x3a, 0xaa, 0x0e, 0xd2, 0x4a,
|
||||
0xec, 0xa5, 0x59, 0x36, 0xd8, 0x0c, 0x84, 0x49, 0x03, 0x05, 0x37, 0x63, 0x11, 0x98, 0x88, 0xe4,
|
||||
0x66, 0xf3, 0x32, 0x1f, 0x74, 0xd9, 0x91, 0x54, 0xaf, 0x48, 0x76, 0x69, 0xb0, 0xa1, 0xe6, 0x3c,
|
||||
0x6a, 0x53, 0xdb, 0x61, 0xf8, 0xc1, 0xb5, 0x2a, 0xbc, 0xdd, 0x9b, 0x07, 0x6f, 0xfb, 0x25, 0x25,
|
||||
0x67, 0x96, 0x3b, 0x94, 0x09, 0x6f, 0x26, 0xb9, 0xdb, 0x41, 0x81, 0x4d, 0x49, 0xd5, 0x8d, 0x5e,
|
||||
0xa6, 0xc9, 0x9c, 0x09, 0xf4, 0x45, 0x95, 0x0b, 0x04, 0x5f, 0x54, 0x01, 0x10, 0x34, 0x9d, 0xfa,
|
||||
0xbb, 0xd9, 0x8d, 0x1d, 0x27, 0x58, 0xd3, 0x69, 0x65, 0x87, 0x0a, 0x35, 0x1d, 0x4a, 0x83, 0xd1,
|
||||
0xc0, 0xb8, 0xd5, 0xf7, 0x3e, 0x3c, 0x08, 0x99, 0x01, 0x97, 0x3f, 0x6c, 0xf6, 0x62, 0xc1, 0x8c,
|
||||
0x62, 0x1d, 0xa6, 0x8b, 0x54, 0x60, 0x33, 0x8a, 0x63, 0xa3, 0x46, 0x42, 0x33, 0x4a, 0x1b, 0xa5,
|
||||
0xaa, 0x57, 0xe7, 0x08, 0xe3, 0x24, 0x5c, 0x3d, 0xc5, 0xf4, 0xab, 0x9e, 0x61, 0x5b, 0xef, 0x55,
|
||||
0x73, 0x13, 0x32, 0xe2, 0x42, 0x2f, 0x96, 0x91, 0xd8, 0x96, 0xdf, 0x03, 0x43, 0x30, 0x34, 0xea,
|
||||
0x50, 0x0a, 0xf0, 0x7d, 0x41, 0xcd, 0x35, 0xaf, 0x7e, 0x8b, 0x82, 0xc5, 0x65, 0x9c, 0xcf, 0xd0,
|
||||
0xc5, 0xa9, 0x34, 0xd8, 0x22, 0x43, 0x8b, 0x53, 0x52, 0x03, 0xbc, 0xb5, 0xf7, 0xbf, 0xe4, 0x45,
|
||||
0xba, 0x82, 0xf9, 0x64, 0xd6, 0xff, 0x90, 0xf7, 0x7e, 0x0f, 0x12, 0xbe, 0xb5, 0x6f, 0x00, 0xb3,
|
||||
0xef, 0xae, 0x9c, 0x7e, 0x18, 0x30, 0xe5, 0xa3, 0xa1, 0x85, 0x30, 0xad, 0x02, 0x82, 0xda, 0xd9,
|
||||
0x5b, 0xfc, 0x82, 0xad, 0xb0, 0xa0, 0x76, 0x37, 0x09, 0xbf, 0x60, 0xab, 0x50, 0x50, 0xb7, 0x51,
|
||||
0x90, 0x67, 0xba, 0xeb, 0xa0, 0xf5, 0x80, 0xbe, 0xbb, 0xf4, 0xd9, 0xe8, 0xe4, 0x40, 0xcf, 0xd9,
|
||||
0x4d, 0xaf, 0xbc, 0xd7, 0x14, 0x48, 0x41, 0x77, 0xd3, 0x2b, 0xfc, 0x2d, 0xc5, 0x66, 0x2f, 0x16,
|
||||
0x9e, 0x08, 0x88, 0x05, 0x7b, 0xdd, 0xbc, 0xaa, 0x47, 0x8a, 0x2b, 0xe5, 0xad, 0x77, 0xf5, 0xf7,
|
||||
0xba, 0x41, 0x7b, 0xfe, 0xf6, 0xb8, 0xe4, 0x33, 0x56, 0x55, 0xfa, 0x4a, 0x54, 0xff, 0x80, 0x93,
|
||||
0x96, 0x0d, 0xc1, 0x85, 0xa8, 0x77, 0xc2, 0x90, 0x73, 0x8f, 0xa1, 0x12, 0xd9, 0xeb, 0x95, 0xd6,
|
||||
0x51, 0xcd, 0xf6, 0xcd, 0x4a, 0x1b, 0x9d, 0x9c, 0xed, 0x5e, 0x5a, 0xea, 0xde, 0xa7, 0x74, 0x0f,
|
||||
0x55, 0xc7, 0xae, 0x52, 0xba, 0xdf, 0x83, 0xd4, 0xae, 0x3e, 0x8f, 0xde, 0x3c, 0xe0, 0xf3, 0x09,
|
||||
0xcb, 0x93, 0xc1, 0xf7, 0xfc, 0x13, 0xbc, 0x7c, 0x3e, 0xac, 0xff, 0x6c, 0x8c, 0xde, 0xa0, 0xc4,
|
||||
0xf6, 0x0c, 0xe2, 0x2e, 0x3b, 0x5b, 0xce, 0x27, 0x22, 0x16, 0xe0, 0x0c, 0xa2, 0xfc, 0xfb, 0xb0,
|
||||
0x16, 0x10, 0x67, 0x10, 0x3d, 0x00, 0xd8, 0x9b, 0x96, 0x8c, 0xa1, 0xf6, 0x6a, 0x41, 0xd0, 0x9e,
|
||||
0x06, 0x6c, 0x16, 0x61, 0xec, 0xd5, 0x89, 0x3a, 0x3c, 0x33, 0x68, 0x75, 0xa4, 0x94, 0xc8, 0x22,
|
||||
0xda, 0x94, 0x0d, 0x6e, 0x55, 0x7d, 0x79, 0xbd, 0xcd, 0x72, 0xb1, 0x88, 0xcb, 0x15, 0x08, 0x6e,
|
||||
0x5d, 0x4b, 0x07, 0x20, 0x82, 0x1b, 0x05, 0x6d, 0xaf, 0x6d, 0x1e, 0xf3, 0xec, 0x72, 0x9f, 0x97,
|
||||
0x7c, 0x29, 0xd2, 0x9c, 0xc1, 0x2b, 0x4e, 0xcc, 0x03, 0x75, 0x19, 0xa2, 0xd7, 0x52, 0xac, 0xcd,
|
||||
0x72, 0x25, 0xa1, 0x8e, 0x33, 0xca, 0x3b, 0xe1, 0x2b, 0xc1, 0x4b, 0xf8, 0x3a, 0x53, 0x59, 0x81,
|
||||
0x10, 0x91, 0xe5, 0x92, 0x30, 0x68, 0xfb, 0xe3, 0x34, 0x9f, 0xa3, 0x6d, 0x7f, 0xec, 0x5e, 0x33,
|
||||
0x7c, 0x8b, 0x06, 0x6c, 0x87, 0x52, 0x0f, 0x4d, 0x75, 0x00, 0xfd, 0xd1, 0x30, 0xfa, 0xd0, 0x5d,
|
||||
0x82, 0xe8, 0x50, 0x38, 0x09, 0x5c, 0x3d, 0x2f, 0x58, 0xce, 0x92, 0xe6, 0xd0, 0x1e, 0xe6, 0xca,
|
||||
0x23, 0x82, 0xae, 0x20, 0x69, 0xc7, 0x22, 0x29, 0x3f, 0x59, 0xe6, 0xc7, 0x25, 0x3f, 0x4f, 0x33,
|
||||
0x56, 0x82, 0xb1, 0x48, 0xa9, 0x3b, 0x72, 0x62, 0x2c, 0xc2, 0x38, 0x7b, 0xfa, 0x43, 0x4a, 0xbd,
|
||||
0x1f, 0x36, 0x98, 0x96, 0xf1, 0x0c, 0x9e, 0xfe, 0x50, 0x36, 0xda, 0x18, 0xb1, 0x33, 0x18, 0xc0,
|
||||
0x9d, 0x44, 0x47, 0xb9, 0xce, 0x57, 0x32, 0x3e, 0xf4, 0x47, 0xab, 0xf2, 0xf2, 0xdd, 0x0a, 0x24,
|
||||
0x3a, 0xda, 0x1c, 0x46, 0x12, 0x89, 0x4e, 0x58, 0xc3, 0x4e, 0x25, 0x92, 0x3b, 0xd2, 0xa7, 0x9a,
|
||||
0xc0, 0x54, 0xa2, 0x6c, 0x34, 0x42, 0x62, 0x2a, 0x69, 0x41, 0x60, 0x40, 0x6a, 0xba, 0xc1, 0x1c,
|
||||
0x1d, 0x90, 0x8c, 0x34, 0x38, 0x20, 0xb9, 0x94, 0x1d, 0x28, 0xc6, 0x79, 0x2a, 0xd2, 0x38, 0x9b,
|
||||
0x30, 0x71, 0x1c, 0x97, 0xf1, 0x82, 0x09, 0x56, 0xc2, 0x81, 0x42, 0x23, 0x43, 0x8f, 0x21, 0x06,
|
||||
0x0a, 0x8a, 0xd5, 0x0e, 0x7f, 0x37, 0x7a, 0xbb, 0x9e, 0xf7, 0x59, 0xae, 0x7f, 0x92, 0xe9, 0x99,
|
||||
0xfc, 0x2d, 0xb7, 0xc1, 0x3b, 0xc6, 0xc6, 0x44, 0x94, 0x2c, 0x5e, 0x34, 0xb6, 0xdf, 0x32, 0x7f,
|
||||
0x97, 0xe0, 0xa3, 0xb5, 0x3a, 0x9e, 0x8f, 0xb8, 0x48, 0xcf, 0xeb, 0x65, 0xb6, 0xfe, 0x80, 0x09,
|
||||
0xc4, 0xb3, 0x2b, 0x1e, 0x06, 0x2e, 0x3d, 0xc1, 0x38, 0x3b, 0x4e, 0xbb, 0xd2, 0x13, 0x56, 0x64,
|
||||
0x70, 0x9c, 0xf6, 0xb4, 0x25, 0x40, 0x8c, 0xd3, 0x28, 0x68, 0x3b, 0xa7, 0x2b, 0x9e, 0xb2, 0x70,
|
||||
0x65, 0xa6, 0xac, 0x5f, 0x65, 0xa6, 0xde, 0x37, 0x21, 0x59, 0xf4, 0xf6, 0x21, 0x5b, 0x9c, 0xb1,
|
||||
0xb2, 0xba, 0x48, 0x0b, 0xea, 0x82, 0x60, 0x4b, 0x74, 0x5e, 0x10, 0x4c, 0xa0, 0x76, 0x26, 0xb0,
|
||||
0xc0, 0xb8, 0x3a, 0x8a, 0x17, 0x4c, 0x5e, 0xe1, 0x02, 0x66, 0x02, 0xc7, 0x88, 0x03, 0x11, 0x33,
|
||||
0x01, 0x09, 0x3b, 0x9f, 0x97, 0x59, 0xe6, 0x84, 0xcd, 0xeb, 0x08, 0x2b, 0x8f, 0xe3, 0xd5, 0x82,
|
||||
0xe5, 0x42, 0x9b, 0x04, 0x7b, 0xf2, 0x8e, 0x49, 0x9c, 0x27, 0xf6, 0xe4, 0xfb, 0xe8, 0x39, 0x43,
|
||||
0x93, 0xf7, 0xe0, 0x8f, 0x79, 0x29, 0xd4, 0x0f, 0xae, 0x9d, 0x96, 0x19, 0x18, 0x9a, 0xfc, 0x87,
|
||||
0xea, 0x91, 0xc4, 0xd0, 0x14, 0xd6, 0x70, 0x7e, 0x5c, 0xc3, 0x2b, 0xc3, 0x0b, 0x56, 0x9a, 0x38,
|
||||
0x79, 0xb6, 0x88, 0xd3, 0x4c, 0x47, 0xc3, 0xf7, 0x03, 0xb6, 0x09, 0x1d, 0xe2, 0xc7, 0x35, 0xfa,
|
||||
0xea, 0x3a, 0x3f, 0x47, 0x12, 0x2e, 0x21, 0x78, 0x45, 0xd0, 0x61, 0x9f, 0x78, 0x45, 0xd0, 0xad,
|
||||
0x65, 0x57, 0xee, 0x96, 0x95, 0xdc, 0x4a, 0x12, 0x3b, 0x3c, 0x81, 0xfb, 0x85, 0x8e, 0x4d, 0x00,
|
||||
0x12, 0x2b, 0xf7, 0xa0, 0x82, 0x4d, 0x0d, 0x2c, 0xb6, 0x97, 0xe6, 0x71, 0x96, 0xfe, 0x18, 0xa6,
|
||||
0xf5, 0x8e, 0x9d, 0x86, 0x20, 0x52, 0x03, 0x9c, 0xc4, 0x5c, 0xed, 0x33, 0x31, 0x4d, 0xeb, 0xa1,
|
||||
0xff, 0x5e, 0xe0, 0xb9, 0x49, 0xa2, 0xdb, 0x95, 0x43, 0x3a, 0x97, 0x01, 0xc3, 0xc7, 0x3a, 0x2a,
|
||||
0x8a, 0x49, 0x3d, 0xab, 0x9e, 0xb0, 0x19, 0x4b, 0x0b, 0x31, 0xf8, 0x38, 0xfc, 0xac, 0x00, 0x4e,
|
||||
0x1c, 0xb4, 0xe8, 0xa1, 0xe6, 0xbc, 0xbe, 0xaf, 0xc7, 0x92, 0x89, 0xfa, 0x25, 0xd2, 0xd3, 0x8a,
|
||||
0x95, 0x3a, 0xd1, 0xd8, 0x67, 0x02, 0xf4, 0x4e, 0x87, 0x1b, 0x3a, 0x60, 0x5d, 0x51, 0xa2, 0x77,
|
||||
0x86, 0x35, 0xec, 0x66, 0x9f, 0xc3, 0xe9, 0xcb, 0xdd, 0xe5, 0x71, 0xc7, 0x87, 0xa4, 0x31, 0x87,
|
||||
0x22, 0x36, 0xfb, 0x68, 0xda, 0x66, 0x6b, 0x6d, 0xb7, 0xa3, 0x7c, 0x35, 0x86, 0x47, 0x26, 0x10,
|
||||
0x4b, 0x12, 0x23, 0xb2, 0xb5, 0x00, 0xee, 0x6c, 0x86, 0x97, 0x3c, 0x4e, 0x66, 0x71, 0x25, 0x8e,
|
||||
0xe3, 0x55, 0xc6, 0xe3, 0x44, 0xce, 0xeb, 0x70, 0x33, 0xbc, 0x61, 0x86, 0x2e, 0x44, 0x6d, 0x86,
|
||||
0x53, 0xb0, 0x9b, 0x9d, 0xc9, 0x1f, 0x58, 0xd5, 0x47, 0x49, 0x61, 0x76, 0x26, 0xcb, 0x0b, 0x8f,
|
||||
0x91, 0xde, 0x09, 0x43, 0xf6, 0x13, 0x38, 0x25, 0x92, 0x69, 0xc8, 0x2d, 0x4c, 0xc7, 0x4b, 0x40,
|
||||
0xde, 0x0f, 0x10, 0xf6, 0x02, 0x14, 0xf5, 0xf7, 0xe6, 0x37, 0xb5, 0x84, 0xbe, 0x32, 0xfd, 0x21,
|
||||
0xa6, 0xeb, 0x42, 0xde, 0x09, 0xb5, 0xad, 0x9e, 0xb4, 0x4d, 0x33, 0x77, 0x2e, 0x62, 0x31, 0x4a,
|
||||
0x92, 0x43, 0x56, 0x21, 0xdf, 0xb3, 0xd7, 0xc2, 0xa1, 0x95, 0x12, 0x69, 0x66, 0x9b, 0xb2, 0x81,
|
||||
0x5e, 0xcb, 0x9e, 0x25, 0xa9, 0xd0, 0xb2, 0xe6, 0x80, 0xf6, 0xc3, 0xb6, 0x81, 0x36, 0x45, 0xd4,
|
||||
0x8a, 0xa6, 0xed, 0x58, 0x5e, 0x33, 0x53, 0x3e, 0x9f, 0x67, 0x4c, 0x43, 0x27, 0x2c, 0x56, 0x37,
|
||||
0x46, 0x6e, 0xb7, 0x6d, 0xa1, 0x20, 0x31, 0x96, 0x07, 0x15, 0x6c, 0x1a, 0x59, 0x63, 0xea, 0x95,
|
||||
0x54, 0xf3, 0x60, 0x37, 0xda, 0x66, 0x3c, 0x80, 0x48, 0x23, 0x51, 0xd0, 0x7e, 0x76, 0x57, 0x8b,
|
||||
0xf7, 0x59, 0xf3, 0x24, 0xe0, 0x5d, 0x57, 0x52, 0xd9, 0x11, 0x13, 0x9f, 0xdd, 0x21, 0x98, 0x5d,
|
||||
0x27, 0x00, 0x0f, 0x4f, 0x57, 0xe3, 0x04, 0xae, 0x13, 0xa0, 0xbe, 0x64, 0x88, 0x75, 0x02, 0xc5,
|
||||
0xfa, 0x4d, 0x67, 0xf6, 0xbd, 0x0e, 0xe2, 0xca, 0x56, 0x0e, 0x69, 0x3a, 0x14, 0x0c, 0x35, 0x1d,
|
||||
0xa5, 0xe0, 0x3f, 0x52, 0x77, 0x6b, 0x0d, 0x79, 0xa4, 0xd8, 0xbe, 0xda, 0x7a, 0x17, 0x66, 0x73,
|
||||
0xff, 0x5a, 0x78, 0xc2, 0xe2, 0xc4, 0x54, 0x0c, 0xd1, 0x75, 0xe5, 0x44, 0xee, 0x8f, 0x71, 0xda,
|
||||
0xc9, 0x1f, 0x44, 0x03, 0x55, 0x8d, 0xd2, 0x75, 0x73, 0x0b, 0x2b, 0x62, 0x4d, 0x10, 0x03, 0x95,
|
||||
0x4f, 0x38, 0x89, 0x9b, 0xd7, 0x44, 0x53, 0xae, 0x1d, 0xe8, 0xcf, 0x42, 0x2b, 0x90, 0xb8, 0xf9,
|
||||
0x8f, 0xbd, 0x45, 0x13, 0x89, 0x5b, 0xb7, 0x96, 0x73, 0xed, 0x0f, 0x68, 0xb2, 0xbd, 0x92, 0x2f,
|
||||
0x60, 0x99, 0x3e, 0x0b, 0x36, 0x0f, 0xa2, 0x41, 0x5c, 0xfb, 0xd3, 0x4f, 0xd3, 0xce, 0x41, 0x66,
|
||||
0xef, 0x40, 0x1e, 0x4f, 0xc3, 0x7f, 0x16, 0x44, 0x09, 0x89, 0x39, 0xa8, 0x05, 0x39, 0xbf, 0x25,
|
||||
0x3a, 0x7e, 0x59, 0xa6, 0x22, 0xcd, 0xe7, 0x53, 0xce, 0x33, 0xb8, 0x65, 0x39, 0x1a, 0x0f, 0x5d,
|
||||
0x29, 0xf5, 0x5b, 0xa2, 0x2d, 0xca, 0x4e, 0x71, 0xa3, 0xf1, 0x68, 0x29, 0xf8, 0x79, 0x9a, 0x65,
|
||||
0x20, 0x72, 0x46, 0xe3, 0x61, 0x23, 0x21, 0x22, 0xc7, 0x27, 0x9c, 0x5f, 0xc0, 0x1c, 0xcb, 0xdd,
|
||||
0x7f, 0xbd, 0x03, 0xfa, 0x01, 0xd4, 0x71, 0x84, 0xd4, 0x2f, 0x60, 0x42, 0xc8, 0xf9, 0x45, 0xcf,
|
||||
0x31, 0xf6, 0xdb, 0x26, 0x9b, 0x50, 0x1d, 0x81, 0xa8, 0x5f, 0xf4, 0xa4, 0x60, 0xe7, 0x9b, 0xe4,
|
||||
0xe3, 0x65, 0x75, 0xe1, 0x6f, 0x19, 0xa8, 0xc5, 0xa1, 0xba, 0x20, 0xf5, 0x09, 0xf8, 0x85, 0x1d,
|
||||
0x9f, 0x1d, 0x7a, 0x30, 0x71, 0x3c, 0xad, 0x53, 0x49, 0x15, 0xe6, 0xe9, 0xfb, 0xff, 0xfd, 0xd5,
|
||||
0x8d, 0xb5, 0x9f, 0x7d, 0x75, 0x63, 0xed, 0x7f, 0xbf, 0xba, 0xb1, 0xf6, 0xd3, 0xaf, 0x6f, 0xbc,
|
||||
0xf1, 0xb3, 0xaf, 0x6f, 0xbc, 0xf1, 0x3f, 0x5f, 0xdf, 0x78, 0xe3, 0xcb, 0x37, 0xf5, 0x6f, 0xee,
|
||||
0x9f, 0xfd, 0x82, 0xfc, 0xe5, 0xfc, 0x27, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x89, 0x4b, 0xe1,
|
||||
0x93, 0x97, 0x7f, 0x00, 0x00,
|
||||
// 5713 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x9d, 0xdd, 0x6f, 0x24, 0x49,
|
||||
0x52, 0xc0, 0xd7, 0x2f, 0x2c, 0xd4, 0x71, 0x0b, 0xf4, 0xc2, 0xb2, 0xb7, 0xdc, 0xcd, 0xcc, 0xce,
|
||||
0x87, 0x3d, 0x33, 0x1e, 0xb7, 0x67, 0x67, 0xf6, 0x8b, 0x3b, 0x24, 0xe8, 0xb1, 0xc7, 0xde, 0xbe,
|
||||
0xb5, 0xbd, 0xc6, 0xdd, 0x9e, 0x11, 0x2b, 0x21, 0x51, 0xee, 0x4a, 0xb7, 0x0b, 0x57, 0x57, 0xd6,
|
||||
0x55, 0x65, 0x7b, 0xa6, 0x0f, 0x81, 0x40, 0x20, 0x10, 0x08, 0xc4, 0x89, 0x2f, 0xc1, 0x13, 0x12,
|
||||
0x7f, 0x01, 0x7f, 0x06, 0x8f, 0xf7, 0x78, 0x8f, 0x68, 0xf7, 0xef, 0x40, 0x42, 0x95, 0x95, 0x9f,
|
||||
0x51, 0x11, 0x59, 0xe5, 0xbd, 0xa7, 0x19, 0x39, 0x7e, 0x11, 0x91, 0x1f, 0x91, 0x99, 0x91, 0x59,
|
||||
0x59, 0xd5, 0xd1, 0xcd, 0xe2, 0x6c, 0xbb, 0x28, 0xb9, 0xe0, 0xd5, 0x76, 0xc5, 0xca, 0xab, 0x74,
|
||||
0xc6, 0xf4, 0xbf, 0x43, 0xf9, 0xe7, 0xc1, 0x9b, 0x71, 0xbe, 0x12, 0xab, 0x82, 0xbd, 0xf7, 0xae,
|
||||
0x25, 0x67, 0x7c, 0xb1, 0x88, 0xf3, 0xa4, 0x6a, 0x90, 0xf7, 0xde, 0xb1, 0x12, 0x76, 0xc5, 0x72,
|
||||
0xa1, 0xfe, 0xfe, 0xe4, 0x67, 0xff, 0xb7, 0x16, 0xbd, 0xb5, 0x93, 0xa5, 0x2c, 0x17, 0x3b, 0x4a,
|
||||
0x63, 0xf0, 0x65, 0xf4, 0xed, 0x51, 0x51, 0xec, 0x33, 0xf1, 0x82, 0x95, 0x55, 0xca, 0xf3, 0xc1,
|
||||
0x9d, 0xa1, 0x72, 0x30, 0x3c, 0x29, 0x66, 0xc3, 0x51, 0x51, 0x0c, 0xad, 0x70, 0x78, 0xc2, 0x7e,
|
||||
0xb4, 0x64, 0x95, 0x78, 0xef, 0x6e, 0x18, 0xaa, 0x0a, 0x9e, 0x57, 0x6c, 0x70, 0x1e, 0xfd, 0xda,
|
||||
0xa8, 0x28, 0x26, 0x4c, 0xec, 0xb2, 0xba, 0x02, 0x13, 0x11, 0x0b, 0x36, 0xd8, 0x68, 0xa9, 0xfa,
|
||||
0x80, 0xf1, 0x71, 0xbf, 0x1b, 0x54, 0x7e, 0xa6, 0xd1, 0xb7, 0x6a, 0x3f, 0x17, 0x4b, 0x91, 0xf0,
|
||||
0x57, 0xf9, 0xe0, 0xfd, 0xb6, 0xa2, 0x12, 0x19, 0xdb, 0xb7, 0x43, 0x88, 0xb2, 0xfa, 0x32, 0xfa,
|
||||
0xe5, 0x97, 0x71, 0x96, 0x31, 0xb1, 0x53, 0xb2, 0xba, 0xe0, 0xbe, 0x4e, 0x23, 0x1a, 0x36, 0x32,
|
||||
0x63, 0xf7, 0x4e, 0x90, 0x51, 0x86, 0xbf, 0x8c, 0xbe, 0xdd, 0x48, 0x4e, 0xd8, 0x8c, 0x5f, 0xb1,
|
||||
0x72, 0x80, 0x6a, 0x29, 0x21, 0xd1, 0xe4, 0x2d, 0x08, 0xda, 0xde, 0xe1, 0xf9, 0x15, 0x2b, 0x05,
|
||||
0x6e, 0x5b, 0x09, 0xc3, 0xb6, 0x2d, 0xa4, 0x6c, 0xff, 0xed, 0x5a, 0xf4, 0xdd, 0xd1, 0x6c, 0xc6,
|
||||
0x97, 0xb9, 0x38, 0xe0, 0xb3, 0x38, 0x3b, 0x48, 0xf3, 0xcb, 0x23, 0xf6, 0x6a, 0xe7, 0xa2, 0xe6,
|
||||
0xf3, 0x39, 0x1b, 0x3c, 0xf5, 0x5b, 0xb5, 0x41, 0x87, 0x86, 0x1d, 0xba, 0xb0, 0xf1, 0xfd, 0xe1,
|
||||
0xf5, 0x94, 0x54, 0x59, 0xfe, 0x71, 0x2d, 0xba, 0x01, 0xcb, 0x32, 0xe1, 0xd9, 0x15, 0xb3, 0xa5,
|
||||
0xf9, 0xa8, 0xc3, 0xb0, 0x8f, 0x9b, 0xf2, 0x7c, 0x7c, 0x5d, 0x35, 0x55, 0xa2, 0x2c, 0x7a, 0xdb,
|
||||
0x0d, 0x97, 0x09, 0xab, 0xe4, 0x70, 0x7a, 0x40, 0x47, 0x84, 0x42, 0x8c, 0xe7, 0x87, 0x7d, 0x50,
|
||||
0xe5, 0x2d, 0x8d, 0x06, 0xca, 0x5b, 0xc6, 0x2b, 0xe3, 0xec, 0x3e, 0x6a, 0xc1, 0x21, 0x8c, 0xaf,
|
||||
0x07, 0x3d, 0x48, 0xe5, 0xea, 0x8f, 0xa2, 0x5f, 0x79, 0xc9, 0xcb, 0xcb, 0xaa, 0x88, 0x67, 0x4c,
|
||||
0x0d, 0x85, 0x7b, 0xbe, 0xb6, 0x96, 0xc2, 0xd1, 0xb0, 0xde, 0x85, 0x39, 0x41, 0xab, 0x85, 0x5f,
|
||||
0x14, 0x0c, 0xce, 0x41, 0x56, 0xb1, 0x16, 0x52, 0x41, 0x0b, 0x21, 0x65, 0xfb, 0x32, 0x1a, 0x58,
|
||||
0xdb, 0x67, 0x7f, 0xcc, 0x66, 0x62, 0x94, 0x24, 0xb0, 0x57, 0xac, 0xae, 0x24, 0x86, 0xa3, 0x24,
|
||||
0xa1, 0x7a, 0x05, 0x47, 0x95, 0xb3, 0x57, 0xd1, 0x3b, 0xc0, 0xd9, 0x41, 0x5a, 0x49, 0x87, 0x5b,
|
||||
0x61, 0x2b, 0x0a, 0x33, 0x4e, 0x87, 0x7d, 0x71, 0xe5, 0xf8, 0xcf, 0xd7, 0xa2, 0xef, 0x20, 0x9e,
|
||||
0x4f, 0xd8, 0x82, 0x5f, 0xb1, 0xc1, 0xe3, 0x6e, 0x6b, 0x0d, 0x69, 0xfc, 0x7f, 0x70, 0x0d, 0x0d,
|
||||
0x24, 0x4c, 0x26, 0x2c, 0x63, 0x33, 0x41, 0x86, 0x49, 0x23, 0xee, 0x0c, 0x13, 0x83, 0x39, 0x23,
|
||||
0x4c, 0x0b, 0xf7, 0x99, 0xd8, 0x59, 0x96, 0x25, 0xcb, 0x05, 0xd9, 0x97, 0x16, 0xe9, 0xec, 0x4b,
|
||||
0x0f, 0x45, 0xea, 0xb3, 0xcf, 0xc4, 0x28, 0xcb, 0xc8, 0xfa, 0x34, 0xe2, 0xce, 0xfa, 0x18, 0x4c,
|
||||
0x79, 0x98, 0x45, 0xbf, 0xea, 0xb4, 0x98, 0x18, 0xe7, 0xe7, 0x7c, 0x40, 0xb7, 0x85, 0x94, 0x1b,
|
||||
0x1f, 0x1b, 0x9d, 0x1c, 0x52, 0x8d, 0xe7, 0xaf, 0x0b, 0x5e, 0xd2, 0xdd, 0xd2, 0x88, 0x3b, 0xab,
|
||||
0x61, 0x30, 0xe5, 0xe1, 0x0f, 0xa3, 0xb7, 0xd4, 0x2c, 0xa9, 0xd7, 0xb3, 0xbb, 0xe8, 0x14, 0x0a,
|
||||
0x17, 0xb4, 0x7b, 0x1d, 0x54, 0xcb, 0xfc, 0x61, 0x3a, 0x2f, 0xeb, 0xd9, 0x07, 0x37, 0xaf, 0xa4,
|
||||
0x1d, 0xe6, 0x2d, 0xa5, 0xcc, 0xf3, 0xe8, 0xd7, 0x7d, 0xf3, 0x3b, 0x71, 0x3e, 0x63, 0xd9, 0xe0,
|
||||
0x61, 0x48, 0xbd, 0x61, 0x8c, 0xab, 0xcd, 0x5e, 0xac, 0x9d, 0xec, 0x14, 0xa1, 0x26, 0xd3, 0x3b,
|
||||
0xa8, 0x36, 0x98, 0x4a, 0xef, 0x86, 0xa1, 0x96, 0xed, 0x5d, 0x96, 0x31, 0xd2, 0x76, 0x23, 0xec,
|
||||
0xb0, 0x6d, 0x20, 0x65, 0xbb, 0x8c, 0x7e, 0xc3, 0x74, 0x73, 0x9d, 0x17, 0x48, 0x79, 0xbd, 0xe8,
|
||||
0x6c, 0x12, 0xfd, 0xe8, 0x42, 0xc6, 0xd7, 0xa3, 0x7e, 0x70, 0xab, 0x3e, 0x6a, 0x46, 0xc1, 0xeb,
|
||||
0x03, 0xe6, 0x93, 0xbb, 0x61, 0x48, 0xd9, 0xfe, 0xbb, 0xb5, 0xe8, 0x7b, 0x4a, 0xf6, 0x3c, 0x8f,
|
||||
0xcf, 0x32, 0x26, 0x97, 0xf8, 0x23, 0x26, 0x5e, 0xf1, 0xf2, 0x72, 0xb2, 0xca, 0x67, 0x44, 0x3a,
|
||||
0x83, 0xc3, 0x1d, 0xe9, 0x0c, 0xa9, 0xa4, 0x0a, 0xf3, 0x27, 0xd1, 0xbb, 0x3a, 0x28, 0x2e, 0xe2,
|
||||
0x7c, 0xce, 0x7e, 0x58, 0xf1, 0x7c, 0x54, 0xa4, 0xa3, 0x24, 0x29, 0x07, 0x43, 0xbc, 0xeb, 0x21,
|
||||
0x67, 0x4a, 0xb0, 0xdd, 0x9b, 0x77, 0xd2, 0x67, 0xd5, 0xca, 0x82, 0x17, 0x30, 0x7d, 0xd6, 0xcd,
|
||||
0x27, 0x78, 0x41, 0xa5, 0xcf, 0x3e, 0xd2, 0xb2, 0x7a, 0x58, 0xaf, 0x41, 0xb8, 0xd5, 0x43, 0x77,
|
||||
0xd1, 0xb9, 0x1d, 0x42, 0xec, 0x1a, 0xa0, 0x1b, 0x8a, 0xe7, 0xe7, 0xe9, 0xfc, 0xb4, 0x48, 0xea,
|
||||
0x31, 0xf4, 0x00, 0xaf, 0xb3, 0x83, 0x10, 0x6b, 0x00, 0x81, 0x2a, 0x6f, 0xff, 0x60, 0xb3, 0x4c,
|
||||
0x35, 0x2f, 0xed, 0x95, 0x7c, 0x71, 0xc0, 0xe6, 0xf1, 0x6c, 0xa5, 0x26, 0xd3, 0x0f, 0x43, 0xb3,
|
||||
0x18, 0xa4, 0x4d, 0x21, 0x3e, 0xba, 0xa6, 0x96, 0x2a, 0xcf, 0x7f, 0xae, 0x45, 0x77, 0xbd, 0x38,
|
||||
0x51, 0xc1, 0xd4, 0x94, 0x7e, 0x94, 0x27, 0x27, 0xac, 0x12, 0x71, 0x29, 0x06, 0xdf, 0x0f, 0xc4,
|
||||
0x00, 0xa1, 0x63, 0xca, 0xf6, 0x83, 0x6f, 0xa4, 0x6b, 0x7b, 0x7d, 0x52, 0xaf, 0x12, 0x6a, 0xfe,
|
||||
0xf1, 0x7b, 0x5d, 0x4a, 0xe0, 0xec, 0x73, 0x3b, 0x84, 0xd8, 0x5e, 0x97, 0x82, 0x71, 0x7e, 0x95,
|
||||
0x0a, 0xb6, 0xcf, 0x72, 0x56, 0xb6, 0x7b, 0xbd, 0x51, 0xf5, 0x11, 0xa2, 0xd7, 0x09, 0xd4, 0xce,
|
||||
0x74, 0x9e, 0x37, 0x93, 0x69, 0x6c, 0x06, 0x8c, 0xb4, 0x72, 0x8d, 0x47, 0xfd, 0x60, 0xa2, 0x86,
|
||||
0x62, 0xbf, 0x36, 0x12, 0xac, 0x61, 0x83, 0xf4, 0xaa, 0xa1, 0x41, 0xed, 0xc6, 0xdc, 0xf1, 0x76,
|
||||
0xc2, 0xae, 0xf8, 0x25, 0xdc, 0x98, 0xbb, 0x06, 0x1a, 0x80, 0xd8, 0x98, 0xa3, 0xa0, 0x4d, 0x3e,
|
||||
0x1c, 0x3f, 0x2f, 0x52, 0xf6, 0x0a, 0x24, 0x1f, 0xae, 0x72, 0x2d, 0x26, 0x92, 0x0f, 0x04, 0x53,
|
||||
0x1e, 0x8e, 0xa2, 0x5f, 0x92, 0xc2, 0x1f, 0xf2, 0x34, 0x1f, 0xdc, 0x44, 0x94, 0x6a, 0x81, 0xb1,
|
||||
0x7a, 0x8b, 0x06, 0x40, 0x89, 0xeb, 0xbf, 0xaa, 0x4c, 0xe0, 0x1e, 0xa1, 0x04, 0x92, 0x80, 0xf5,
|
||||
0x2e, 0xcc, 0x66, 0x7d, 0x52, 0x58, 0xcf, 0x96, 0x93, 0x8b, 0xb8, 0x4c, 0xf3, 0xf9, 0x00, 0xd3,
|
||||
0x75, 0xe4, 0x44, 0xd6, 0x87, 0x71, 0x20, 0x9c, 0x94, 0xe2, 0xa8, 0x28, 0xca, 0x7a, 0x12, 0xc6,
|
||||
0xc2, 0xc9, 0x47, 0x82, 0xe1, 0xd4, 0x42, 0x71, 0x6f, 0xbb, 0x6c, 0x96, 0xa5, 0x79, 0xd0, 0x9b,
|
||||
0x42, 0xfa, 0x78, 0xb3, 0x28, 0x08, 0xde, 0x03, 0x16, 0x5f, 0x31, 0x5d, 0x33, 0xac, 0x65, 0x5c,
|
||||
0x20, 0x18, 0xbc, 0x00, 0xb4, 0x5b, 0x6c, 0x29, 0x3e, 0x8c, 0x2f, 0x59, 0xdd, 0xc0, 0xac, 0x5e,
|
||||
0xc2, 0x07, 0x98, 0xbe, 0x47, 0x10, 0x5b, 0x6c, 0x9c, 0x54, 0xae, 0x96, 0xd1, 0x3b, 0x52, 0x7e,
|
||||
0x1c, 0x97, 0x22, 0x9d, 0xa5, 0x45, 0x9c, 0xeb, 0xad, 0x1b, 0x36, 0x8b, 0xb4, 0x28, 0xe3, 0x72,
|
||||
0xab, 0x27, 0xad, 0xdc, 0xfe, 0xdb, 0x5a, 0xf4, 0x3e, 0xf4, 0x7b, 0xcc, 0xca, 0x45, 0x2a, 0x4f,
|
||||
0x00, 0xaa, 0x66, 0xca, 0x1f, 0x7c, 0x12, 0x36, 0xda, 0x52, 0x30, 0xa5, 0xf9, 0xf4, 0xfa, 0x8a,
|
||||
0x36, 0xef, 0x9b, 0xa8, 0x5d, 0xd1, 0x17, 0x65, 0xd2, 0x3a, 0x21, 0x9b, 0xe8, 0xad, 0x8e, 0x14,
|
||||
0x12, 0x79, 0x5f, 0x0b, 0x02, 0x23, 0xfc, 0x34, 0xaf, 0xb4, 0x75, 0x6c, 0x84, 0x5b, 0x71, 0x70,
|
||||
0x84, 0x7b, 0x98, 0x1d, 0xe1, 0xc7, 0xcb, 0xb3, 0x2c, 0xad, 0x2e, 0xd2, 0x7c, 0xae, 0x92, 0x7c,
|
||||
0x5f, 0xd7, 0x8a, 0x61, 0x9e, 0xbf, 0xd1, 0xc9, 0x61, 0x4e, 0x54, 0xb0, 0x90, 0x4e, 0x40, 0x98,
|
||||
0x6c, 0x74, 0x72, 0x76, 0xef, 0x65, 0xa5, 0xf5, 0xa6, 0x1f, 0xec, 0xbd, 0x1c, 0xd5, 0x5a, 0x4a,
|
||||
0xec, 0xbd, 0xda, 0x94, 0xdd, 0x7b, 0xb9, 0x75, 0xa8, 0x78, 0x76, 0xc5, 0x4e, 0xcb, 0x14, 0xec,
|
||||
0xbd, 0xbc, 0xf2, 0x69, 0x86, 0xd8, 0x7b, 0x51, 0xac, 0x9d, 0xa8, 0x2c, 0xb1, 0xcf, 0xc4, 0x44,
|
||||
0xc4, 0x62, 0x59, 0x81, 0x89, 0xca, 0xb1, 0x61, 0x10, 0x62, 0xa2, 0x22, 0x50, 0xe5, 0xed, 0xf7,
|
||||
0xa3, 0xa8, 0x39, 0x2f, 0x91, 0x67, 0x5a, 0xfe, 0xda, 0xa3, 0x0e, 0x52, 0xbc, 0x03, 0xad, 0xf7,
|
||||
0x03, 0x84, 0x4d, 0xaf, 0x9a, 0xbf, 0xcb, 0xa3, 0xba, 0x01, 0xaa, 0x21, 0x45, 0x44, 0x7a, 0x05,
|
||||
0x10, 0x58, 0xd0, 0xc9, 0x05, 0x7f, 0x85, 0x17, 0xb4, 0x96, 0x84, 0x0b, 0xaa, 0x08, 0x7b, 0x78,
|
||||
0xae, 0x0a, 0x8a, 0x1d, 0x9e, 0xeb, 0x62, 0x84, 0x0e, 0xcf, 0x21, 0x63, 0x63, 0xc6, 0x35, 0xfc,
|
||||
0x8c, 0xf3, 0xcb, 0x45, 0x5c, 0x5e, 0x82, 0x98, 0xf1, 0x94, 0x35, 0x43, 0xc4, 0x0c, 0xc5, 0xda,
|
||||
0x98, 0x71, 0x1d, 0xd6, 0xc9, 0xf9, 0x69, 0x99, 0x81, 0x98, 0xf1, 0x6c, 0x28, 0x84, 0x88, 0x19,
|
||||
0x02, 0xb5, 0xb3, 0x93, 0xeb, 0x6d, 0xc2, 0xe0, 0x71, 0x8d, 0xa7, 0x3e, 0x61, 0xd4, 0x71, 0x0d,
|
||||
0x82, 0xc1, 0x10, 0xda, 0x2f, 0xe3, 0xe2, 0x02, 0x0f, 0x21, 0x29, 0x0a, 0x87, 0x90, 0x46, 0x60,
|
||||
0x7f, 0x4f, 0x58, 0x5c, 0xce, 0x2e, 0xf0, 0xfe, 0x6e, 0x64, 0xe1, 0xfe, 0x36, 0x0c, 0xec, 0xef,
|
||||
0x46, 0xf0, 0x32, 0x15, 0x17, 0x87, 0x4c, 0xc4, 0x78, 0x7f, 0xfb, 0x4c, 0xb8, 0xbf, 0x5b, 0xac,
|
||||
0xcd, 0xfe, 0x5d, 0x87, 0x93, 0xe5, 0x59, 0x35, 0x2b, 0xd3, 0x33, 0x36, 0x08, 0x58, 0x31, 0x10,
|
||||
0x91, 0xfd, 0x93, 0xb0, 0xf2, 0xf9, 0x93, 0xb5, 0xe8, 0xa6, 0xee, 0x76, 0x5e, 0x55, 0x6a, 0xed,
|
||||
0xf3, 0xdd, 0x7f, 0x84, 0xf7, 0x2f, 0x81, 0x13, 0x8f, 0x33, 0x7a, 0xa8, 0x39, 0xb9, 0x01, 0x5e,
|
||||
0xa4, 0xd3, 0xbc, 0x32, 0x85, 0xfa, 0xa4, 0x8f, 0x75, 0x47, 0x81, 0xc8, 0x0d, 0x7a, 0x29, 0xda,
|
||||
0xb4, 0x4c, 0xf5, 0x8f, 0x96, 0x8d, 0x93, 0x0a, 0xa4, 0x65, 0xba, 0xbd, 0x1d, 0x82, 0x48, 0xcb,
|
||||
0x70, 0x12, 0x86, 0xc2, 0x7e, 0xc9, 0x97, 0x45, 0xd5, 0x11, 0x0a, 0x00, 0x0a, 0x87, 0x42, 0x1b,
|
||||
0x56, 0x3e, 0x5f, 0x47, 0xbf, 0xe9, 0x86, 0x9f, 0xdb, 0xd8, 0x5b, 0x74, 0x4c, 0x61, 0x4d, 0x3c,
|
||||
0xec, 0x8b, 0xdb, 0x8c, 0x42, 0x7b, 0x16, 0xbb, 0x4c, 0xc4, 0x69, 0x56, 0x0d, 0xd6, 0x71, 0x1b,
|
||||
0x5a, 0x4e, 0x64, 0x14, 0x18, 0x07, 0xe7, 0xb7, 0xdd, 0x65, 0x91, 0xa5, 0xb3, 0xf6, 0xc3, 0x24,
|
||||
0xa5, 0x6b, 0xc4, 0xe1, 0xf9, 0xcd, 0xc5, 0xe0, 0x7c, 0x5d, 0xa7, 0x7e, 0xf2, 0x3f, 0xd3, 0x55,
|
||||
0xc1, 0xf0, 0xf9, 0xda, 0x43, 0xc2, 0xf3, 0x35, 0x44, 0x61, 0x7d, 0x26, 0x4c, 0x1c, 0xc4, 0x2b,
|
||||
0xbe, 0x24, 0xe6, 0x6b, 0x23, 0x0e, 0xd7, 0xc7, 0xc5, 0xec, 0xde, 0xc0, 0x78, 0x18, 0xe7, 0x82,
|
||||
0x95, 0x79, 0x9c, 0xed, 0x65, 0xf1, 0xbc, 0x1a, 0x10, 0x73, 0x8c, 0x4f, 0x11, 0x7b, 0x03, 0x9a,
|
||||
0x46, 0x9a, 0x71, 0x5c, 0xed, 0xc5, 0x57, 0xbc, 0x4c, 0x05, 0xdd, 0x8c, 0x16, 0xe9, 0x6c, 0x46,
|
||||
0x0f, 0x45, 0xbd, 0x8d, 0xca, 0xd9, 0x45, 0x7a, 0xc5, 0x92, 0x80, 0x37, 0x8d, 0xf4, 0xf0, 0xe6,
|
||||
0xa0, 0x48, 0xa7, 0x4d, 0xf8, 0xb2, 0x9c, 0x31, 0xb2, 0xd3, 0x1a, 0x71, 0x67, 0xa7, 0x19, 0x4c,
|
||||
0x79, 0xf8, 0xab, 0xb5, 0xe8, 0xb7, 0x1a, 0xa9, 0xfb, 0x84, 0x67, 0x37, 0xae, 0x2e, 0xce, 0x78,
|
||||
0x5c, 0x26, 0x83, 0x0f, 0x30, 0x3b, 0x28, 0x6a, 0x5c, 0x3f, 0xb9, 0x8e, 0x0a, 0x6c, 0xd6, 0x3a,
|
||||
0xef, 0xb6, 0x23, 0x0e, 0x6d, 0x56, 0x0f, 0x09, 0x37, 0x2b, 0x44, 0xe1, 0x04, 0x22, 0xe5, 0xcd,
|
||||
0x01, 0xe0, 0x3a, 0xa9, 0xef, 0x9f, 0x02, 0x6e, 0x74, 0x72, 0x70, 0x7e, 0xac, 0x85, 0x7e, 0xb4,
|
||||
0x6c, 0x51, 0x36, 0xf0, 0x88, 0x19, 0xf6, 0xc5, 0x49, 0xcf, 0x66, 0x54, 0x84, 0x3d, 0xb7, 0x46,
|
||||
0xc6, 0xb0, 0x2f, 0x4e, 0x78, 0x76, 0xa6, 0xb5, 0x90, 0x67, 0x64, 0x6a, 0x1b, 0xf6, 0xc5, 0x61,
|
||||
0xf6, 0xa5, 0x18, 0xbd, 0x2e, 0x3c, 0x0c, 0xd8, 0x81, 0x6b, 0xc3, 0x66, 0x2f, 0x56, 0x39, 0xfc,
|
||||
0x9b, 0xb5, 0xe8, 0xbb, 0xd6, 0xe3, 0x21, 0x4f, 0xd2, 0xf3, 0x55, 0x03, 0xbd, 0x88, 0xb3, 0x25,
|
||||
0xab, 0x06, 0x4f, 0x28, 0x6b, 0x6d, 0xd6, 0x94, 0xe0, 0xe9, 0xb5, 0x74, 0xe0, 0xd8, 0x19, 0x15,
|
||||
0x45, 0xb6, 0x9a, 0xb2, 0x45, 0x91, 0x91, 0x63, 0xc7, 0x43, 0xc2, 0x63, 0x07, 0xa2, 0x30, 0x2b,
|
||||
0x9f, 0xf2, 0x3a, 0xe7, 0x47, 0xb3, 0x72, 0x29, 0x0a, 0x67, 0xe5, 0x1a, 0x81, 0xb9, 0xd2, 0x94,
|
||||
0xef, 0xf0, 0x2c, 0x63, 0x33, 0xd1, 0xbe, 0x25, 0x62, 0x34, 0x2d, 0x11, 0xce, 0x95, 0x00, 0x69,
|
||||
0x4f, 0xe5, 0xf4, 0x1e, 0x32, 0x2e, 0xd9, 0xb3, 0xd5, 0x41, 0x9a, 0x5f, 0x0e, 0xf0, 0xb4, 0xc0,
|
||||
0x02, 0xc4, 0xa9, 0x1c, 0x0a, 0xc2, 0xbd, 0xea, 0x69, 0x9e, 0x70, 0x7c, 0xaf, 0x5a, 0x4b, 0xc2,
|
||||
0x7b, 0x55, 0x45, 0x40, 0x93, 0x27, 0x8c, 0x32, 0x59, 0x4b, 0xc2, 0x26, 0x15, 0x81, 0x4d, 0x85,
|
||||
0xea, 0x49, 0x11, 0x39, 0x15, 0x82, 0x67, 0x43, 0x1b, 0x9d, 0x1c, 0xdc, 0x73, 0x29, 0x07, 0x68,
|
||||
0x44, 0x00, 0xe3, 0x77, 0x82, 0x0c, 0x0c, 0x7d, 0xbd, 0x1b, 0xde, 0x63, 0x62, 0x76, 0x81, 0x87,
|
||||
0xbe, 0x87, 0x84, 0x43, 0x1f, 0xa2, 0xb0, 0xad, 0xa6, 0xdc, 0xec, 0xe6, 0xd7, 0xf1, 0xc0, 0x6b,
|
||||
0xed, 0xe4, 0x37, 0x3a, 0x39, 0xd8, 0x56, 0xe3, 0x05, 0xdd, 0x56, 0x8d, 0x2c, 0xdc, 0x56, 0x86,
|
||||
0x81, 0xa5, 0x6f, 0x04, 0xf2, 0x90, 0x6c, 0x9d, 0x56, 0xf4, 0x8e, 0xc9, 0x36, 0x3a, 0x39, 0xe5,
|
||||
0xe4, 0x5f, 0xcc, 0xfe, 0xb0, 0x91, 0x1e, 0xf1, 0x7a, 0xf0, 0xbd, 0x88, 0xb3, 0x34, 0x89, 0x05,
|
||||
0x9b, 0xf2, 0x4b, 0x96, 0xe3, 0x5b, 0x31, 0x55, 0xda, 0x86, 0x1f, 0x7a, 0x0a, 0xe1, 0xad, 0x58,
|
||||
0x58, 0x11, 0xc6, 0x49, 0x43, 0x9f, 0x56, 0x6c, 0x27, 0xae, 0x88, 0x29, 0xd2, 0x43, 0xc2, 0x71,
|
||||
0x02, 0x51, 0x98, 0x08, 0x37, 0xf2, 0xe7, 0xaf, 0x0b, 0x56, 0xa6, 0x2c, 0x9f, 0x31, 0x3c, 0x11,
|
||||
0x86, 0x54, 0x38, 0x11, 0x46, 0x68, 0xb8, 0x09, 0xdc, 0x8d, 0x05, 0x7b, 0xb6, 0x9a, 0xa6, 0x0b,
|
||||
0x56, 0x89, 0x78, 0x51, 0xe0, 0x9b, 0x40, 0x00, 0x85, 0x37, 0x81, 0x6d, 0xb8, 0x75, 0xe6, 0x64,
|
||||
0x66, 0xda, 0xf6, 0xad, 0x35, 0x48, 0x04, 0x6e, 0xad, 0x11, 0x28, 0x6c, 0x58, 0x0b, 0xa0, 0x4f,
|
||||
0x1f, 0x5a, 0x56, 0x82, 0x4f, 0x1f, 0x68, 0xba, 0x75, 0x92, 0x67, 0x98, 0x49, 0x3d, 0x34, 0x3b,
|
||||
0x8a, 0x3e, 0x71, 0x87, 0xe8, 0x66, 0x2f, 0x16, 0x3f, 0x3a, 0x3c, 0x61, 0x59, 0x2c, 0xd7, 0xc3,
|
||||
0xc0, 0xf9, 0x9c, 0x66, 0xfa, 0x1c, 0x1d, 0x3a, 0xac, 0x72, 0xf8, 0x17, 0x6b, 0xd1, 0x7b, 0x98,
|
||||
0xc7, 0x2f, 0x0a, 0xe9, 0xf7, 0x71, 0xb7, 0xad, 0x86, 0x24, 0xae, 0xe5, 0x85, 0x35, 0xec, 0xcd,
|
||||
0x12, 0x2d, 0xb2, 0xb7, 0xf6, 0x54, 0x01, 0xfc, 0x6c, 0xd0, 0x94, 0x1f, 0x72, 0xc4, 0xcd, 0x92,
|
||||
0x10, 0x6f, 0x37, 0x5a, 0x7e, 0xb9, 0x2a, 0xb0, 0xd1, 0x32, 0x36, 0x94, 0x98, 0xd8, 0x68, 0x21,
|
||||
0x98, 0x1d, 0x9d, 0x6e, 0xf5, 0x5e, 0xa6, 0xe2, 0x42, 0x26, 0x72, 0x60, 0x74, 0x7a, 0x65, 0x35,
|
||||
0x10, 0x31, 0x3a, 0x49, 0x18, 0xa6, 0x3a, 0x1a, 0xac, 0xc7, 0x26, 0x36, 0x97, 0x1b, 0x43, 0xee,
|
||||
0xc8, 0xbc, 0xdf, 0x0d, 0xc2, 0x78, 0xd5, 0x62, 0xb5, 0xa7, 0x7a, 0x18, 0xb2, 0x00, 0xf6, 0x55,
|
||||
0x9b, 0xbd, 0x58, 0xe5, 0xf0, 0xcf, 0xa2, 0xef, 0xb4, 0x2a, 0xb6, 0xc7, 0x62, 0xb1, 0x2c, 0x59,
|
||||
0x32, 0xd8, 0xee, 0x28, 0xb7, 0x06, 0x8d, 0xeb, 0xc7, 0xfd, 0x15, 0x5a, 0xc9, 0xbf, 0xe6, 0x9a,
|
||||
0xb0, 0x32, 0x65, 0x78, 0x12, 0x32, 0xe9, 0xb3, 0xc1, 0xe4, 0x9f, 0xd6, 0x69, 0xed, 0xdf, 0xdd,
|
||||
0xe8, 0x1a, 0x5d, 0xc5, 0x69, 0x26, 0x9f, 0x02, 0x7f, 0x10, 0x32, 0xea, 0xa1, 0xc1, 0xfd, 0x3b,
|
||||
0xa9, 0xd2, 0x9a, 0x99, 0xe5, 0x18, 0x77, 0xf6, 0x7d, 0x8f, 0xe8, 0x99, 0x00, 0xd9, 0xf6, 0x6d,
|
||||
0xf5, 0xa4, 0x95, 0x5b, 0xa1, 0x97, 0xbc, 0xfa, 0xcf, 0x6e, 0x90, 0x63, 0x5e, 0x95, 0x2a, 0x12,
|
||||
0xe9, 0x5b, 0x3d, 0x69, 0xe5, 0xf5, 0x4f, 0xa3, 0x77, 0xdb, 0x5e, 0xd5, 0x42, 0xb4, 0xdd, 0x69,
|
||||
0x0a, 0xac, 0x45, 0x8f, 0xfb, 0x2b, 0x28, 0xf7, 0xff, 0x6e, 0x0e, 0xbc, 0x1b, 0xff, 0x33, 0xbe,
|
||||
0x58, 0xb0, 0x3c, 0x61, 0x89, 0xd6, 0xa8, 0xea, 0x8d, 0xd9, 0xa7, 0xb4, 0x5d, 0xa3, 0x30, 0x74,
|
||||
0x35, 0x4c, 0x89, 0x7e, 0xfb, 0x1b, 0x68, 0xaa, 0xa2, 0xfd, 0xf7, 0x5a, 0xf4, 0x00, 0x2d, 0x9a,
|
||||
0x0e, 0x5c, 0xaf, 0x88, 0xbf, 0xd7, 0xc7, 0x11, 0xa6, 0x69, 0x8a, 0x3a, 0xfa, 0x39, 0x2c, 0xa8,
|
||||
0x22, 0xff, 0xc7, 0x5a, 0x74, 0xdb, 0x2a, 0xd6, 0xe1, 0xbd, 0xc3, 0xf3, 0xf3, 0x2c, 0x9d, 0x09,
|
||||
0xf9, 0xa8, 0x57, 0xa9, 0xd0, 0xcd, 0x49, 0x69, 0x74, 0x37, 0x67, 0x40, 0xd3, 0xee, 0x8a, 0x3f,
|
||||
0x4b, 0x2b, 0xc1, 0xcb, 0xd5, 0xe4, 0x82, 0xbf, 0xd2, 0xef, 0x3d, 0xf9, 0xf3, 0xb2, 0x02, 0x86,
|
||||
0x0e, 0x41, 0xec, 0x8a, 0x71, 0xb2, 0xe5, 0xca, 0xbe, 0x1f, 0x55, 0x11, 0xae, 0x1c, 0xa2, 0xc3,
|
||||
0x95, 0x4f, 0xda, 0x55, 0x49, 0xd7, 0xca, 0xbe, 0xcc, 0xb5, 0x81, 0x17, 0xb5, 0xfd, 0x42, 0xd7,
|
||||
0xfd, 0x6e, 0xd0, 0xe6, 0xa6, 0x4a, 0xbc, 0x9b, 0x9e, 0x9f, 0x9b, 0x3a, 0xe1, 0x25, 0x75, 0x11,
|
||||
0x22, 0x37, 0x25, 0x50, 0xbb, 0xbd, 0xda, 0x4b, 0x33, 0x26, 0x1f, 0x0a, 0x7d, 0x71, 0x7e, 0x9e,
|
||||
0xf1, 0x38, 0x01, 0xdb, 0xab, 0x5a, 0x3c, 0x74, 0xe5, 0xc4, 0xf6, 0x0a, 0xe3, 0xec, 0x75, 0x93,
|
||||
0x5a, 0x5a, 0x47, 0x77, 0x3e, 0x4b, 0x33, 0x78, 0x6d, 0x5a, 0x6a, 0x1a, 0x21, 0x71, 0xdd, 0xa4,
|
||||
0x05, 0xd9, 0x14, 0xa8, 0x16, 0xd5, 0x51, 0xa9, 0xcb, 0x7f, 0xaf, 0xad, 0xe8, 0x88, 0x89, 0x14,
|
||||
0x08, 0xc1, 0xec, 0xf1, 0x45, 0x2d, 0x3c, 0x2d, 0xa4, 0xf1, 0x5b, 0x6d, 0xad, 0x46, 0x42, 0x1c,
|
||||
0x5f, 0xf8, 0x84, 0xdd, 0x2d, 0xd7, 0x7f, 0xdf, 0xe5, 0xaf, 0x72, 0x69, 0xf4, 0x76, 0x5b, 0x45,
|
||||
0xcb, 0x88, 0xdd, 0x32, 0x64, 0x94, 0xe1, 0xcf, 0xa3, 0x5f, 0x94, 0x86, 0x4b, 0x5e, 0x0c, 0x6e,
|
||||
0x20, 0x0a, 0xa5, 0x73, 0xc9, 0xf8, 0x26, 0x29, 0xb7, 0xb7, 0x53, 0x4c, 0x6c, 0x9c, 0x56, 0xf1,
|
||||
0x1c, 0xbe, 0x19, 0x60, 0x7b, 0x5c, 0x4a, 0x89, 0xdb, 0x29, 0x6d, 0xca, 0x8f, 0x8a, 0x23, 0x9e,
|
||||
0x28, 0xeb, 0x48, 0x0d, 0x8d, 0x30, 0x14, 0x15, 0x2e, 0x64, 0xd3, 0xd6, 0xa3, 0xf8, 0x2a, 0x9d,
|
||||
0x9b, 0xd4, 0xa2, 0x99, 0xc0, 0x2a, 0x90, 0xb6, 0x5a, 0x66, 0xe8, 0x40, 0x44, 0xda, 0x4a, 0xc2,
|
||||
0xca, 0xe7, 0x3f, 0xaf, 0x45, 0xb7, 0x2c, 0xb3, 0xaf, 0x0f, 0x7c, 0xc7, 0xf9, 0x39, 0xaf, 0x93,
|
||||
0xdc, 0x83, 0x34, 0xbf, 0xac, 0x06, 0x1f, 0x53, 0x26, 0x71, 0xde, 0x14, 0xe5, 0x93, 0x6b, 0xeb,
|
||||
0xd9, 0xfd, 0x89, 0x3e, 0x0d, 0xb5, 0x57, 0x22, 0x1a, 0x0d, 0xb0, 0x3f, 0x31, 0x87, 0xa6, 0x90,
|
||||
0x23, 0xf6, 0x27, 0x21, 0xde, 0x76, 0xb1, 0x71, 0x9e, 0xf1, 0x1c, 0x76, 0xb1, 0xb5, 0x50, 0x0b,
|
||||
0x89, 0x2e, 0x6e, 0x41, 0x76, 0x3e, 0xd6, 0xa2, 0xe6, 0x7c, 0x6d, 0x94, 0x65, 0x60, 0x3e, 0x36,
|
||||
0xaa, 0x06, 0x20, 0xe6, 0x63, 0x14, 0x54, 0x7e, 0x4e, 0xa2, 0x6f, 0xd5, 0x4d, 0x7a, 0x5c, 0xb2,
|
||||
0xab, 0x94, 0xc1, 0xdb, 0x3b, 0x8e, 0x84, 0x18, 0xff, 0x3e, 0x61, 0x47, 0xd6, 0x69, 0x5e, 0x15,
|
||||
0x59, 0x5c, 0x5d, 0xa8, 0xfb, 0x1c, 0x7e, 0x9d, 0xb5, 0x10, 0xde, 0xe8, 0xb8, 0xd7, 0x41, 0xd9,
|
||||
0x49, 0x5d, 0xcb, 0xcc, 0x14, 0xb3, 0x8e, 0xab, 0xb6, 0xa6, 0x99, 0x8d, 0x4e, 0xce, 0x3e, 0x34,
|
||||
0xd9, 0x8f, 0xb3, 0x8c, 0x95, 0x2b, 0x2d, 0x3b, 0x8c, 0xf3, 0xf4, 0x9c, 0x55, 0x02, 0x3c, 0x34,
|
||||
0x51, 0xd4, 0x10, 0x62, 0xc4, 0x43, 0x93, 0x00, 0x6e, 0xf7, 0x6d, 0xc0, 0xf3, 0x38, 0x4f, 0xd8,
|
||||
0x6b, 0xb0, 0x6f, 0x83, 0x76, 0x24, 0x43, 0xec, 0xdb, 0x28, 0xd6, 0x3e, 0x3c, 0x78, 0x96, 0xf1,
|
||||
0xd9, 0xa5, 0x5a, 0x02, 0xfc, 0x0e, 0x96, 0x12, 0xb8, 0x06, 0xdc, 0x0e, 0x21, 0x76, 0x11, 0x90,
|
||||
0x82, 0x13, 0x56, 0x64, 0xf1, 0x0c, 0x5e, 0xe1, 0x6a, 0x74, 0x94, 0x8c, 0x58, 0x04, 0x20, 0x03,
|
||||
0x8a, 0xab, 0xae, 0x86, 0x61, 0xc5, 0x05, 0x37, 0xc3, 0x6e, 0x87, 0x10, 0xbb, 0x0c, 0x4a, 0xc1,
|
||||
0xa4, 0xc8, 0x52, 0x01, 0x86, 0x41, 0xa3, 0x21, 0x25, 0xc4, 0x30, 0xf0, 0x09, 0x60, 0xf2, 0x90,
|
||||
0x95, 0x73, 0x86, 0x9a, 0x94, 0x92, 0xa0, 0x49, 0x4d, 0xd8, 0xfb, 0xea, 0x4d, 0xdd, 0x79, 0xb1,
|
||||
0x02, 0xf7, 0xd5, 0x55, 0xb5, 0x78, 0xb1, 0x22, 0xee, 0xab, 0x7b, 0x00, 0x28, 0xe2, 0x71, 0x5c,
|
||||
0x09, 0xbc, 0x88, 0x52, 0x12, 0x2c, 0xa2, 0x26, 0xec, 0x1a, 0xdd, 0x14, 0x71, 0x29, 0xc0, 0x1a,
|
||||
0xad, 0x0a, 0xe0, 0x5c, 0x62, 0xb8, 0x49, 0xca, 0xed, 0x4c, 0xd2, 0xf4, 0x0a, 0x13, 0x7b, 0x29,
|
||||
0xcb, 0x92, 0x0a, 0xcc, 0x24, 0xaa, 0xdd, 0xb5, 0x94, 0x98, 0x49, 0xda, 0x14, 0x08, 0x25, 0xf5,
|
||||
0x04, 0x04, 0xab, 0x1d, 0x78, 0x00, 0x72, 0x3b, 0x84, 0xd8, 0xf9, 0x49, 0x17, 0x7a, 0x27, 0x2e,
|
||||
0xcb, 0xb4, 0x5e, 0xfc, 0xd7, 0xf1, 0x02, 0x69, 0x39, 0x31, 0x3f, 0x61, 0x1c, 0x18, 0x5e, 0x7a,
|
||||
0xe2, 0xc6, 0x0a, 0x06, 0xa7, 0xee, 0x3b, 0x41, 0xc6, 0x66, 0x9c, 0x52, 0xe2, 0x3c, 0x85, 0xc7,
|
||||
0x5a, 0x13, 0x79, 0x08, 0xbf, 0xde, 0x85, 0x39, 0xaf, 0xce, 0x19, 0x17, 0x87, 0xfc, 0x8a, 0x4d,
|
||||
0xf9, 0xf3, 0xd7, 0x69, 0x55, 0x6f, 0xb7, 0xd4, 0xca, 0xfd, 0x94, 0xb0, 0x84, 0xc1, 0xc4, 0xab,
|
||||
0x73, 0x9d, 0x4a, 0x36, 0x81, 0x00, 0x65, 0x39, 0x62, 0xaf, 0xd0, 0x04, 0x02, 0x5a, 0x34, 0x1c,
|
||||
0x91, 0x40, 0x84, 0x78, 0x7b, 0x62, 0x66, 0x9c, 0xab, 0xef, 0x25, 0x4c, 0xb9, 0xce, 0xe5, 0x28,
|
||||
0x6b, 0x10, 0x24, 0x0e, 0x2d, 0x82, 0x0a, 0x76, 0x7f, 0x69, 0xfc, 0xdb, 0x21, 0x76, 0x9f, 0xb0,
|
||||
0xd3, 0x1e, 0x66, 0x0f, 0x7a, 0x90, 0x88, 0x2b, 0x7b, 0x95, 0x84, 0x72, 0xd5, 0xbe, 0x49, 0xf2,
|
||||
0xa0, 0x07, 0xe9, 0x9c, 0xbe, 0xb9, 0xd5, 0x7a, 0x16, 0xcf, 0x2e, 0xe7, 0x25, 0x5f, 0xe6, 0xc9,
|
||||
0x0e, 0xcf, 0x78, 0x09, 0x4e, 0xdf, 0xbc, 0x52, 0x03, 0x94, 0x38, 0x7d, 0xeb, 0x50, 0xb1, 0x19,
|
||||
0x9c, 0x5b, 0x8a, 0x51, 0x96, 0xce, 0xe1, 0x8e, 0xda, 0x33, 0x24, 0x01, 0x22, 0x83, 0x43, 0x41,
|
||||
0x24, 0x88, 0x9a, 0x1d, 0xb7, 0x48, 0x67, 0x71, 0xd6, 0xf8, 0xdb, 0xa6, 0xcd, 0x78, 0x60, 0x67,
|
||||
0x10, 0x21, 0x0a, 0x48, 0x3d, 0xa7, 0xcb, 0x32, 0x1f, 0xe7, 0x82, 0x93, 0xf5, 0xd4, 0x40, 0x67,
|
||||
0x3d, 0x1d, 0x10, 0x4c, 0xab, 0x53, 0xf6, 0xba, 0x2e, 0x4d, 0xfd, 0x0f, 0x36, 0xad, 0xd6, 0x7f,
|
||||
0x1f, 0x2a, 0x79, 0x68, 0x5a, 0x05, 0x1c, 0xa8, 0x8c, 0x72, 0xd2, 0x04, 0x4c, 0x40, 0xdb, 0x0f,
|
||||
0x93, 0xfb, 0xdd, 0x20, 0xee, 0x67, 0x22, 0x56, 0x19, 0x0b, 0xf9, 0x91, 0x40, 0x1f, 0x3f, 0x1a,
|
||||
0xb4, 0xc7, 0x2d, 0x5e, 0x7d, 0x2e, 0xd8, 0xec, 0xb2, 0x75, 0x33, 0xce, 0x2f, 0x68, 0x83, 0x10,
|
||||
0xc7, 0x2d, 0x04, 0x8a, 0x77, 0xd1, 0x78, 0xc6, 0xf3, 0x50, 0x17, 0xd5, 0xf2, 0x3e, 0x5d, 0xa4,
|
||||
0x38, 0xbb, 0xf9, 0x35, 0x52, 0x15, 0x99, 0x4d, 0x37, 0x6d, 0x12, 0x16, 0x5c, 0x88, 0xd8, 0xfc,
|
||||
0x92, 0xb0, 0xcd, 0xc9, 0xa1, 0xcf, 0xc3, 0xf6, 0x6b, 0x03, 0x2d, 0x2b, 0x87, 0xf4, 0x6b, 0x03,
|
||||
0x14, 0x4b, 0x57, 0xb2, 0x89, 0x91, 0x0e, 0x2b, 0x7e, 0x9c, 0x3c, 0xea, 0x07, 0xdb, 0x2d, 0x8f,
|
||||
0xe7, 0x73, 0x27, 0x63, 0x71, 0xd9, 0x78, 0xdd, 0x0a, 0x18, 0xb2, 0x18, 0xb1, 0xe5, 0x09, 0xe0,
|
||||
0x60, 0x0a, 0xf3, 0x3c, 0xef, 0xf0, 0x5c, 0xb0, 0x5c, 0x60, 0x53, 0x98, 0x6f, 0x4c, 0x81, 0xa1,
|
||||
0x29, 0x8c, 0x52, 0x00, 0x71, 0x2b, 0xcf, 0x83, 0x98, 0x38, 0x8a, 0x17, 0x68, 0xc6, 0xd6, 0x9c,
|
||||
0xf5, 0x34, 0xf2, 0x50, 0xdc, 0x02, 0xce, 0x79, 0x9c, 0xeb, 0x7a, 0x99, 0xc6, 0xe5, 0xdc, 0x9c,
|
||||
0x6e, 0x24, 0x83, 0xc7, 0xb4, 0x1d, 0x9f, 0x24, 0x1e, 0xe7, 0x86, 0x35, 0xc0, 0xb4, 0x33, 0x5e,
|
||||
0xc4, 0x73, 0x53, 0x53, 0xa4, 0x06, 0x52, 0xde, 0xaa, 0xea, 0xfd, 0x6e, 0x10, 0xf8, 0x79, 0x91,
|
||||
0x26, 0x8c, 0x07, 0xfc, 0x48, 0x79, 0x1f, 0x3f, 0x10, 0x04, 0xd9, 0x5b, 0x5d, 0xef, 0x66, 0x47,
|
||||
0x37, 0xca, 0x13, 0xb5, 0x8f, 0x1d, 0x12, 0xcd, 0x03, 0xb8, 0x50, 0xf6, 0x46, 0xf0, 0x60, 0x8c,
|
||||
0xea, 0x03, 0xda, 0xd0, 0x18, 0x35, 0xe7, 0xaf, 0x7d, 0xc6, 0x28, 0x06, 0x2b, 0x9f, 0x3f, 0x56,
|
||||
0x63, 0x74, 0x37, 0x16, 0x71, 0x9d, 0xb7, 0xbf, 0x48, 0xd9, 0x2b, 0xb5, 0x11, 0x46, 0xea, 0xab,
|
||||
0xa9, 0xa1, 0x7c, 0xeb, 0x19, 0xec, 0x8a, 0xb7, 0x7b, 0xf3, 0x01, 0xdf, 0x6a, 0x87, 0xd0, 0xe9,
|
||||
0x1b, 0x6c, 0x15, 0xb6, 0x7b, 0xf3, 0x01, 0xdf, 0xea, 0xe3, 0x0d, 0x9d, 0xbe, 0xc1, 0x17, 0x1c,
|
||||
0xb6, 0x7b, 0xf3, 0xca, 0xf7, 0x5f, 0xea, 0x81, 0xeb, 0x3a, 0xaf, 0xf3, 0xb0, 0x99, 0x48, 0xaf,
|
||||
0x18, 0x96, 0x4e, 0xfa, 0xf6, 0x0c, 0x1a, 0x4a, 0x27, 0x69, 0x15, 0xe7, 0xf3, 0x69, 0x58, 0x29,
|
||||
0x8e, 0x79, 0x95, 0xca, 0xeb, 0x18, 0x4f, 0x7b, 0x18, 0xd5, 0x70, 0x68, 0xd3, 0x14, 0x52, 0xb2,
|
||||
0x0f, 0x96, 0x3d, 0xd4, 0x5e, 0x84, 0x7f, 0x14, 0xb0, 0xd7, 0xbe, 0x0f, 0xbf, 0xd5, 0x93, 0xb6,
|
||||
0x8f, 0x78, 0x3d, 0x46, 0x3f, 0x9c, 0x9b, 0x30, 0x74, 0x95, 0x30, 0xa6, 0xcc, 0x43, 0x5b, 0xf7,
|
||||
0x29, 0xe5, 0xe3, 0xfe, 0x0a, 0x1d, 0xee, 0x47, 0x49, 0xd2, 0xcf, 0xbd, 0xfb, 0x74, 0xfb, 0x71,
|
||||
0x7f, 0x05, 0xe5, 0xfe, 0xaf, 0xf5, 0xb6, 0x06, 0xfa, 0x57, 0x63, 0xf0, 0x49, 0x1f, 0x8b, 0x60,
|
||||
0x1c, 0x3e, 0xbd, 0x96, 0x8e, 0x2a, 0xc8, 0xdf, 0xeb, 0xfd, 0xbb, 0x46, 0xe5, 0xdb, 0x48, 0xf2,
|
||||
0x2d, 0x66, 0x35, 0x24, 0x43, 0x51, 0x65, 0x61, 0x38, 0x30, 0x3f, 0xba, 0xa6, 0x96, 0xf3, 0x2d,
|
||||
0x3f, 0x0f, 0x56, 0x6f, 0xcd, 0x3a, 0xe5, 0x09, 0x59, 0x76, 0x68, 0x58, 0xa0, 0x8f, 0xaf, 0xab,
|
||||
0x46, 0x0d, 0x55, 0x07, 0x96, 0x5f, 0xb3, 0x79, 0xda, 0xd3, 0xb0, 0xf7, 0x7d, 0x9b, 0x0f, 0xaf,
|
||||
0xa7, 0xa4, 0xca, 0xf2, 0x5f, 0x6b, 0xd1, 0x3d, 0x8f, 0xb5, 0x8f, 0x33, 0xc0, 0xa1, 0xcb, 0x0f,
|
||||
0x02, 0xf6, 0x29, 0x25, 0x53, 0xb8, 0xdf, 0xf9, 0x66, 0xca, 0xf6, 0xc3, 0x77, 0x9e, 0xca, 0x5e,
|
||||
0x9a, 0x09, 0x56, 0xb6, 0x3f, 0x7c, 0xe7, 0xdb, 0x6d, 0xa8, 0x21, 0xfd, 0xe1, 0xbb, 0x00, 0xee,
|
||||
0x7c, 0xf8, 0x0e, 0xf1, 0x8c, 0x7e, 0xf8, 0x0e, 0xb5, 0x16, 0xfc, 0xf0, 0x5d, 0x58, 0x83, 0x5a,
|
||||
0x5d, 0x74, 0x11, 0x9a, 0x63, 0xf3, 0x5e, 0x16, 0xfd, 0x53, 0xf4, 0x27, 0xd7, 0x51, 0x21, 0xd6,
|
||||
0xd7, 0x86, 0x93, 0x17, 0x2a, 0x7b, 0xb4, 0xa9, 0x77, 0xa9, 0x72, 0xbb, 0x37, 0xaf, 0x7c, 0xff,
|
||||
0x48, 0x6d, 0xae, 0xcc, 0x6a, 0xc2, 0x4b, 0xf9, 0xd1, 0xc3, 0xcd, 0xd0, 0xea, 0x50, 0x5b, 0x70,
|
||||
0x7b, 0xfe, 0x51, 0x3f, 0x98, 0xa8, 0x6e, 0x4d, 0xa8, 0x4e, 0x1f, 0x76, 0x19, 0x02, 0x5d, 0xbe,
|
||||
0xdd, 0x9b, 0x27, 0x96, 0x91, 0xc6, 0x77, 0xd3, 0xdb, 0x3d, 0x8c, 0xf9, 0x7d, 0xfd, 0xb8, 0xbf,
|
||||
0x82, 0x72, 0x7f, 0xa5, 0xb2, 0x56, 0xd7, 0xbd, 0xec, 0xe7, 0xad, 0x2e, 0x53, 0x13, 0xaf, 0x9b,
|
||||
0x87, 0x7d, 0xf1, 0x50, 0xfe, 0xe2, 0x2e, 0xa1, 0x5d, 0xf9, 0x0b, 0xba, 0x8c, 0x7e, 0x78, 0x3d,
|
||||
0x25, 0x55, 0x96, 0x7f, 0x5a, 0x8b, 0x6e, 0x92, 0x65, 0x51, 0x71, 0xf0, 0x71, 0x5f, 0xcb, 0x20,
|
||||
0x1e, 0x3e, 0xb9, 0xb6, 0x9e, 0x2a, 0xd4, 0xbf, 0xae, 0x45, 0xb7, 0x02, 0x85, 0x6a, 0x02, 0xe4,
|
||||
0x1a, 0xd6, 0xfd, 0x40, 0xf9, 0xf4, 0xfa, 0x8a, 0xd4, 0x72, 0xef, 0xe2, 0x93, 0xf6, 0x47, 0xcc,
|
||||
0x02, 0xb6, 0x27, 0xf4, 0x47, 0xcc, 0xba, 0xb5, 0xe0, 0x19, 0x53, 0x7c, 0xa6, 0xf7, 0x7c, 0xe8,
|
||||
0x19, 0x93, 0xbc, 0x0a, 0x19, 0xfc, 0x3c, 0x0a, 0xc6, 0x61, 0x4e, 0x9e, 0xbf, 0x2e, 0xe2, 0x3c,
|
||||
0xa1, 0x9d, 0x34, 0xf2, 0x6e, 0x27, 0x86, 0x83, 0x67, 0x73, 0xb5, 0xf4, 0x84, 0xeb, 0x7d, 0xdc,
|
||||
0x03, 0x4a, 0xdf, 0x20, 0xc1, 0xb3, 0xb9, 0x16, 0x4a, 0x78, 0x53, 0x59, 0x63, 0xc8, 0x1b, 0x48,
|
||||
0x16, 0x1f, 0xf6, 0x41, 0xc1, 0x0e, 0xc1, 0x78, 0x33, 0x47, 0xfe, 0x8f, 0x42, 0x56, 0x5a, 0xc7,
|
||||
0xfe, 0x5b, 0x3d, 0x69, 0xc2, 0xed, 0x84, 0x89, 0xcf, 0x58, 0x9c, 0xb0, 0x32, 0xe8, 0xd6, 0x50,
|
||||
0xbd, 0xdc, 0xba, 0x34, 0xe6, 0x76, 0x87, 0x67, 0xcb, 0x45, 0xae, 0x3a, 0x93, 0x74, 0xeb, 0x52,
|
||||
0xdd, 0x6e, 0x01, 0x0d, 0x4f, 0x25, 0xad, 0x5b, 0x99, 0x5e, 0x3e, 0x0c, 0x9b, 0xf1, 0xb2, 0xca,
|
||||
0xcd, 0x5e, 0x2c, 0x5d, 0x4f, 0x15, 0x46, 0x1d, 0xf5, 0x04, 0x91, 0xb4, 0xd5, 0x93, 0x86, 0xc7,
|
||||
0x83, 0x8e, 0x5b, 0x13, 0x4f, 0xdb, 0x1d, 0xb6, 0x5a, 0x21, 0xf5, 0xb8, 0xbf, 0x02, 0x3c, 0x8c,
|
||||
0x55, 0x51, 0x75, 0x90, 0x56, 0x62, 0x2f, 0xcd, 0xb2, 0xc1, 0x66, 0x20, 0x4c, 0x34, 0x14, 0x3c,
|
||||
0x8c, 0x45, 0x60, 0x22, 0x92, 0xf5, 0xe1, 0x65, 0x3e, 0xe8, 0xb2, 0x23, 0xa9, 0x5e, 0x91, 0xec,
|
||||
0xd2, 0xe0, 0x40, 0xcd, 0x69, 0x6a, 0x53, 0xdb, 0x61, 0xb8, 0xe1, 0x5a, 0x15, 0xde, 0xee, 0xcd,
|
||||
0x83, 0xa7, 0xfd, 0x92, 0x92, 0x2b, 0xcb, 0x5d, 0xca, 0x84, 0xb7, 0x92, 0xdc, 0xeb, 0xa0, 0xc0,
|
||||
0xa1, 0x64, 0x33, 0x8c, 0x5e, 0xa6, 0xc9, 0x9c, 0x09, 0xf4, 0x41, 0x95, 0x0b, 0x04, 0x1f, 0x54,
|
||||
0x01, 0x10, 0x74, 0x5d, 0xf3, 0x77, 0x73, 0x1a, 0x3b, 0x4e, 0xb0, 0xae, 0x53, 0xca, 0x0e, 0x15,
|
||||
0xea, 0x3a, 0x94, 0x06, 0xb3, 0x81, 0x71, 0xab, 0x3e, 0x28, 0xf1, 0x30, 0x64, 0x06, 0x7c, 0x55,
|
||||
0x62, 0xb3, 0x17, 0x0b, 0x56, 0x14, 0xeb, 0x30, 0x5d, 0xa4, 0x02, 0x5b, 0x51, 0x1c, 0x1b, 0x35,
|
||||
0x12, 0x5a, 0x51, 0xda, 0x28, 0x55, 0xbd, 0x3a, 0x47, 0x18, 0x27, 0xe1, 0xea, 0x35, 0x4c, 0xbf,
|
||||
0xea, 0x19, 0xb6, 0xf5, 0x5c, 0x35, 0x37, 0x21, 0x23, 0x2e, 0xd4, 0x66, 0x19, 0x89, 0x6d, 0xf9,
|
||||
0xa2, 0x31, 0x04, 0x43, 0xb3, 0x0e, 0xa5, 0x00, 0x9f, 0x17, 0xd4, 0x9c, 0x7e, 0xf4, 0x5b, 0x14,
|
||||
0x2c, 0x2e, 0xe3, 0x7c, 0x86, 0x6e, 0x4e, 0xa5, 0xc1, 0x16, 0x19, 0xda, 0x9c, 0x92, 0x1a, 0xe0,
|
||||
0xa9, 0xbd, 0xff, 0x26, 0x2f, 0x32, 0x14, 0xcc, 0x2b, 0xb3, 0xfe, 0x8b, 0xbc, 0x0f, 0x7a, 0x90,
|
||||
0xf0, 0xa9, 0xbd, 0x06, 0xcc, 0xb9, 0x7b, 0xe3, 0xf4, 0x83, 0x80, 0x29, 0x1f, 0x0d, 0x6d, 0x84,
|
||||
0x69, 0x15, 0x10, 0xd4, 0xce, 0xd9, 0xe2, 0xe7, 0x6c, 0x85, 0x05, 0xb5, 0x7b, 0x48, 0xf8, 0x39,
|
||||
0x5b, 0x85, 0x82, 0xba, 0x8d, 0x82, 0x3c, 0xd3, 0xdd, 0x07, 0xad, 0x07, 0xf4, 0xdd, 0xad, 0xcf,
|
||||
0x46, 0x27, 0x07, 0x46, 0xce, 0x6e, 0x7a, 0xe5, 0x3d, 0xa6, 0x40, 0x0a, 0xba, 0x9b, 0x5e, 0xe1,
|
||||
0x4f, 0x29, 0x36, 0x7b, 0xb1, 0xf0, 0x46, 0x40, 0x2c, 0xd8, 0x6b, 0xfd, 0xa8, 0x1e, 0x29, 0xae,
|
||||
0x94, 0xb7, 0x9e, 0xd5, 0xdf, 0xef, 0x06, 0xed, 0xfd, 0xdb, 0xe3, 0x92, 0xcf, 0x58, 0x55, 0xa9,
|
||||
0x6f, 0xad, 0xfa, 0x17, 0x9c, 0x94, 0x6c, 0x08, 0xbe, 0xb4, 0x7a, 0x37, 0x0c, 0x39, 0x1f, 0x48,
|
||||
0x6c, 0x44, 0xf6, 0xbb, 0x4d, 0xeb, 0xa8, 0x66, 0xfb, 0x93, 0x4d, 0x1b, 0x9d, 0x9c, 0x1d, 0x5e,
|
||||
0x4a, 0xea, 0x7e, 0xa8, 0xe9, 0x3e, 0xaa, 0x8e, 0x7d, 0xa3, 0xe9, 0x41, 0x0f, 0x52, 0xb9, 0xfa,
|
||||
0x2c, 0x7a, 0xf3, 0x80, 0xcf, 0x27, 0x2c, 0x4f, 0x06, 0xdf, 0xf3, 0x6f, 0xf0, 0xf2, 0xf9, 0xb0,
|
||||
0xfe, 0xb3, 0x31, 0x7a, 0x83, 0x12, 0xdb, 0x3b, 0x88, 0xbb, 0xec, 0x6c, 0x39, 0x9f, 0x88, 0x58,
|
||||
0x80, 0x3b, 0x88, 0xf2, 0xef, 0xc3, 0x5a, 0x40, 0xdc, 0x41, 0xf4, 0x00, 0x60, 0x6f, 0x5a, 0x32,
|
||||
0x86, 0xda, 0xab, 0x05, 0x41, 0x7b, 0x0a, 0xb0, 0x59, 0x84, 0xb1, 0x57, 0x27, 0xea, 0xf0, 0xce,
|
||||
0xa0, 0xd5, 0x91, 0x52, 0x22, 0x8b, 0x68, 0x53, 0x36, 0xb8, 0x9b, 0xea, 0xcb, 0xef, 0xe6, 0x2c,
|
||||
0x17, 0x8b, 0xb8, 0x5c, 0x81, 0xe0, 0x56, 0xb5, 0x74, 0x00, 0x22, 0xb8, 0x51, 0xd0, 0x8e, 0x5a,
|
||||
0xdd, 0xcc, 0xb3, 0xcb, 0x7d, 0x5e, 0xf2, 0xa5, 0x48, 0x73, 0x06, 0xbf, 0x9d, 0x62, 0x1a, 0xd4,
|
||||
0x65, 0x88, 0x51, 0x4b, 0xb1, 0x36, 0xcb, 0x95, 0x44, 0x73, 0x9d, 0x51, 0x7e, 0x6c, 0xbe, 0x12,
|
||||
0xbc, 0x84, 0x8f, 0x33, 0x1b, 0x2b, 0x10, 0x22, 0xb2, 0x5c, 0x12, 0x06, 0x7d, 0x7f, 0x9c, 0xe6,
|
||||
0x73, 0xb4, 0xef, 0x8f, 0xdd, 0xef, 0x17, 0xdf, 0xa2, 0x01, 0x3b, 0xa0, 0x9a, 0x46, 0x6b, 0x06,
|
||||
0x80, 0x7a, 0x69, 0x18, 0x6d, 0x74, 0x97, 0x20, 0x06, 0x14, 0x4e, 0x02, 0x57, 0x5f, 0x14, 0x2c,
|
||||
0x67, 0x89, 0xbe, 0xb4, 0x87, 0xb9, 0xf2, 0x88, 0xa0, 0x2b, 0x48, 0xda, 0xb9, 0x48, 0xca, 0x4f,
|
||||
0x96, 0xf9, 0x71, 0xc9, 0xcf, 0xd3, 0x8c, 0x95, 0x60, 0x2e, 0x6a, 0xd4, 0x1d, 0x39, 0x31, 0x17,
|
||||
0x61, 0x9c, 0xbd, 0xfd, 0x21, 0xa5, 0xde, 0x2f, 0x26, 0x4c, 0xcb, 0x78, 0x06, 0x6f, 0x7f, 0x34,
|
||||
0x36, 0xda, 0x18, 0x71, 0x32, 0x18, 0xc0, 0x9d, 0x44, 0xa7, 0x71, 0x9d, 0xaf, 0x64, 0x7c, 0xa8,
|
||||
0x97, 0x56, 0xe5, 0x57, 0x7d, 0x2b, 0x90, 0xe8, 0x28, 0x73, 0x18, 0x49, 0x24, 0x3a, 0x61, 0x0d,
|
||||
0xbb, 0x94, 0x48, 0xee, 0x48, 0xdd, 0x6a, 0x02, 0x4b, 0x49, 0x63, 0x43, 0x0b, 0x89, 0xa5, 0xa4,
|
||||
0x05, 0x81, 0x09, 0x49, 0x0f, 0x83, 0x39, 0x3a, 0x21, 0x19, 0x69, 0x70, 0x42, 0x72, 0x29, 0x3b,
|
||||
0x51, 0x8c, 0xf3, 0x54, 0xa4, 0x71, 0x36, 0x61, 0xe2, 0x38, 0x2e, 0xe3, 0x05, 0x13, 0xac, 0x84,
|
||||
0x13, 0x85, 0x42, 0x86, 0x1e, 0x43, 0x4c, 0x14, 0x14, 0xab, 0x1c, 0xfe, 0x6e, 0xf4, 0x76, 0xbd,
|
||||
0xee, 0xb3, 0x5c, 0xfd, 0xd6, 0xd3, 0x73, 0xf9, 0x23, 0x71, 0x83, 0x77, 0x8c, 0x8d, 0x89, 0x28,
|
||||
0x59, 0xbc, 0xd0, 0xb6, 0xdf, 0x32, 0x7f, 0x97, 0xe0, 0xe3, 0xb5, 0x3a, 0x9e, 0x8f, 0xb8, 0x48,
|
||||
0xcf, 0xeb, 0x6d, 0xb6, 0x7a, 0x81, 0x09, 0xc4, 0xb3, 0x2b, 0x1e, 0x06, 0x3e, 0x7a, 0x82, 0x71,
|
||||
0x76, 0x9e, 0x76, 0xa5, 0x27, 0xac, 0xc8, 0xe0, 0x3c, 0xed, 0x69, 0x4b, 0x80, 0x98, 0xa7, 0x51,
|
||||
0xd0, 0x0e, 0x4e, 0x57, 0x3c, 0x65, 0xe1, 0xca, 0x4c, 0x59, 0xbf, 0xca, 0x4c, 0xbd, 0x77, 0x42,
|
||||
0xb2, 0xe8, 0xed, 0x43, 0xb6, 0x38, 0x63, 0x65, 0x75, 0x91, 0x16, 0xd4, 0x97, 0x87, 0x2d, 0xd1,
|
||||
0xf9, 0xe5, 0x61, 0x02, 0xb5, 0x2b, 0x81, 0x05, 0xc6, 0xd5, 0x51, 0xbc, 0x60, 0xf2, 0x13, 0x2e,
|
||||
0x60, 0x25, 0x70, 0x8c, 0x38, 0x10, 0xb1, 0x12, 0x90, 0xb0, 0xf3, 0x7a, 0x99, 0x65, 0x4e, 0xd8,
|
||||
0xbc, 0x8e, 0xb0, 0xf2, 0x38, 0x5e, 0x2d, 0x58, 0x2e, 0x94, 0x49, 0x70, 0x26, 0xef, 0x98, 0xc4,
|
||||
0x79, 0xe2, 0x4c, 0xbe, 0x8f, 0x9e, 0x33, 0x35, 0x79, 0x0d, 0x7f, 0xcc, 0x4b, 0xd1, 0xfc, 0x92,
|
||||
0xdb, 0x69, 0x99, 0x81, 0xa9, 0xc9, 0x6f, 0x54, 0x8f, 0x24, 0xa6, 0xa6, 0xb0, 0x86, 0xf3, 0xab,
|
||||
0x1d, 0x5e, 0x19, 0x5e, 0xb0, 0xd2, 0xc4, 0xc9, 0xf3, 0x45, 0x9c, 0x66, 0x2a, 0x1a, 0xbe, 0x1f,
|
||||
0xb0, 0x4d, 0xe8, 0x10, 0xbf, 0xda, 0xd1, 0x57, 0xd7, 0xf9, 0x9d, 0x93, 0x70, 0x09, 0xc1, 0x23,
|
||||
0x82, 0x0e, 0xfb, 0xc4, 0x23, 0x82, 0x6e, 0x2d, 0xbb, 0x73, 0xb7, 0xac, 0xe4, 0x56, 0x92, 0xd8,
|
||||
0xe1, 0x09, 0x3c, 0x2f, 0x74, 0x6c, 0x02, 0x90, 0xd8, 0xb9, 0x07, 0x15, 0x6c, 0x6a, 0x60, 0xb1,
|
||||
0xbd, 0x34, 0x8f, 0xb3, 0xf4, 0xc7, 0x30, 0xad, 0x77, 0xec, 0x68, 0x82, 0x48, 0x0d, 0x70, 0x12,
|
||||
0x73, 0xb5, 0xcf, 0xc4, 0x34, 0xad, 0xa7, 0xfe, 0xfb, 0x81, 0x76, 0x93, 0x44, 0xb7, 0x2b, 0x87,
|
||||
0x74, 0xbe, 0x32, 0x0c, 0x9b, 0x75, 0x54, 0x14, 0x93, 0x7a, 0x55, 0x3d, 0x61, 0x33, 0x96, 0x16,
|
||||
0x62, 0xf0, 0x51, 0xb8, 0xad, 0x00, 0x4e, 0x5c, 0xb4, 0xe8, 0xa1, 0xe6, 0x3c, 0xbe, 0xaf, 0xe7,
|
||||
0x92, 0x49, 0xf3, 0x13, 0xa7, 0xa7, 0x15, 0x2b, 0x55, 0xa2, 0xb1, 0xcf, 0x04, 0x18, 0x9d, 0x0e,
|
||||
0x37, 0x74, 0xc0, 0xba, 0xa2, 0xc4, 0xe8, 0x0c, 0x6b, 0xd8, 0xc3, 0x3e, 0x87, 0x53, 0x5f, 0x8d,
|
||||
0x97, 0xd7, 0x1d, 0x1f, 0x91, 0xc6, 0x1c, 0x8a, 0x38, 0xec, 0xa3, 0x69, 0x9b, 0xad, 0xb5, 0xdd,
|
||||
0x8e, 0xf2, 0xd5, 0x18, 0x5e, 0x99, 0x40, 0x2c, 0x49, 0x8c, 0xc8, 0xd6, 0x02, 0xb8, 0x73, 0x18,
|
||||
0x5e, 0xf2, 0x38, 0x99, 0xc5, 0x95, 0x38, 0x8e, 0x57, 0x19, 0x8f, 0x13, 0xb9, 0xae, 0xc3, 0xc3,
|
||||
0x70, 0xcd, 0x0c, 0x5d, 0x88, 0x3a, 0x0c, 0xa7, 0x60, 0x37, 0x3b, 0x93, 0xbf, 0xdc, 0xaa, 0xae,
|
||||
0x92, 0xc2, 0xec, 0x4c, 0x96, 0x17, 0x5e, 0x23, 0xbd, 0x1b, 0x86, 0xec, 0x2b, 0x70, 0x8d, 0x48,
|
||||
0xa6, 0x21, 0xb7, 0x30, 0x1d, 0x2f, 0x01, 0x79, 0x3f, 0x40, 0xd8, 0x0f, 0xa0, 0x34, 0x7f, 0xd7,
|
||||
0x3f, 0xd6, 0x25, 0xd4, 0xb7, 0xd8, 0x1f, 0x61, 0xba, 0x2e, 0xe4, 0xdd, 0x50, 0xdb, 0xea, 0x49,
|
||||
0xdb, 0x34, 0x73, 0xe7, 0x22, 0x16, 0xa3, 0x24, 0x39, 0x64, 0x15, 0xf2, 0x3e, 0x7b, 0x2d, 0x1c,
|
||||
0x5a, 0x29, 0x91, 0x66, 0xb6, 0x29, 0x1b, 0xe8, 0xb5, 0xec, 0x79, 0x92, 0x0a, 0x25, 0xd3, 0x17,
|
||||
0xb4, 0x1f, 0xb5, 0x0d, 0xb4, 0x29, 0xa2, 0x56, 0x34, 0x6d, 0xe7, 0xf2, 0x9a, 0x99, 0xf2, 0xf9,
|
||||
0x3c, 0x63, 0x0a, 0x3a, 0x61, 0x71, 0xf3, 0x29, 0xca, 0xed, 0xb6, 0x2d, 0x14, 0x24, 0xe6, 0xf2,
|
||||
0xa0, 0x82, 0x4d, 0x23, 0x6b, 0xac, 0x79, 0x24, 0xa5, 0x1b, 0x76, 0xa3, 0x6d, 0xc6, 0x03, 0x88,
|
||||
0x34, 0x12, 0x05, 0xed, 0x6b, 0x77, 0xb5, 0x78, 0x9f, 0xe9, 0x96, 0x80, 0xdf, 0xba, 0x92, 0xca,
|
||||
0x8e, 0x98, 0x78, 0xed, 0x0e, 0xc1, 0xec, 0x3e, 0x01, 0x78, 0x78, 0xb6, 0x1a, 0x27, 0x70, 0x9f,
|
||||
0x00, 0xf5, 0x25, 0x43, 0xec, 0x13, 0x28, 0xd6, 0xef, 0x3a, 0x73, 0xee, 0x75, 0x10, 0x57, 0xb6,
|
||||
0x72, 0x48, 0xd7, 0xa1, 0x60, 0xa8, 0xeb, 0x28, 0x05, 0xbf, 0x49, 0xdd, 0xa3, 0x35, 0xa4, 0x49,
|
||||
0xb1, 0x73, 0xb5, 0xf5, 0x2e, 0xcc, 0xe6, 0xfe, 0xb5, 0xf0, 0x84, 0xc5, 0x89, 0xa9, 0x18, 0xa2,
|
||||
0xeb, 0xca, 0x89, 0xdc, 0x1f, 0xe3, 0x94, 0x93, 0x3f, 0x88, 0x06, 0x4d, 0x35, 0x4a, 0xd7, 0xcd,
|
||||
0x2d, 0xac, 0x88, 0x35, 0x41, 0x4c, 0x54, 0x3e, 0xe1, 0x24, 0x6e, 0x5e, 0x17, 0x4d, 0xb9, 0x72,
|
||||
0xa0, 0x5e, 0x0b, 0xad, 0x40, 0xe2, 0xe6, 0x37, 0x7b, 0x8b, 0x26, 0x12, 0xb7, 0x6e, 0x2d, 0xe7,
|
||||
0xb3, 0x3f, 0xa0, 0xcb, 0xf6, 0x4a, 0xbe, 0x80, 0x65, 0xfa, 0x34, 0xd8, 0x3d, 0x88, 0x06, 0xf1,
|
||||
0xd9, 0x9f, 0x7e, 0x9a, 0x76, 0x0d, 0x32, 0x67, 0x07, 0xf2, 0x7a, 0x1a, 0xfe, 0x7b, 0x23, 0x8d,
|
||||
0x90, 0x58, 0x83, 0x5a, 0x90, 0xf3, 0x23, 0xa5, 0xe3, 0x97, 0x65, 0x2a, 0xd2, 0x7c, 0x3e, 0xe5,
|
||||
0x3c, 0x83, 0x47, 0x96, 0xa3, 0xf1, 0xd0, 0x95, 0x52, 0x3f, 0x52, 0xda, 0xa2, 0xec, 0x12, 0x37,
|
||||
0x1a, 0x8f, 0x96, 0x82, 0x9f, 0xa7, 0x59, 0x06, 0x22, 0x67, 0x34, 0x1e, 0x6a, 0x09, 0x11, 0x39,
|
||||
0x3e, 0xe1, 0xfc, 0xb4, 0xe6, 0x58, 0x9e, 0xfe, 0xab, 0x13, 0xd0, 0x3b, 0x50, 0xc7, 0x11, 0x52,
|
||||
0x3f, 0xad, 0x09, 0x21, 0xe7, 0xa7, 0x42, 0xc7, 0xd8, 0x8f, 0xa6, 0x6c, 0x42, 0x75, 0x04, 0xa2,
|
||||
0x7e, 0x2a, 0x94, 0x82, 0x1b, 0x9f, 0xcf, 0xde, 0xff, 0x9f, 0xaf, 0x6e, 0xac, 0xfd, 0xf4, 0xab,
|
||||
0x1b, 0x6b, 0xff, 0xfb, 0xd5, 0x8d, 0xb5, 0x9f, 0x7c, 0x7d, 0xe3, 0x8d, 0x9f, 0x7e, 0x7d, 0xe3,
|
||||
0x8d, 0x9f, 0x7d, 0x7d, 0xe3, 0x8d, 0x2f, 0xdf, 0x54, 0xbf, 0x9f, 0x7f, 0xf6, 0x0b, 0xf2, 0x57,
|
||||
0xf0, 0x9f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x64, 0x71, 0xe6, 0xb7, 0x63, 0x7f, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
@ -511,6 +510,7 @@ type ClientCommandsClient interface {
|
|||
ObjectUndo(ctx context.Context, in *pb.RpcObjectUndoRequest, opts ...grpc.CallOption) (*pb.RpcObjectUndoResponse, error)
|
||||
ObjectRedo(ctx context.Context, in *pb.RpcObjectRedoRequest, opts ...grpc.CallOption) (*pb.RpcObjectRedoResponse, error)
|
||||
ObjectListExport(ctx context.Context, in *pb.RpcObjectListExportRequest, opts ...grpc.CallOption) (*pb.RpcObjectListExportResponse, error)
|
||||
ObjectExport(ctx context.Context, in *pb.RpcObjectExportRequest, opts ...grpc.CallOption) (*pb.RpcObjectExportResponse, error)
|
||||
ObjectBookmarkFetch(ctx context.Context, in *pb.RpcObjectBookmarkFetchRequest, opts ...grpc.CallOption) (*pb.RpcObjectBookmarkFetchResponse, error)
|
||||
ObjectToBookmark(ctx context.Context, in *pb.RpcObjectToBookmarkRequest, opts ...grpc.CallOption) (*pb.RpcObjectToBookmarkResponse, error)
|
||||
ObjectImport(ctx context.Context, in *pb.RpcObjectImportRequest, opts ...grpc.CallOption) (*pb.RpcObjectImportResponse, error)
|
||||
|
@ -755,8 +755,6 @@ type ClientCommandsClient interface {
|
|||
AIAutofill(ctx context.Context, in *pb.RpcAIAutofillRequest, opts ...grpc.CallOption) (*pb.RpcAIAutofillResponse, error)
|
||||
AIListSummary(ctx context.Context, in *pb.RpcAIListSummaryRequest, opts ...grpc.CallOption) (*pb.RpcAIListSummaryResponse, error)
|
||||
AIObjectCreateFromUrl(ctx context.Context, in *pb.RpcAIObjectCreateFromUrlRequest, opts ...grpc.CallOption) (*pb.RpcAIObjectCreateFromUrlResponse, error)
|
||||
// Push
|
||||
PushNotificationRegisterToken(ctx context.Context, in *pb.RpcPushNotificationRegisterTokenRequest, opts ...grpc.CallOption) (*pb.RpcPushNotificationRegisterTokenResponse, error)
|
||||
}
|
||||
|
||||
type clientCommandsClient struct {
|
||||
|
@ -1622,6 +1620,15 @@ func (c *clientCommandsClient) ObjectListExport(ctx context.Context, in *pb.RpcO
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientCommandsClient) ObjectExport(ctx context.Context, in *pb.RpcObjectExportRequest, opts ...grpc.CallOption) (*pb.RpcObjectExportResponse, error) {
|
||||
out := new(pb.RpcObjectExportResponse)
|
||||
err := c.cc.Invoke(ctx, "/anytype.ClientCommands/ObjectExport", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientCommandsClient) ObjectBookmarkFetch(ctx context.Context, in *pb.RpcObjectBookmarkFetchRequest, opts ...grpc.CallOption) (*pb.RpcObjectBookmarkFetchResponse, error) {
|
||||
out := new(pb.RpcObjectBookmarkFetchResponse)
|
||||
err := c.cc.Invoke(ctx, "/anytype.ClientCommands/ObjectBookmarkFetch", in, out, opts...)
|
||||
|
@ -3409,15 +3416,6 @@ func (c *clientCommandsClient) AIObjectCreateFromUrl(ctx context.Context, in *pb
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientCommandsClient) PushNotificationRegisterToken(ctx context.Context, in *pb.RpcPushNotificationRegisterTokenRequest, opts ...grpc.CallOption) (*pb.RpcPushNotificationRegisterTokenResponse, error) {
|
||||
out := new(pb.RpcPushNotificationRegisterTokenResponse)
|
||||
err := c.cc.Invoke(ctx, "/anytype.ClientCommands/PushNotificationRegisterToken", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ClientCommandsServer is the server API for ClientCommands service.
|
||||
type ClientCommandsServer interface {
|
||||
AppGetVersion(context.Context, *pb.RpcAppGetVersionRequest) *pb.RpcAppGetVersionResponse
|
||||
|
@ -3531,6 +3529,7 @@ type ClientCommandsServer interface {
|
|||
ObjectUndo(context.Context, *pb.RpcObjectUndoRequest) *pb.RpcObjectUndoResponse
|
||||
ObjectRedo(context.Context, *pb.RpcObjectRedoRequest) *pb.RpcObjectRedoResponse
|
||||
ObjectListExport(context.Context, *pb.RpcObjectListExportRequest) *pb.RpcObjectListExportResponse
|
||||
ObjectExport(context.Context, *pb.RpcObjectExportRequest) *pb.RpcObjectExportResponse
|
||||
ObjectBookmarkFetch(context.Context, *pb.RpcObjectBookmarkFetchRequest) *pb.RpcObjectBookmarkFetchResponse
|
||||
ObjectToBookmark(context.Context, *pb.RpcObjectToBookmarkRequest) *pb.RpcObjectToBookmarkResponse
|
||||
ObjectImport(context.Context, *pb.RpcObjectImportRequest) *pb.RpcObjectImportResponse
|
||||
|
@ -3775,8 +3774,6 @@ type ClientCommandsServer interface {
|
|||
AIAutofill(context.Context, *pb.RpcAIAutofillRequest) *pb.RpcAIAutofillResponse
|
||||
AIListSummary(context.Context, *pb.RpcAIListSummaryRequest) *pb.RpcAIListSummaryResponse
|
||||
AIObjectCreateFromUrl(context.Context, *pb.RpcAIObjectCreateFromUrlRequest) *pb.RpcAIObjectCreateFromUrlResponse
|
||||
// Push
|
||||
PushNotificationRegisterToken(context.Context, *pb.RpcPushNotificationRegisterTokenRequest) *pb.RpcPushNotificationRegisterTokenResponse
|
||||
}
|
||||
|
||||
// UnimplementedClientCommandsServer can be embedded to have forward compatible implementations.
|
||||
|
@ -4068,6 +4065,9 @@ func (*UnimplementedClientCommandsServer) ObjectRedo(ctx context.Context, req *p
|
|||
func (*UnimplementedClientCommandsServer) ObjectListExport(ctx context.Context, req *pb.RpcObjectListExportRequest) *pb.RpcObjectListExportResponse {
|
||||
return nil
|
||||
}
|
||||
func (*UnimplementedClientCommandsServer) ObjectExport(ctx context.Context, req *pb.RpcObjectExportRequest) *pb.RpcObjectExportResponse {
|
||||
return nil
|
||||
}
|
||||
func (*UnimplementedClientCommandsServer) ObjectBookmarkFetch(ctx context.Context, req *pb.RpcObjectBookmarkFetchRequest) *pb.RpcObjectBookmarkFetchResponse {
|
||||
return nil
|
||||
}
|
||||
|
@ -4656,9 +4656,6 @@ func (*UnimplementedClientCommandsServer) AIListSummary(ctx context.Context, req
|
|||
func (*UnimplementedClientCommandsServer) AIObjectCreateFromUrl(ctx context.Context, req *pb.RpcAIObjectCreateFromUrlRequest) *pb.RpcAIObjectCreateFromUrlResponse {
|
||||
return nil
|
||||
}
|
||||
func (*UnimplementedClientCommandsServer) PushNotificationRegisterToken(ctx context.Context, req *pb.RpcPushNotificationRegisterTokenRequest) *pb.RpcPushNotificationRegisterTokenResponse {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RegisterClientCommandsServer(s *grpc.Server, srv ClientCommandsServer) {
|
||||
s.RegisterService(&_ClientCommands_serviceDesc, srv)
|
||||
|
@ -6374,6 +6371,24 @@ func _ClientCommands_ObjectListExport_Handler(srv interface{}, ctx context.Conte
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ClientCommands_ObjectExport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(pb.RpcObjectExportRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClientCommandsServer).ObjectExport(ctx, in), nil
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/anytype.ClientCommands/ObjectExport",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientCommandsServer).ObjectExport(ctx, req.(*pb.RpcObjectExportRequest)), nil
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ClientCommands_ObjectBookmarkFetch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(pb.RpcObjectBookmarkFetchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
|
@ -9906,24 +9921,6 @@ func _ClientCommands_AIObjectCreateFromUrl_Handler(srv interface{}, ctx context.
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ClientCommands_PushNotificationRegisterToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(pb.RpcPushNotificationRegisterTokenRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClientCommandsServer).PushNotificationRegisterToken(ctx, in), nil
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/anytype.ClientCommands/PushNotificationRegisterToken",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientCommandsServer).PushNotificationRegisterToken(ctx, req.(*pb.RpcPushNotificationRegisterTokenRequest)), nil
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _ClientCommands_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "anytype.ClientCommands",
|
||||
HandlerType: (*ClientCommandsServer)(nil),
|
||||
|
@ -10308,6 +10305,10 @@ var _ClientCommands_serviceDesc = grpc.ServiceDesc{
|
|||
MethodName: "ObjectListExport",
|
||||
Handler: _ClientCommands_ObjectListExport_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ObjectExport",
|
||||
Handler: _ClientCommands_ObjectExport_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ObjectBookmarkFetch",
|
||||
Handler: _ClientCommands_ObjectBookmarkFetch_Handler,
|
||||
|
@ -11088,10 +11089,6 @@ var _ClientCommands_serviceDesc = grpc.ServiceDesc{
|
|||
MethodName: "AIObjectCreateFromUrl",
|
||||
Handler: _ClientCommands_AIObjectCreateFromUrl_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PushNotificationRegisterToken",
|
||||
Handler: _ClientCommands_PushNotificationRegisterToken_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
const RelationChecksum = "e8392d6bf8efab6b707d68bc3922e4284a18a68370828e3ec6b18e0267b84bea"
|
||||
const RelationChecksum = "a23583f8faef1cbfcd05ca1d2761538e67bbdd1336d1a467a0d657b5f986b313"
|
||||
const (
|
||||
RelationKeyTag domain.RelationKey = "tag"
|
||||
RelationKeyCamera domain.RelationKey = "camera"
|
||||
|
@ -158,6 +158,7 @@ const (
|
|||
RelationKeyDefaultViewType domain.RelationKey = "defaultViewType"
|
||||
RelationKeyDefaultTypeId domain.RelationKey = "defaultTypeId"
|
||||
RelationKeyAutoWidgetTargets domain.RelationKey = "autoWidgetTargets"
|
||||
RelationKeyAutoWidgetDisabled domain.RelationKey = "autoWidgetDisabled"
|
||||
RelationKeyPluralName domain.RelationKey = "pluralName"
|
||||
)
|
||||
|
||||
|
@ -282,6 +283,19 @@ var (
|
|||
Revision: 1,
|
||||
Scope: model.Relation_type,
|
||||
},
|
||||
RelationKeyAutoWidgetDisabled: {
|
||||
|
||||
DataSource: model.Relation_details,
|
||||
Description: "",
|
||||
Format: model.RelationFormat_checkbox,
|
||||
Hidden: true,
|
||||
Id: "_brautoWidgetDisabled",
|
||||
Key: "autoWidgetDisabled",
|
||||
Name: "Auto Widget disabled",
|
||||
ReadOnly: false,
|
||||
ReadOnlyRelation: true,
|
||||
Scope: model.Relation_type,
|
||||
},
|
||||
RelationKeyAutoWidgetTargets: {
|
||||
|
||||
DataSource: model.Relation_details,
|
||||
|
|
|
@ -1517,6 +1517,15 @@
|
|||
"readonly": false,
|
||||
"source": "details"
|
||||
},
|
||||
{
|
||||
"format": "checkbox",
|
||||
"hidden": true,
|
||||
"key": "autoWidgetDisabled",
|
||||
"maxCount": 0,
|
||||
"name": "Auto Widget disabled",
|
||||
"readonly": false,
|
||||
"source": "details"
|
||||
},
|
||||
{
|
||||
"description": "Name of Object type in plural form",
|
||||
"format": "longtext",
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
)
|
||||
|
||||
const TypeChecksum = "91c804ca7dd4df74fafbdaff99e50fa2ffcc6cb0fb073887a90bdf1b4a3d98d5"
|
||||
const TypeChecksum = "f2672b9f52915f228e335b78ed94ce1b6c7b195ceb1cba9a44b25a870a3b2ac0"
|
||||
const (
|
||||
TypePrefix = "_ot"
|
||||
)
|
||||
|
@ -426,7 +426,7 @@ var (
|
|||
Name: "Task",
|
||||
PluralName: "Tasks",
|
||||
Readonly: true,
|
||||
RelationLinks: []*model.RelationLink{MustGetRelationLink(RelationKeyTag), MustGetRelationLink(RelationKeyAssignee), MustGetRelationLink(RelationKeyDone), MustGetRelationLink(RelationKeyDueDate), MustGetRelationLink(RelationKeyLinkedProjects), MustGetRelationLink(RelationKeyPriority), MustGetRelationLink(RelationKeyStatus), MustGetRelationLink(RelationKeyTasks)},
|
||||
RelationLinks: []*model.RelationLink{MustGetRelationLink(RelationKeyTag), MustGetRelationLink(RelationKeyAssignee), MustGetRelationLink(RelationKeyDone), MustGetRelationLink(RelationKeyDueDate), MustGetRelationLink(RelationKeyLinkedProjects), MustGetRelationLink(RelationKeyStatus)},
|
||||
Revision: 2,
|
||||
Types: []model.SmartBlockType{model.SmartBlockType_Page},
|
||||
Url: TypePrefix + "task",
|
||||
|
|
|
@ -107,9 +107,7 @@
|
|||
"done",
|
||||
"dueDate",
|
||||
"linkedProjects",
|
||||
"priority",
|
||||
"status",
|
||||
"tasks"
|
||||
"status"
|
||||
],
|
||||
"description": "A piece of work to be done or undertaken",
|
||||
"revision": 2
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
|
||||
|
@ -54,8 +55,36 @@ func (s *TempDirService) TempDir() string {
|
|||
s.tempDir = os.TempDir()
|
||||
} else {
|
||||
s.tempDir = path
|
||||
go s.cleanUp()
|
||||
}
|
||||
})
|
||||
|
||||
return s.tempDir
|
||||
}
|
||||
|
||||
func (s *TempDirService) cleanUp() {
|
||||
cutoff := time.Now().Add(-72 * time.Hour)
|
||||
recursiveCleanup(s.tempDir, cutoff)
|
||||
}
|
||||
|
||||
func recursiveCleanup(path string, cutoff time.Time) {
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Warnf("tmp cleanup readdir: %v", err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
fullEntryPath := filepath.Join(path, entry.Name())
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
log.Warnf("tmp cleanup entry: %v", err)
|
||||
}
|
||||
if entry.IsDir() {
|
||||
recursiveCleanup(fullEntryPath, cutoff)
|
||||
} else if info.ModTime().IsZero() || info.ModTime().Before(cutoff) {
|
||||
err = os.RemoveAll(fullEntryPath)
|
||||
if err != nil {
|
||||
log.Warnf("tmp cleanup delete: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
50
pkg/lib/core/tmp_dir_test.go
Normal file
50
pkg/lib/core/tmp_dir_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTempDirService(t *testing.T) {
|
||||
t.Run("cleanup", func(t *testing.T) {
|
||||
// given
|
||||
s := NewTempDirService()
|
||||
s.tempDir = t.TempDir()
|
||||
|
||||
oldFile := filepath.Join(s.tempDir, "old.txt")
|
||||
require.NoError(t, os.WriteFile(oldFile, []byte("old"), 0600))
|
||||
oldTime := time.Now().Add(-100 * time.Hour)
|
||||
require.NoError(t, os.Chtimes(oldFile, oldTime, oldTime))
|
||||
|
||||
newFile := filepath.Join(s.tempDir, "new.txt")
|
||||
require.NoError(t, os.WriteFile(newFile, []byte("new"), 0600))
|
||||
|
||||
nestedDir := filepath.Join(s.tempDir, "nested")
|
||||
require.NoError(t, os.MkdirAll(nestedDir, 0755))
|
||||
nestedOldFile := filepath.Join(nestedDir, "nested_old.txt")
|
||||
require.NoError(t, os.WriteFile(nestedOldFile, []byte("nested old"), 0600))
|
||||
require.NoError(t, os.Chtimes(nestedOldFile, oldTime, oldTime))
|
||||
nestedNewFile := filepath.Join(nestedDir, "nested_new.txt")
|
||||
require.NoError(t, os.WriteFile(nestedNewFile, []byte("nested new"), 0600))
|
||||
|
||||
// when
|
||||
s.cleanUp()
|
||||
|
||||
// then
|
||||
_, err := os.Stat(oldFile)
|
||||
require.True(t, os.IsNotExist(err), "old file should be deleted")
|
||||
|
||||
_, err = os.Stat(newFile)
|
||||
require.NoError(t, err, "new file should remain")
|
||||
|
||||
_, err = os.Stat(nestedOldFile)
|
||||
require.True(t, os.IsNotExist(err), "nested old file should be deleted")
|
||||
|
||||
_, err = os.Stat(nestedNewFile)
|
||||
require.NoError(t, err, "nested new file should remain")
|
||||
})
|
||||
}
|
|
@ -25,6 +25,7 @@ import (
|
|||
"unicode"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/app/debugstat"
|
||||
tantivy "github.com/anyproto/tantivy-go"
|
||||
"github.com/valyala/fastjson"
|
||||
|
||||
|
@ -102,6 +103,19 @@ type ftSearch struct {
|
|||
lang tantivy.Language
|
||||
}
|
||||
|
||||
func (f *ftSearch) ProvideStat() any {
|
||||
count, _ := f.DocCount()
|
||||
return count
|
||||
}
|
||||
|
||||
func (f *ftSearch) StatId() string {
|
||||
return "doc_count"
|
||||
}
|
||||
|
||||
func (f *ftSearch) StatType() string {
|
||||
return CName
|
||||
}
|
||||
|
||||
func TantivyNew() FTSearch {
|
||||
return new(ftSearch)
|
||||
}
|
||||
|
@ -138,6 +152,10 @@ func (f *ftSearch) DeleteObject(objectId string) error {
|
|||
|
||||
func (f *ftSearch) Init(a *app.App) error {
|
||||
repoPath := app.MustComponent[wallet.Wallet](a).RepoPath()
|
||||
statService, _ := app.GetComponent[debugstat.StatService](a)
|
||||
if statService != nil {
|
||||
statService.AddProvider(f)
|
||||
}
|
||||
f.lang = validateLanguage(app.MustComponent[wallet.Wallet](a).FtsPrimaryLang())
|
||||
f.rootPath = filepath.Join(repoPath, ftsDir2)
|
||||
f.blevePath = filepath.Join(repoPath, ftsDir)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
anystore "github.com/anyproto/any-store"
|
||||
"github.com/anyproto/any-store/anyenc"
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/app/debugstat"
|
||||
"github.com/anyproto/any-sync/coordinator/coordinatorproto"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
|
@ -124,6 +125,19 @@ type dsObjectStore struct {
|
|||
componentCtxCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (s *dsObjectStore) ProvideStat() any {
|
||||
count, _ := s.ListIdsCrossSpace()
|
||||
return len(count)
|
||||
}
|
||||
|
||||
func (s *dsObjectStore) StatId() string {
|
||||
return "ds_count"
|
||||
}
|
||||
|
||||
func (s *dsObjectStore) StatType() string {
|
||||
return CName
|
||||
}
|
||||
|
||||
func (s *dsObjectStore) IterateSpaceIndex(f func(store spaceindex.Store) error) error {
|
||||
s.Lock()
|
||||
spaceIndexes := make([]spaceindex.Store, 0, len(s.spaceIndexes))
|
||||
|
@ -169,6 +183,10 @@ func (s *dsObjectStore) Init(a *app.App) (err error) {
|
|||
s.setDefaultConfig()
|
||||
s.oldStore = app.MustComponent[oldstore.Service](a)
|
||||
s.techSpaceIdProvider = app.MustComponent[TechSpaceIdProvider](a)
|
||||
statService, _ := app.GetComponent[debugstat.StatService](a)
|
||||
if statService != nil {
|
||||
statService.AddProvider(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -42,6 +42,14 @@ const (
|
|||
TIFF Format = "tiff"
|
||||
)
|
||||
|
||||
func IsImageExt(ext string) bool {
|
||||
switch strings.ToLower(strings.TrimPrefix(ext, ".")) {
|
||||
case "jpg", "jpeg", "png", "gif", "ico", "webp", "heic", "heif", "bmp", "tiff", "psd":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsImage(mime string) bool {
|
||||
parts := strings.SplitN(mime, "/", 2)
|
||||
if len(parts) == 1 {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -593,6 +593,7 @@ message Block {
|
|||
Layout layout = 1;
|
||||
int32 limit = 2;
|
||||
string viewId = 3;
|
||||
bool autoAdded = 4;
|
||||
|
||||
enum Layout {
|
||||
Link = 0;
|
||||
|
|
|
@ -405,7 +405,7 @@ func (b *builtinObjects) createWidgets(ctx session.Context, spaceId string, useC
|
|||
}
|
||||
if err = cache.DoStateCtx(b.objectGetter, ctx, widgetObjectID, func(s *state.State, w widget.Widget) error {
|
||||
for _, targetId := range widgetTargetsToCreate {
|
||||
if err := w.AddAutoWidget(s, targetId, "", addr.ObjectTypeAllViewId, model.BlockContentWidget_View); err != nil {
|
||||
if err := w.AddAutoWidget(s, targetId, "", addr.ObjectTypeAllViewId, model.BlockContentWidget_View, ""); err != nil {
|
||||
log.Errorf("failed to create widget block for type '%s': %v", targetId, err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue