diff --git a/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.cc b/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.cc index 551bf9a84da..331da2f4a33 100644 --- a/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.cc +++ b/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.cc @@ -276,6 +276,9 @@ PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT inline constexpr ListItemsResponse::Impl_::Impl_( ::_pbi::ConstantInitialized) noexcept : items_{}, + canonical_path_( + &::google::protobuf::internal::fixed_address_empty_string, + ::_pbi::ConstantInitialized()), _cached_size_{0} {} template @@ -339,6 +342,7 @@ const ::uint32_t TableStruct_deephaven_2fproto_2fstorage_2eproto::offsets[] PROT ~0u, // no _split_ ~0u, // no sizeof(Split) PROTOBUF_FIELD_OFFSET(::io::deephaven::proto::backplane::grpc::ListItemsResponse, _impl_.items_), + PROTOBUF_FIELD_OFFSET(::io::deephaven::proto::backplane::grpc::ListItemsResponse, _impl_.canonical_path_), PROTOBUF_FIELD_OFFSET(::io::deephaven::proto::backplane::grpc::FetchFileRequest, _impl_._has_bits_), PROTOBUF_FIELD_OFFSET(::io::deephaven::proto::backplane::grpc::FetchFileRequest, _internal_metadata_), ~0u, // no _extensions_ @@ -444,16 +448,16 @@ static const ::_pbi::MigrationSchema {0, 10, -1, sizeof(::io::deephaven::proto::backplane::grpc::ListItemsRequest)}, {12, 24, -1, sizeof(::io::deephaven::proto::backplane::grpc::ItemInfo)}, {28, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::ListItemsResponse)}, - {37, 47, -1, sizeof(::io::deephaven::proto::backplane::grpc::FetchFileRequest)}, - {49, 59, -1, sizeof(::io::deephaven::proto::backplane::grpc::FetchFileResponse)}, - {61, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::SaveFileRequest)}, - {72, 81, -1, sizeof(::io::deephaven::proto::backplane::grpc::SaveFileResponse)}, - {82, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::MoveItemRequest)}, - {93, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::MoveItemResponse)}, - {101, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::CreateDirectoryRequest)}, - {110, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::CreateDirectoryResponse)}, - {118, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::DeleteItemRequest)}, - {127, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::DeleteItemResponse)}, + {38, 48, -1, sizeof(::io::deephaven::proto::backplane::grpc::FetchFileRequest)}, + {50, 60, -1, sizeof(::io::deephaven::proto::backplane::grpc::FetchFileResponse)}, + {62, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::SaveFileRequest)}, + {73, 82, -1, sizeof(::io::deephaven::proto::backplane::grpc::SaveFileResponse)}, + {83, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::MoveItemRequest)}, + {94, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::MoveItemResponse)}, + {102, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::CreateDirectoryRequest)}, + {111, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::CreateDirectoryResponse)}, + {119, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::DeleteItemRequest)}, + {128, -1, -1, sizeof(::io::deephaven::proto::backplane::grpc::DeleteItemResponse)}, }; static const ::_pb::Message* const file_default_instances[] = { @@ -478,50 +482,50 @@ const char descriptor_table_protodef_deephaven_2fproto_2fstorage_2eproto[] PROTO "\tH\000\210\001\001B\016\n\014_filter_glob\"\201\001\n\010ItemInfo\022\014\n\004p" "ath\030\001 \001(\t\0229\n\004type\030\002 \001(\0162+.io.deephaven.p" "roto.backplane.grpc.ItemType\022\020\n\004size\030\003 \001" - "(\022B\0020\001\022\021\n\004etag\030\004 \001(\tH\000\210\001\001B\007\n\005_etag\"O\n\021Li" + "(\022B\0020\001\022\021\n\004etag\030\004 \001(\tH\000\210\001\001B\007\n\005_etag\"g\n\021Li" "stItemsResponse\022:\n\005items\030\001 \003(\0132+.io.deep" - "haven.proto.backplane.grpc.ItemInfo\"<\n\020F" - "etchFileRequest\022\014\n\004path\030\001 \001(\t\022\021\n\004etag\030\002 " - "\001(\tH\000\210\001\001B\007\n\005_etag\"A\n\021FetchFileResponse\022\020" - "\n\010contents\030\001 \001(\014\022\021\n\004etag\030\002 \001(\tH\000\210\001\001B\007\n\005_" - "etag\"J\n\017SaveFileRequest\022\027\n\017allow_overwri" - "te\030\001 \001(\010\022\014\n\004path\030\002 \001(\t\022\020\n\010contents\030\003 \001(\014" - "\".\n\020SaveFileResponse\022\021\n\004etag\030\001 \001(\tH\000\210\001\001B" - "\007\n\005_etag\"N\n\017MoveItemRequest\022\020\n\010old_path\030" - "\001 \001(\t\022\020\n\010new_path\030\002 \001(\t\022\027\n\017allow_overwri" - "te\030\003 \001(\010\"\022\n\020MoveItemResponse\"&\n\026CreateDi" - "rectoryRequest\022\014\n\004path\030\001 \001(\t\"\031\n\027CreateDi" - "rectoryResponse\"!\n\021DeleteItemRequest\022\014\n\004" - "path\030\001 \001(\t\"\024\n\022DeleteItemResponse*0\n\010Item" - "Type\022\013\n\007UNKNOWN\020\000\022\r\n\tDIRECTORY\020\001\022\010\n\004FILE" - "\020\0022\374\005\n\016StorageService\022x\n\tListItems\0223.io." - "deephaven.proto.backplane.grpc.ListItems" - "Request\0324.io.deephaven.proto.backplane.g" - "rpc.ListItemsResponse\"\000\022x\n\tFetchFile\0223.i" - "o.deephaven.proto.backplane.grpc.FetchFi" - "leRequest\0324.io.deephaven.proto.backplane" - ".grpc.FetchFileResponse\"\000\022u\n\010SaveFile\0222." - "io.deephaven.proto.backplane.grpc.SaveFi" - "leRequest\0323.io.deephaven.proto.backplane" - ".grpc.SaveFileResponse\"\000\022u\n\010MoveItem\0222.i" - "o.deephaven.proto.backplane.grpc.MoveIte" - "mRequest\0323.io.deephaven.proto.backplane." - "grpc.MoveItemResponse\"\000\022\212\001\n\017CreateDirect" - "ory\0229.io.deephaven.proto.backplane.grpc." - "CreateDirectoryRequest\032:.io.deephaven.pr" - "oto.backplane.grpc.CreateDirectoryRespon" - "se\"\000\022{\n\nDeleteItem\0224.io.deephaven.proto." - "backplane.grpc.DeleteItemRequest\0325.io.de" - "ephaven.proto.backplane.grpc.DeleteItemR" - "esponse\"\000BCH\001P\001Z=github.com/deephaven/de" - "ephaven-core/go/internal/proto/storageb\006" - "proto3" + "haven.proto.backplane.grpc.ItemInfo\022\026\n\016c" + "anonical_path\030\002 \001(\t\"<\n\020FetchFileRequest\022" + "\014\n\004path\030\001 \001(\t\022\021\n\004etag\030\002 \001(\tH\000\210\001\001B\007\n\005_eta" + "g\"A\n\021FetchFileResponse\022\020\n\010contents\030\001 \001(\014" + "\022\021\n\004etag\030\002 \001(\tH\000\210\001\001B\007\n\005_etag\"J\n\017SaveFile" + "Request\022\027\n\017allow_overwrite\030\001 \001(\010\022\014\n\004path" + "\030\002 \001(\t\022\020\n\010contents\030\003 \001(\014\".\n\020SaveFileResp" + "onse\022\021\n\004etag\030\001 \001(\tH\000\210\001\001B\007\n\005_etag\"N\n\017Move" + "ItemRequest\022\020\n\010old_path\030\001 \001(\t\022\020\n\010new_pat" + "h\030\002 \001(\t\022\027\n\017allow_overwrite\030\003 \001(\010\"\022\n\020Move" + "ItemResponse\"&\n\026CreateDirectoryRequest\022\014" + "\n\004path\030\001 \001(\t\"\031\n\027CreateDirectoryResponse\"" + "!\n\021DeleteItemRequest\022\014\n\004path\030\001 \001(\t\"\024\n\022De" + "leteItemResponse*0\n\010ItemType\022\013\n\007UNKNOWN\020" + "\000\022\r\n\tDIRECTORY\020\001\022\010\n\004FILE\020\0022\374\005\n\016StorageSe" + "rvice\022x\n\tListItems\0223.io.deephaven.proto." + "backplane.grpc.ListItemsRequest\0324.io.dee" + "phaven.proto.backplane.grpc.ListItemsRes" + "ponse\"\000\022x\n\tFetchFile\0223.io.deephaven.prot" + "o.backplane.grpc.FetchFileRequest\0324.io.d" + "eephaven.proto.backplane.grpc.FetchFileR" + "esponse\"\000\022u\n\010SaveFile\0222.io.deephaven.pro" + "to.backplane.grpc.SaveFileRequest\0323.io.d" + "eephaven.proto.backplane.grpc.SaveFileRe" + "sponse\"\000\022u\n\010MoveItem\0222.io.deephaven.prot" + "o.backplane.grpc.MoveItemRequest\0323.io.de" + "ephaven.proto.backplane.grpc.MoveItemRes" + "ponse\"\000\022\212\001\n\017CreateDirectory\0229.io.deephav" + "en.proto.backplane.grpc.CreateDirectoryR" + "equest\032:.io.deephaven.proto.backplane.gr" + "pc.CreateDirectoryResponse\"\000\022{\n\nDeleteIt" + "em\0224.io.deephaven.proto.backplane.grpc.D" + "eleteItemRequest\0325.io.deephaven.proto.ba" + "ckplane.grpc.DeleteItemResponse\"\000BCH\001P\001Z" + "=github.com/deephaven/deephaven-core/go/" + "internal/proto/storageb\006proto3" }; static ::absl::once_flag descriptor_table_deephaven_2fproto_2fstorage_2eproto_once; const ::_pbi::DescriptorTable descriptor_table_deephaven_2fproto_2fstorage_2eproto = { false, false, - 1726, + 1750, descriptor_table_protodef_deephaven_2fproto_2fstorage_2eproto, "deephaven/proto/storage.proto", &descriptor_table_deephaven_2fproto_2fstorage_2eproto_once, @@ -1114,6 +1118,7 @@ inline PROTOBUF_NDEBUG_INLINE ListItemsResponse::Impl_::Impl_( ::google::protobuf::internal::InternalVisibility visibility, ::google::protobuf::Arena* arena, const Impl_& from) : items_{visibility, arena, from.items_}, + canonical_path_(arena, from.canonical_path_), _cached_size_{0} {} ListItemsResponse::ListItemsResponse( @@ -1132,6 +1137,7 @@ inline PROTOBUF_NDEBUG_INLINE ListItemsResponse::Impl_::Impl_( ::google::protobuf::internal::InternalVisibility visibility, ::google::protobuf::Arena* arena) : items_{visibility, arena}, + canonical_path_(arena), _cached_size_{0} {} inline void ListItemsResponse::SharedCtor(::_pb::Arena* arena) { @@ -1144,6 +1150,7 @@ ListItemsResponse::~ListItemsResponse() { } inline void ListItemsResponse::SharedDtor() { ABSL_DCHECK(GetArena() == nullptr); + _impl_.canonical_path_.Destroy(); _impl_.~Impl_(); } @@ -1155,6 +1162,7 @@ PROTOBUF_NOINLINE void ListItemsResponse::Clear() { (void) cached_has_bits; _impl_.items_.Clear(); + _impl_.canonical_path_.ClearToEmpty(); _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>(); } @@ -1166,20 +1174,23 @@ const char* ListItemsResponse::_InternalParse( PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 -const ::_pbi::TcParseTable<0, 1, 1, 0, 2> ListItemsResponse::_table_ = { +const ::_pbi::TcParseTable<1, 2, 1, 74, 2> ListItemsResponse::_table_ = { { 0, // no _has_bits_ 0, // no _extensions_ - 1, 0, // max_field_number, fast_idx_mask + 2, 8, // max_field_number, fast_idx_mask offsetof(decltype(_table_), field_lookup_table), - 4294967294, // skipmap + 4294967292, // skipmap offsetof(decltype(_table_), field_entries), - 1, // num_field_entries + 2, // num_field_entries 1, // num_aux_entries offsetof(decltype(_table_), aux_entries), &_ListItemsResponse_default_instance_._instance, ::_pbi::TcParser::GenericFallback, // fallback }, {{ + // string canonical_path = 2; + {::_pbi::TcParser::FastUS1, + {18, 63, 0, PROTOBUF_FIELD_OFFSET(ListItemsResponse, _impl_.canonical_path_)}}, // repeated .io.deephaven.proto.backplane.grpc.ItemInfo items = 1; {::_pbi::TcParser::FastMtR1, {10, 63, 0, PROTOBUF_FIELD_OFFSET(ListItemsResponse, _impl_.items_)}}, @@ -1189,9 +1200,15 @@ const ::_pbi::TcParseTable<0, 1, 1, 0, 2> ListItemsResponse::_table_ = { // repeated .io.deephaven.proto.backplane.grpc.ItemInfo items = 1; {PROTOBUF_FIELD_OFFSET(ListItemsResponse, _impl_.items_), 0, 0, (0 | ::_fl::kFcRepeated | ::_fl::kMessage | ::_fl::kTvTable)}, + // string canonical_path = 2; + {PROTOBUF_FIELD_OFFSET(ListItemsResponse, _impl_.canonical_path_), 0, 0, + (0 | ::_fl::kFcSingular | ::_fl::kUtf8String | ::_fl::kRepAString)}, }}, {{ {::_pbi::TcParser::GetTable<::io::deephaven::proto::backplane::grpc::ItemInfo>()}, }}, {{ + "\63\0\16\0\0\0\0\0" + "io.deephaven.proto.backplane.grpc.ListItemsResponse" + "canonical_path" }}, }; @@ -1210,6 +1227,14 @@ ::uint8_t* ListItemsResponse::_InternalSerialize( InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream); } + // string canonical_path = 2; + if (!this->_internal_canonical_path().empty()) { + const std::string& _s = this->_internal_canonical_path(); + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + _s.data(), static_cast(_s.length()), ::google::protobuf::internal::WireFormatLite::SERIALIZE, "io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path"); + target = stream->WriteStringMaybeAliased(2, _s, target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( @@ -1233,6 +1258,12 @@ ::size_t ListItemsResponse::ByteSizeLong() const { total_size += ::google::protobuf::internal::WireFormatLite::MessageSize(msg); } + // string canonical_path = 2; + if (!this->_internal_canonical_path().empty()) { + total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( + this->_internal_canonical_path()); + } + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); } @@ -1254,6 +1285,9 @@ void ListItemsResponse::MergeImpl(::google::protobuf::Message& to_msg, const ::g _this->_internal_mutable_items()->MergeFrom( from._internal_items()); + if (!from._internal_canonical_path().empty()) { + _this->_internal_set_canonical_path(from._internal_canonical_path()); + } _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_); } @@ -1273,8 +1307,11 @@ ::_pbi::CachedSize* ListItemsResponse::AccessCachedSize() const { } void ListItemsResponse::InternalSwap(ListItemsResponse* PROTOBUF_RESTRICT other) { using std::swap; + auto* arena = GetArena(); + ABSL_DCHECK_EQ(arena, other->GetArena()); _internal_metadata_.InternalSwap(&other->_internal_metadata_); _impl_.items_.InternalSwap(&other->_impl_.items_); + ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.canonical_path_, &other->_impl_.canonical_path_, arena); } ::google::protobuf::Metadata ListItemsResponse::GetMetadata() const { diff --git a/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.h b/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.h index 19c5b583bce..9d2a9b9d6a7 100644 --- a/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.h +++ b/cpp-client/deephaven/dhclient/proto/deephaven/proto/storage.pb.h @@ -2492,6 +2492,7 @@ class ListItemsResponse final : enum : int { kItemsFieldNumber = 1, + kCanonicalPathFieldNumber = 2, }; // repeated .io.deephaven.proto.backplane.grpc.ItemInfo items = 1; int items_size() const; @@ -2511,14 +2512,30 @@ class ListItemsResponse final : ::io::deephaven::proto::backplane::grpc::ItemInfo* add_items(); const ::google::protobuf::RepeatedPtrField< ::io::deephaven::proto::backplane::grpc::ItemInfo >& items() const; + // string canonical_path = 2; + void clear_canonical_path() ; + const std::string& canonical_path() const; + template + void set_canonical_path(Arg_&& arg, Args_... args); + std::string* mutable_canonical_path(); + PROTOBUF_NODISCARD std::string* release_canonical_path(); + void set_allocated_canonical_path(std::string* value); + + private: + const std::string& _internal_canonical_path() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_canonical_path( + const std::string& value); + std::string* _internal_mutable_canonical_path(); + + public: // @@protoc_insertion_point(class_scope:io.deephaven.proto.backplane.grpc.ListItemsResponse) private: class _Internal; friend class ::google::protobuf::internal::TcParser; static const ::google::protobuf::internal::TcParseTable< - 0, 1, 1, - 0, 2> + 1, 2, 1, + 74, 2> _table_; friend class ::google::protobuf::MessageLite; friend class ::google::protobuf::Arena; @@ -2535,6 +2552,7 @@ class ListItemsResponse final : inline explicit Impl_(::google::protobuf::internal::InternalVisibility visibility, ::google::protobuf::Arena* arena, const Impl_& from); ::google::protobuf::RepeatedPtrField< ::io::deephaven::proto::backplane::grpc::ItemInfo > items_; + ::google::protobuf::internal::ArenaStringPtr canonical_path_; mutable ::google::protobuf::internal::CachedSize _cached_size_; PROTOBUF_TSAN_DECLARE_MEMBER }; @@ -2909,6 +2927,59 @@ ListItemsResponse::_internal_mutable_items() { return &_impl_.items_; } +// string canonical_path = 2; +inline void ListItemsResponse::clear_canonical_path() { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + _impl_.canonical_path_.ClearToEmpty(); +} +inline const std::string& ListItemsResponse::canonical_path() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_get:io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path) + return _internal_canonical_path(); +} +template +inline PROTOBUF_ALWAYS_INLINE void ListItemsResponse::set_canonical_path(Arg_&& arg, + Args_... args) { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + ; + _impl_.canonical_path_.Set(static_cast(arg), args..., GetArena()); + // @@protoc_insertion_point(field_set:io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path) +} +inline std::string* ListItemsResponse::mutable_canonical_path() ABSL_ATTRIBUTE_LIFETIME_BOUND { + std::string* _s = _internal_mutable_canonical_path(); + // @@protoc_insertion_point(field_mutable:io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path) + return _s; +} +inline const std::string& ListItemsResponse::_internal_canonical_path() const { + PROTOBUF_TSAN_READ(&_impl_._tsan_detect_race); + return _impl_.canonical_path_.Get(); +} +inline void ListItemsResponse::_internal_set_canonical_path(const std::string& value) { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + ; + _impl_.canonical_path_.Set(value, GetArena()); +} +inline std::string* ListItemsResponse::_internal_mutable_canonical_path() { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + ; + return _impl_.canonical_path_.Mutable( GetArena()); +} +inline std::string* ListItemsResponse::release_canonical_path() { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + // @@protoc_insertion_point(field_release:io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path) + return _impl_.canonical_path_.Release(); +} +inline void ListItemsResponse::set_allocated_canonical_path(std::string* value) { + PROTOBUF_TSAN_WRITE(&_impl_._tsan_detect_race); + _impl_.canonical_path_.SetAllocated(value, GetArena()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.canonical_path_.IsDefault()) { + _impl_.canonical_path_.Set("", GetArena()); + } + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:io.deephaven.proto.backplane.grpc.ListItemsResponse.canonical_path) +} + // ------------------------------------------------------------------- // FetchFileRequest diff --git a/go/internal/proto/storage/storage.pb.go b/go/internal/proto/storage/storage.pb.go index fbaab95d99a..7744648cb62 100644 --- a/go/internal/proto/storage/storage.pb.go +++ b/go/internal/proto/storage/storage.pb.go @@ -214,6 +214,9 @@ type ListItemsResponse struct { // List of items found in the specified directory. Items []*ItemInfo `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + // The canonical path of the listed directory. This is useful to recognize the basename + // of the items in a cross-platform way. + CanonicalPath string `protobuf:"bytes,2,opt,name=canonical_path,json=canonicalPath,proto3" json:"canonical_path,omitempty"` } func (x *ListItemsResponse) Reset() { @@ -255,6 +258,13 @@ func (x *ListItemsResponse) GetItems() []*ItemInfo { return nil } +func (x *ListItemsResponse) GetCanonicalPath() string { + if x != nil { + return x.CanonicalPath + } + return "" +} + type FetchFileRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -785,105 +795,108 @@ var file_deephaven_proto_storage_proto_rawDesc = []byte{ 0x70, 0x65, 0x12, 0x16, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x12, 0x42, 0x02, 0x30, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, - 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x56, 0x0a, 0x11, + 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x7d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x22, 0x48, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x17, 0x0a, 0x04, - 0x65, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x65, 0x74, - 0x61, 0x67, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x51, - 0x0a, 0x11, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, - 0x67, 0x22, 0x6a, 0x0a, 0x0f, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, - 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x34, 0x0a, - 0x10, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, - 0x74, 0x61, 0x67, 0x22, 0x70, 0x0a, 0x0f, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x6c, 0x64, 0x50, 0x61, 0x74, - 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x0f, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, - 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x27, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2a, 0x30, 0x0a, 0x08, 0x49, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x49, - 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, - 0x45, 0x10, 0x02, 0x32, 0xfc, 0x05, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x78, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, - 0x65, 0x6d, 0x73, 0x12, 0x33, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, - 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, - 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, - 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x78, 0x0a, 0x09, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x33, 0x2e, - 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x08, 0x53, 0x61, - 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x32, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, - 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x69, 0x6f, 0x2e, + 0x74, 0x65, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x61, + 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x22, 0x48, 0x0a, 0x10, 0x46, + 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x12, 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, + 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x51, 0x0a, 0x11, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x42, + 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x6a, 0x0a, 0x0f, 0x53, 0x61, 0x76, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x34, 0x0a, 0x10, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, + 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x22, 0x70, 0x0a, 0x0f, 0x4d, 0x6f, + 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x6c, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x12, 0x0a, 0x10, + 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x19, + 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x0a, 0x11, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x30, 0x0a, 0x08, 0x49, 0x74, 0x65, 0x6d, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, + 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x32, 0xfc, 0x05, 0x0a, 0x0e, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x78, 0x0a, + 0x09, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x33, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x75, 0x0a, 0x08, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x32, 0x2e, + 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x34, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x78, 0x0a, 0x09, 0x46, 0x65, 0x74, 0x63, 0x68, + 0x46, 0x69, 0x6c, 0x65, 0x12, 0x33, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, + 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, + 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, + 0x74, 0x63, 0x68, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x75, 0x0a, 0x08, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x32, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, - 0x63, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8a, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x39, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, - 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, - 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7b, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, - 0x74, 0x65, 0x6d, 0x12, 0x34, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, + 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x08, 0x4d, 0x6f, 0x76, 0x65, + 0x49, 0x74, 0x65, 0x6d, 0x12, 0x32, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, + 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x49, 0x74, 0x65, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, + 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, + 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x76, + 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x8a, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x39, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, - 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, - 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x43, 0x48, 0x01, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2f, 0x64, - 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x67, 0x6f, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, + 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7b, 0x0a, 0x0a, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x34, 0x2e, 0x69, 0x6f, 0x2e, + 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x43, 0x48, 0x01, 0x50, 0x01, 0x5a, + 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x65, 0x70, + 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2f, 0x64, 0x65, 0x65, 0x70, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2d, + 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/props/configs/src/main/resources/dh-defaults.prop b/props/configs/src/main/resources/dh-defaults.prop index be2dcba5eb9..3b97d130368 100644 --- a/props/configs/src/main/resources/dh-defaults.prop +++ b/props/configs/src/main/resources/dh-defaults.prop @@ -50,12 +50,15 @@ http.session.durationMs=300000 AuthHandlers=io.deephaven.authentication.psk.PskAuthenticationHandler authentication.anonymous.warn=true +web.storage.layout.directory=/layouts +web.storage.notebook.directory=/notebooks + # List of configuration properties to provide to unauthenticated clients, so that they can decide how best to prove their # identity to the server. authentication.client.configuration.list=AuthHandlers # List of configuration properties to provide to authenticated clients, so they can interact with the server. -client.configuration.list=java.version,deephaven.version,barrage.version,http.session.durationMs +client.configuration.list=java.version,deephaven.version,barrage.version,http.session.durationMs,file.separator,web.storage.layout.directory,web.storage.notebook.directory # Version list to add to the configuration property list. Each `=`-delimited pair denotes a short name for a versioned # jar, and a class that is found in that jar. Any such keys will be made available to the client.configuration.list # as .version. diff --git a/props/test-configs/src/main/resources/dh-tests.prop b/props/test-configs/src/main/resources/dh-tests.prop index baff6680273..b8aa3bec295 100644 --- a/props/test-configs/src/main/resources/dh-tests.prop +++ b/props/test-configs/src/main/resources/dh-tests.prop @@ -94,6 +94,9 @@ BarrageMessageProducer.minSnapshotCellCount=50 BarrageMessageProducer.maxSnapshotCellCount=50 BarrageStreamGenerator.batchSize=4 +web.storage.layout.directory=/layouts +web.storage.notebook.directory=/notebooks + http.session.durationMs=300000 AuthHandlers=io.deephaven.auth.AnonymousAuthenticationHandler authentication.client.configuration.list= diff --git a/proto/proto-backplane-grpc/src/main/proto/deephaven/proto/storage.proto b/proto/proto-backplane-grpc/src/main/proto/deephaven/proto/storage.proto index 10b39633aa5..9bdf28af4ae 100644 --- a/proto/proto-backplane-grpc/src/main/proto/deephaven/proto/storage.proto +++ b/proto/proto-backplane-grpc/src/main/proto/deephaven/proto/storage.proto @@ -62,6 +62,10 @@ message ItemInfo { message ListItemsResponse { // List of items found in the specified directory. repeated ItemInfo items = 1; + + // The canonical path of the listed directory. This is useful to recognize the basename + // of the items in a cross-platform way. + string canonical_path = 2; } message FetchFileRequest { diff --git a/py/client/pydeephaven/proto/storage_pb2.py b/py/client/pydeephaven/proto/storage_pb2.py index 140664b4249..279758a8f9b 100644 --- a/py/client/pydeephaven/proto/storage_pb2.py +++ b/py/client/pydeephaven/proto/storage_pb2.py @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x64\x65\x65phaven/proto/storage.proto\x12!io.deephaven.proto.backplane.grpc\"J\n\x10ListItemsRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x18\n\x0b\x66ilter_glob\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_filter_glob\"\x81\x01\n\x08ItemInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x39\n\x04type\x18\x02 \x01(\x0e\x32+.io.deephaven.proto.backplane.grpc.ItemType\x12\x10\n\x04size\x18\x03 \x01(\x12\x42\x02\x30\x01\x12\x11\n\x04\x65tag\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"O\n\x11ListItemsResponse\x12:\n\x05items\x18\x01 \x03(\x0b\x32+.io.deephaven.proto.backplane.grpc.ItemInfo\"<\n\x10\x46\x65tchFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x11\n\x04\x65tag\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"A\n\x11\x46\x65tchFileResponse\x12\x10\n\x08\x63ontents\x18\x01 \x01(\x0c\x12\x11\n\x04\x65tag\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"J\n\x0fSaveFileRequest\x12\x17\n\x0f\x61llow_overwrite\x18\x01 \x01(\x08\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x10\n\x08\x63ontents\x18\x03 \x01(\x0c\".\n\x10SaveFileResponse\x12\x11\n\x04\x65tag\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"N\n\x0fMoveItemRequest\x12\x10\n\x08old_path\x18\x01 \x01(\t\x12\x10\n\x08new_path\x18\x02 \x01(\t\x12\x17\n\x0f\x61llow_overwrite\x18\x03 \x01(\x08\"\x12\n\x10MoveItemResponse\"&\n\x16\x43reateDirectoryRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x19\n\x17\x43reateDirectoryResponse\"!\n\x11\x44\x65leteItemRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x14\n\x12\x44\x65leteItemResponse*0\n\x08ItemType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\r\n\tDIRECTORY\x10\x01\x12\x08\n\x04\x46ILE\x10\x02\x32\xfc\x05\n\x0eStorageService\x12x\n\tListItems\x12\x33.io.deephaven.proto.backplane.grpc.ListItemsRequest\x1a\x34.io.deephaven.proto.backplane.grpc.ListItemsResponse\"\x00\x12x\n\tFetchFile\x12\x33.io.deephaven.proto.backplane.grpc.FetchFileRequest\x1a\x34.io.deephaven.proto.backplane.grpc.FetchFileResponse\"\x00\x12u\n\x08SaveFile\x12\x32.io.deephaven.proto.backplane.grpc.SaveFileRequest\x1a\x33.io.deephaven.proto.backplane.grpc.SaveFileResponse\"\x00\x12u\n\x08MoveItem\x12\x32.io.deephaven.proto.backplane.grpc.MoveItemRequest\x1a\x33.io.deephaven.proto.backplane.grpc.MoveItemResponse\"\x00\x12\x8a\x01\n\x0f\x43reateDirectory\x12\x39.io.deephaven.proto.backplane.grpc.CreateDirectoryRequest\x1a:.io.deephaven.proto.backplane.grpc.CreateDirectoryResponse\"\x00\x12{\n\nDeleteItem\x12\x34.io.deephaven.proto.backplane.grpc.DeleteItemRequest\x1a\x35.io.deephaven.proto.backplane.grpc.DeleteItemResponse\"\x00\x42\x43H\x01P\x01Z=github.com/deephaven/deephaven-core/go/internal/proto/storageb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x64\x65\x65phaven/proto/storage.proto\x12!io.deephaven.proto.backplane.grpc\"J\n\x10ListItemsRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x18\n\x0b\x66ilter_glob\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_filter_glob\"\x81\x01\n\x08ItemInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x39\n\x04type\x18\x02 \x01(\x0e\x32+.io.deephaven.proto.backplane.grpc.ItemType\x12\x10\n\x04size\x18\x03 \x01(\x12\x42\x02\x30\x01\x12\x11\n\x04\x65tag\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"g\n\x11ListItemsResponse\x12:\n\x05items\x18\x01 \x03(\x0b\x32+.io.deephaven.proto.backplane.grpc.ItemInfo\x12\x16\n\x0e\x63\x61nonical_path\x18\x02 \x01(\t\"<\n\x10\x46\x65tchFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x11\n\x04\x65tag\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"A\n\x11\x46\x65tchFileResponse\x12\x10\n\x08\x63ontents\x18\x01 \x01(\x0c\x12\x11\n\x04\x65tag\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"J\n\x0fSaveFileRequest\x12\x17\n\x0f\x61llow_overwrite\x18\x01 \x01(\x08\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x10\n\x08\x63ontents\x18\x03 \x01(\x0c\".\n\x10SaveFileResponse\x12\x11\n\x04\x65tag\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_etag\"N\n\x0fMoveItemRequest\x12\x10\n\x08old_path\x18\x01 \x01(\t\x12\x10\n\x08new_path\x18\x02 \x01(\t\x12\x17\n\x0f\x61llow_overwrite\x18\x03 \x01(\x08\"\x12\n\x10MoveItemResponse\"&\n\x16\x43reateDirectoryRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x19\n\x17\x43reateDirectoryResponse\"!\n\x11\x44\x65leteItemRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x14\n\x12\x44\x65leteItemResponse*0\n\x08ItemType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\r\n\tDIRECTORY\x10\x01\x12\x08\n\x04\x46ILE\x10\x02\x32\xfc\x05\n\x0eStorageService\x12x\n\tListItems\x12\x33.io.deephaven.proto.backplane.grpc.ListItemsRequest\x1a\x34.io.deephaven.proto.backplane.grpc.ListItemsResponse\"\x00\x12x\n\tFetchFile\x12\x33.io.deephaven.proto.backplane.grpc.FetchFileRequest\x1a\x34.io.deephaven.proto.backplane.grpc.FetchFileResponse\"\x00\x12u\n\x08SaveFile\x12\x32.io.deephaven.proto.backplane.grpc.SaveFileRequest\x1a\x33.io.deephaven.proto.backplane.grpc.SaveFileResponse\"\x00\x12u\n\x08MoveItem\x12\x32.io.deephaven.proto.backplane.grpc.MoveItemRequest\x1a\x33.io.deephaven.proto.backplane.grpc.MoveItemResponse\"\x00\x12\x8a\x01\n\x0f\x43reateDirectory\x12\x39.io.deephaven.proto.backplane.grpc.CreateDirectoryRequest\x1a:.io.deephaven.proto.backplane.grpc.CreateDirectoryResponse\"\x00\x12{\n\nDeleteItem\x12\x34.io.deephaven.proto.backplane.grpc.DeleteItemRequest\x1a\x35.io.deephaven.proto.backplane.grpc.DeleteItemResponse\"\x00\x42\x43H\x01P\x01Z=github.com/deephaven/deephaven-core/go/internal/proto/storageb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -24,34 +24,34 @@ _globals['DESCRIPTOR']._serialized_options = b'H\001P\001Z=github.com/deephaven/deephaven-core/go/internal/proto/storage' _globals['_ITEMINFO'].fields_by_name['size']._loaded_options = None _globals['_ITEMINFO'].fields_by_name['size']._serialized_options = b'0\001' - _globals['_ITEMTYPE']._serialized_start=834 - _globals['_ITEMTYPE']._serialized_end=882 + _globals['_ITEMTYPE']._serialized_start=858 + _globals['_ITEMTYPE']._serialized_end=906 _globals['_LISTITEMSREQUEST']._serialized_start=68 _globals['_LISTITEMSREQUEST']._serialized_end=142 _globals['_ITEMINFO']._serialized_start=145 _globals['_ITEMINFO']._serialized_end=274 _globals['_LISTITEMSRESPONSE']._serialized_start=276 - _globals['_LISTITEMSRESPONSE']._serialized_end=355 - _globals['_FETCHFILEREQUEST']._serialized_start=357 - _globals['_FETCHFILEREQUEST']._serialized_end=417 - _globals['_FETCHFILERESPONSE']._serialized_start=419 - _globals['_FETCHFILERESPONSE']._serialized_end=484 - _globals['_SAVEFILEREQUEST']._serialized_start=486 - _globals['_SAVEFILEREQUEST']._serialized_end=560 - _globals['_SAVEFILERESPONSE']._serialized_start=562 - _globals['_SAVEFILERESPONSE']._serialized_end=608 - _globals['_MOVEITEMREQUEST']._serialized_start=610 - _globals['_MOVEITEMREQUEST']._serialized_end=688 - _globals['_MOVEITEMRESPONSE']._serialized_start=690 - _globals['_MOVEITEMRESPONSE']._serialized_end=708 - _globals['_CREATEDIRECTORYREQUEST']._serialized_start=710 - _globals['_CREATEDIRECTORYREQUEST']._serialized_end=748 - _globals['_CREATEDIRECTORYRESPONSE']._serialized_start=750 - _globals['_CREATEDIRECTORYRESPONSE']._serialized_end=775 - _globals['_DELETEITEMREQUEST']._serialized_start=777 - _globals['_DELETEITEMREQUEST']._serialized_end=810 - _globals['_DELETEITEMRESPONSE']._serialized_start=812 - _globals['_DELETEITEMRESPONSE']._serialized_end=832 - _globals['_STORAGESERVICE']._serialized_start=885 - _globals['_STORAGESERVICE']._serialized_end=1649 + _globals['_LISTITEMSRESPONSE']._serialized_end=379 + _globals['_FETCHFILEREQUEST']._serialized_start=381 + _globals['_FETCHFILEREQUEST']._serialized_end=441 + _globals['_FETCHFILERESPONSE']._serialized_start=443 + _globals['_FETCHFILERESPONSE']._serialized_end=508 + _globals['_SAVEFILEREQUEST']._serialized_start=510 + _globals['_SAVEFILEREQUEST']._serialized_end=584 + _globals['_SAVEFILERESPONSE']._serialized_start=586 + _globals['_SAVEFILERESPONSE']._serialized_end=632 + _globals['_MOVEITEMREQUEST']._serialized_start=634 + _globals['_MOVEITEMREQUEST']._serialized_end=712 + _globals['_MOVEITEMRESPONSE']._serialized_start=714 + _globals['_MOVEITEMRESPONSE']._serialized_end=732 + _globals['_CREATEDIRECTORYREQUEST']._serialized_start=734 + _globals['_CREATEDIRECTORYREQUEST']._serialized_end=772 + _globals['_CREATEDIRECTORYRESPONSE']._serialized_start=774 + _globals['_CREATEDIRECTORYRESPONSE']._serialized_end=799 + _globals['_DELETEITEMREQUEST']._serialized_start=801 + _globals['_DELETEITEMREQUEST']._serialized_end=834 + _globals['_DELETEITEMRESPONSE']._serialized_start=836 + _globals['_DELETEITEMRESPONSE']._serialized_end=856 + _globals['_STORAGESERVICE']._serialized_start=909 + _globals['_STORAGESERVICE']._serialized_end=1673 # @@protoc_insertion_point(module_scope) diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java index b9c7b79dd93..c8dacf941d8 100644 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -35,6 +35,7 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.DirectoryNotEmptyException; @@ -70,9 +71,9 @@ public class FilesystemStorageServiceGrpcImpl extends StorageServiceGrpc.Storage DataDir.get().resolve("storage").toString()); private static final String WEB_LAYOUT_DIRECTORY = - Configuration.getInstance().getStringWithDefault("web.storage.layout.directory", "/layouts"); + Configuration.getInstance().getProperty("web.storage.layout.directory"); private static final String WEB_NOTEBOOK_DIRECTORY = - Configuration.getInstance().getStringWithDefault("web.storage.notebook.directory", "/notebooks"); + Configuration.getInstance().getProperty("web.storage.notebook.directory"); private static final String[] PRE_CREATE_PATHS = Configuration.getInstance() .getStringArrayFromPropertyWithDefault("storage.path.defaults", new String[] { WEB_LAYOUT_DIRECTORY, @@ -86,6 +87,12 @@ public class FilesystemStorageServiceGrpcImpl extends StorageServiceGrpc.Storage */ private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0); + /** + * Presently, the Web IDE requires that all paths start with "/". When this is no longer true, remove this constant. + */ + @Deprecated + private static final String REQUIRED_PATH_PREFIX = "/"; + private final Path root = Paths.get(STORAGE_PATH).normalize(); private final SessionService sessionService; private final SessionService.ErrorTransformer errorTransformer; @@ -107,11 +114,12 @@ public FilesystemStorageServiceGrpcImpl( } private Path resolveOrThrow(String incomingPath) { - if (incomingPath.startsWith("/")) { - Path resolved = root.resolve(incomingPath.substring(1)).normalize(); - if (resolved.startsWith(root)) { - return resolved; - } + if (incomingPath.startsWith(File.separator)) { + incomingPath = incomingPath.substring(1); + } + Path resolved = root.resolve(incomingPath).normalize(); + if (resolved.startsWith(root)) { + return resolved; } throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Invalid path: " + incomingPath); } @@ -132,6 +140,7 @@ public void listItems( PathMatcher matcher = request.hasFilterGlob() ? createPathFilter(request.getFilterGlob()) : ignore -> true; Path dir = resolveOrThrow(request.getPath()); + builder.setCanonicalPath(REQUIRED_PATH_PREFIX + root.relativize(dir)); try (Stream list = Files.list(dir)) { for (Path p : (Iterable) list::iterator) { if (!matcher.matches(dir.relativize(p))) { @@ -140,7 +149,7 @@ public void listItems( BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class); boolean isDirectory = attrs.isDirectory(); ItemInfo.Builder info = ItemInfo.newBuilder() - .setPath("/" + root.relativize(p)); + .setPath(REQUIRED_PATH_PREFIX + root.relativize(p)); if (isDirectory) { info.setType(ItemType.DIRECTORY); } else { @@ -163,7 +172,7 @@ private static PathMatcher createPathFilter(String filterGlob) { if (filterGlob.contains("**")) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only single `*`s are supported"); } - if (filterGlob.contains("/")) { + if (filterGlob.contains(File.separator)) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only the same directory can be checked"); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsItemDetails.java b/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsItemDetails.java index 3d45dc0fe7e..dcc26bac3a8 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsItemDetails.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsItemDetails.java @@ -7,6 +7,7 @@ import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.Storage_pb; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.storage_pb.ItemInfo; import jsinterop.annotations.JsIgnore; +import jsinterop.annotations.JsMethod; import jsinterop.annotations.JsProperty; import jsinterop.annotations.JsType; @@ -15,13 +16,15 @@ */ @JsType(namespace = "dh.storage", name = "ItemDetails") public class JsItemDetails { + private final String parentPath; private final String path; private final int type; private final String size; private final String etag; @JsIgnore - public JsItemDetails(String path, int kind, String size, String etag) { + public JsItemDetails(String parentPath, String path, int kind, String size, String etag) { + this.parentPath = parentPath; this.path = path; this.type = kind; this.size = size; @@ -29,8 +32,8 @@ public JsItemDetails(String path, int kind, String size, String etag) { } @JsIgnore - public static JsItemDetails fromProto(ItemInfo item) { - return new JsItemDetails(item.getPath(), item.getType(), item.getSize(), item.getEtag()); + public static JsItemDetails fromProto(String parentPath, ItemInfo item) { + return new JsItemDetails(parentPath, item.getPath(), item.getType(), item.getSize(), item.getEtag()); } @JsProperty @@ -40,12 +43,16 @@ public String getFilename() { @JsProperty public String getBasename() { - return path.substring(path.lastIndexOf('/') + 1); + // TODO (deephaven-core#5068) remove extra check + if (parentPath.equals("/")) { + return path.substring(1); + } + return path.substring(parentPath.length() + 1); } @JsProperty public String getDirname() { - return path.substring(0, path.lastIndexOf('/')); + return parentPath; } @JsProperty @@ -63,4 +70,10 @@ public double getSize() { public String getEtag() { return etag; } + + @JsMethod + @Override + public String toString() { + return getBasename(); + } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsStorageService.java b/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsStorageService.java index f83c5731910..0ec927df9ee 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsStorageService.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/storage/JsStorageService.java @@ -64,7 +64,8 @@ public Promise> listItems(String path, @JsOptional String req.setFilterGlob(glob); return Callbacks.grpcUnaryPromise(c -> client().listItems(req, metadata(), c::apply)) .then(response -> Promise - .resolve(response.getItemsList().map((item, i, arr) -> JsItemDetails.fromProto(item)))); + .resolve(response.getItemsList() + .map((item, i, arr) -> JsItemDetails.fromProto(response.getCanonicalPath(), item)))); } /** diff --git a/web/client-api/src/test/java/io/deephaven/web/ClientIntegrationTestSuite.java b/web/client-api/src/test/java/io/deephaven/web/ClientIntegrationTestSuite.java index 635cbaaee7e..077edddb63a 100644 --- a/web/client-api/src/test/java/io/deephaven/web/ClientIntegrationTestSuite.java +++ b/web/client-api/src/test/java/io/deephaven/web/ClientIntegrationTestSuite.java @@ -7,6 +7,7 @@ import io.deephaven.web.client.api.HierarchicalTableTestGwt; import io.deephaven.web.client.api.NullValueTestGwt; import io.deephaven.web.client.api.PartitionedTableTestGwt; +import io.deephaven.web.client.api.storage.JsStorageServiceTestGwt; import io.deephaven.web.client.api.subscription.ConcurrentTableTestGwt; import io.deephaven.web.client.api.TableManipulationTestGwt; import io.deephaven.web.client.api.subscription.ViewportTestGwt; @@ -29,6 +30,7 @@ public static Test suite() { suite.addTestSuite(NullValueTestGwt.class); suite.addTestSuite(HierarchicalTableTestGwt.class); suite.addTestSuite(PartitionedTableTestGwt.class); + suite.addTestSuite(JsStorageServiceTestGwt.class); // This should be a unit test, but it requires a browser environment to run on GWT 2.9 // GWT 2.9 doesn't have proper bindings for Promises in HtmlUnit, so we need to use the IntegrationTest suite diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java b/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java index 0035fa5ad05..7b36f3b98a5 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java @@ -250,9 +250,9 @@ protected Promise assertUpdateReceived(JsTable table, Consumer IThenable.ThenOnFulfilledCallbackFn delayFinish(int timeout) { - return table -> { + return object -> { delayTestFinish(timeout); - return Promise.resolve(table); + return Promise.resolve(object); }; } diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/storage/JsStorageServiceTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/storage/JsStorageServiceTestGwt.java new file mode 100644 index 00000000000..d37b03d4728 --- /dev/null +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/storage/JsStorageServiceTestGwt.java @@ -0,0 +1,142 @@ +// +// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.storage; + +import elemental2.promise.Promise; +import io.deephaven.web.client.api.AbstractAsyncGwtTestCase; +import io.deephaven.web.client.api.CoreClient; +import jsinterop.base.JsPropertyMap; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class JsStorageServiceTestGwt extends AbstractAsyncGwtTestCase { + + public void testStorageService() { + setupDhInternal().then(ignore -> { + CoreClient coreClient = new CoreClient(localServer, null); + return coreClient.login(JsPropertyMap.of("type", CoreClient.LOGIN_TYPE_ANONYMOUS)) + .then(ignore2 -> Promise.resolve(coreClient)); + }).then(coreClient -> { + JsStorageService storageService = coreClient.getStorageService(); + delayTestFinish(20_000); + + // On startup, there are only default items + return storageService.listItems("", null).then(items -> { + assertEquals(items.toString_(), 2, items.length); + + // TODO (deephaven-core#5068) remove leading slash + Set expected = new HashSet<>(Arrays.asList("layouts", "notebooks")); + for (int i = 0; i < items.length; i++) { + expected.remove(items.getAt(i).getBasename()); + } + assertTrue(expected.toString(), expected.isEmpty()); + + delayTestFinish(20_001); + return storageService.createDirectory("notebooks/myDir"); + }).then(ignore -> { + delayTestFinish(20_002); + + return Promise.all(storageService.listItems("notebooks", null).then(items -> { + delayTestFinish(20_003); + + assertEquals(1, items.length); + + assertEquals(JsItemType.DIRECTORY, items.getAt(0).getType()); + assertEquals("myDir", items.getAt(0).getBasename()); + + return null; + }), storageService.listItems("/notebooks", null).then(items -> { + delayTestFinish(20_004); + + assertEquals(1, items.length); + + assertEquals(JsItemType.DIRECTORY, items.getAt(0).getType()); + assertEquals("myDir", items.getAt(0).getBasename()); + return null; + }), storageService.listItems("notebooks/", null).then(items -> { + delayTestFinish(20_005); + + assertEquals(1, items.length); + + assertEquals(JsItemType.DIRECTORY, items.getAt(0).getType()); + assertEquals("myDir", items.getAt(0).getBasename()); + return null; + }), storageService.listItems("/notebooks/", null).then(items -> { + delayTestFinish(20_006); + + assertEquals(1, items.length); + + assertEquals(JsItemType.DIRECTORY, items.getAt(0).getType()); + assertEquals("myDir", items.getAt(0).getBasename()); + return null; + })); + }).then(ignore -> storageService + .saveFile("layouts/myFile.txt", JsFileContents.text("hello, ", "world"), false) + .then(file -> { + delayTestFinish(20_007); + + assertEquals("8ebc5e3a62ac2f344d41429607bcdc4c", file.getEtag()); + return Promise.all(storageService.listItems("layouts", null) + .then(items -> { + delayTestFinish(20_008); + + assertEquals(JsItemType.FILE, items.getAt(0).getType()); + assertEquals("myFile.txt", items.getAt(0).getBasename()); + return null; + }), storageService.loadFile("layouts/myFile.txt", null).then(contents -> { + delayTestFinish(20_009); + + return contents.text().then(c -> { + delayTestFinish(20_010); + + assertEquals("hello, world", c); + return null; + }); + }), storageService.loadFile("/layouts/myFile.txt", null).then(contents -> { + delayTestFinish(20_011); + + return contents.text().then(c -> { + delayTestFinish(20_012); + + assertEquals("hello, world", c); + return null; + }); + }), storageService.loadFile("layouts/myFile.txt", "8ebc5e3a62ac2f344d41429607bcdc4c") + .then(contents -> { + delayTestFinish(20_013); + + return contents.text().then(c -> { + fail("should not have resolved"); + return null; + }, error -> { + assertTrue(error.toString() + .contains("No contents available, please use provided etag")); + return null; + }); + }), + storageService.loadFile("layouts/myFile.txt", "definitely-the-wrong-hash") + .then(contents -> { + delayTestFinish(20_014); + + return contents.text().then(c -> { + delayTestFinish(20_015); + + assertEquals("hello, world", c); + return null; + }); + })); + })).then(ignore -> { + finishTest(); + return null; + }).catch_(this::report); + }); + } + + @Override + public String getModuleName() { + return "io.deephaven.web.DeephavenIntegrationTest"; + } +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven/proto/storage_pb/ListItemsResponse.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven/proto/storage_pb/ListItemsResponse.java index 2cdbbfeb7bf..f6a3c7376ce 100644 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven/proto/storage_pb/ListItemsResponse.java +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven/proto/storage_pb/ListItemsResponse.java @@ -56,9 +56,15 @@ static ListItemsResponse.ToObjectReturnType create() { return Js.uncheckedCast(JsPropertyMap.of()); } + @JsProperty + String getCanonicalPath(); + @JsProperty JsArray getItemsList(); + @JsProperty + void setCanonicalPath(String canonicalPath); + @JsOverlay default void setItemsList(ListItemsResponse.ToObjectReturnType.ItemsListFieldType[] itemsList) { setItemsList( @@ -109,9 +115,15 @@ static ListItemsResponse.ToObjectReturnType0 create() { return Js.uncheckedCast(JsPropertyMap.of()); } + @JsProperty + String getCanonicalPath(); + @JsProperty JsArray getItemsList(); + @JsProperty + void setCanonicalPath(String canonicalPath); + @JsOverlay default void setItemsList( ListItemsResponse.ToObjectReturnType0.ItemsListFieldType[] itemsList) { @@ -142,10 +154,14 @@ public static native ListItemsResponse.ToObjectReturnType toObject( public native void clearItemsList(); + public native String getCanonicalPath(); + public native JsArray getItemsList(); public native Uint8Array serializeBinary(); + public native void setCanonicalPath(String value); + @JsOverlay public final void setItemsList(ItemInfo[] value) { setItemsList(Js.>uncheckedCast(value));