From 1db03dd5125cb9ddee269f6f567e3be2d382a717 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Mon, 19 Dec 2022 15:44:02 +0100 Subject: [PATCH] [full-ci] experimental tags backport (#5227) * add tags to search service resource add tags getTags, AssignTags and UnassignTags endpoint to graph use and prefer search event spaceOwner over executant add tags to search report response update libre graph api update reva Co-authored-by: David Christofas --- .../unreleased/add-global-vars-extrator.md | 2 +- changelog/unreleased/enhancement-search.md | 5 +- changelog/unreleased/enhancement-tags.md | 8 + go.mod | 4 +- go.sum | 8 +- .../gen/ocis/messages/search/v0/search.pb.go | 35 ++- .../ocis/messages/settings/v0/settings.pb.go | 2 + .../services/search/v0/search.swagger.json | 6 + .../ocis/messages/search/v0/search.proto | 1 + services/graph/pkg/server/http/server.go | 2 + services/graph/pkg/service/v0/graph.go | 3 + services/graph/pkg/service/v0/instrument.go | 15 ++ services/graph/pkg/service/v0/logging.go | 15 ++ services/graph/pkg/service/v0/option.go | 9 + services/graph/pkg/service/v0/service.go | 10 + services/graph/pkg/service/v0/tags.go | 228 ++++++++++++++++++ services/graph/pkg/service/v0/tracing.go | 15 ++ services/search/pkg/content/basic.go | 12 +- services/search/pkg/content/basic_test.go | 4 +- services/search/pkg/content/content.go | 2 +- .../search/pkg/content/mocks/extractor.go | 2 +- .../search/pkg/content/mocks/retriever.go | 2 +- services/search/pkg/engine/bleve.go | 6 +- services/search/pkg/engine/bleve_test.go | 4 +- services/search/pkg/engine/engine.go | 51 ++-- services/search/pkg/engine/mocks/engine.go | 2 +- services/search/pkg/search/events.go | 53 ++-- services/search/pkg/search/events_test.go | 7 +- services/search/pkg/search/mocks/searcher.go | 2 +- services/webdav/pkg/service/v0/search.go | 7 + 30 files changed, 434 insertions(+), 88 deletions(-) create mode 100644 changelog/unreleased/enhancement-tags.md create mode 100644 services/graph/pkg/service/v0/tags.go diff --git a/changelog/unreleased/add-global-vars-extrator.md b/changelog/unreleased/add-global-vars-extrator.md index 80d01b0425..c56a1b6f58 100644 --- a/changelog/unreleased/add-global-vars-extrator.md +++ b/changelog/unreleased/add-global-vars-extrator.md @@ -3,5 +3,5 @@ Enhancement: add global env variable extractor We have added a little tool that will extract global env vars, that are loaded only through os.Getenv for documentation purposes -https://github.com/owncloud/ocis/issues/4916 https://github.com/owncloud/ocis/pull/5164 +https://github.com/owncloud/ocis/issues/4916 diff --git a/changelog/unreleased/enhancement-search.md b/changelog/unreleased/enhancement-search.md index 699b81871b..0b69333852 100644 --- a/changelog/unreleased/enhancement-search.md +++ b/changelog/unreleased/enhancement-search.md @@ -1,8 +1,9 @@ -Bugfix: Enhancement search +Enhancement: extended search -Provides multiple enhancement to the current search implementation. +Provides multiple enhancement to the search implementation. * content extraction, search now supports apache tika to extract resource contents. * search engine, underlying search engine is swappable now. * event consumers, the number of event consumers can now be set, which improves the speed of the individual tasks https://github.com/owncloud/ocis/pull/5221 +https://github.com/owncloud/ocis/issues/5184 diff --git a/changelog/unreleased/enhancement-tags.md b/changelog/unreleased/enhancement-tags.md new file mode 100644 index 0000000000..647b2340f8 --- /dev/null +++ b/changelog/unreleased/enhancement-tags.md @@ -0,0 +1,8 @@ +Enhancement: resource tags + +We've added the ability to tag resources via the graph api. +Tags can be added (put request) and removed (delete request) from a resource, +a list of available tags can also be requested by sending a get request to the graph endpoint. + +https://github.com/owncloud/ocis/pull/5227 +https://github.com/owncloud/ocis/issues/5184 diff --git a/go.mod b/go.mod index d8c2fee1e0..b2aea6f2fe 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/blevesearch/bleve/v2 v2.3.5 github.com/coreos/go-oidc/v3 v3.4.0 github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965 - github.com/cs3org/reva/v2 v2.12.1-0.20221214090401-47e0591bb902 + github.com/cs3org/reva/v2 v2.12.1-0.20221215082748-05a97d63f308 github.com/disintegration/imaging v1.6.2 github.com/ggwhite/go-masker v1.0.9 github.com/go-chi/chi/v5 v5.0.7 @@ -54,7 +54,7 @@ require ( github.com/onsi/ginkgo/v2 v2.5.0 github.com/onsi/gomega v1.24.1 github.com/orcaman/concurrent-map v1.0.0 - github.com/owncloud/libre-graph-api-go v1.0.0 + github.com/owncloud/libre-graph-api-go v1.0.1-0.20221216081114-57ab57ed98b0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/rs/zerolog v1.28.0 diff --git a/go.sum b/go.sum index 87e0c4c223..785f5dc1f5 100644 --- a/go.sum +++ b/go.sum @@ -343,8 +343,8 @@ github.com/crewjam/saml v0.4.6 h1:XCUFPkQSJLvzyl4cW9OvpWUbRf0gE7VUpU8ZnilbeM4= github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A= github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965 h1:y4n2j68LLnvac+zw/al8MfPgO5aQiIwLmHM/JzYN8AM= github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/reva/v2 v2.12.1-0.20221214090401-47e0591bb902 h1:r8K9y0RMFXjQlrbx17iQziWYhNyAYmh70ixaXbQHsHY= -github.com/cs3org/reva/v2 v2.12.1-0.20221214090401-47e0591bb902/go.mod h1:GpocVB1w6yxeSr1VBsO9jztmt1SyNC4lCwudLwDzxHQ= +github.com/cs3org/reva/v2 v2.12.1-0.20221215082748-05a97d63f308 h1:9MePXhcJ39iQGnI7ojNlqrFrPv8B+dCa4EnlGtQEbn4= +github.com/cs3org/reva/v2 v2.12.1-0.20221215082748-05a97d63f308/go.mod h1:GpocVB1w6yxeSr1VBsO9jztmt1SyNC4lCwudLwDzxHQ= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -1054,8 +1054,8 @@ github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35uk github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= -github.com/owncloud/libre-graph-api-go v1.0.0 h1:3uu5Thr9uxRkF6yak91TKydtvvvG8j9LbfirYIh+hcM= -github.com/owncloud/libre-graph-api-go v1.0.0/go.mod h1:579sFrPP7aP24LZXGPopLfvE+hAka/2DYHk0+Ij+w+U= +github.com/owncloud/libre-graph-api-go v1.0.1-0.20221216081114-57ab57ed98b0 h1:47N8o/5MmQ5781dF44fk1lQtyFmTAvu6/5hPG3rM1TQ= +github.com/owncloud/libre-graph-api-go v1.0.1-0.20221216081114-57ab57ed98b0/go.mod h1:579sFrPP7aP24LZXGPopLfvE+hAka/2DYHk0+Ij+w+U= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= diff --git a/protogen/gen/ocis/messages/search/v0/search.pb.go b/protogen/gen/ocis/messages/search/v0/search.pb.go index 1fbd76c007..68cc77dec6 100644 --- a/protogen/gen/ocis/messages/search/v0/search.pb.go +++ b/protogen/gen/ocis/messages/search/v0/search.pb.go @@ -156,6 +156,7 @@ type Entity struct { Deleted bool `protobuf:"varint,10,opt,name=deleted,proto3" json:"deleted,omitempty"` ShareRootName string `protobuf:"bytes,11,opt,name=shareRootName,proto3" json:"shareRootName,omitempty"` ParentId *ResourceID `protobuf:"bytes,12,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` + Tags []string `protobuf:"bytes,13,rep,name=tags,proto3" json:"tags,omitempty"` } func (x *Entity) Reset() { @@ -274,6 +275,13 @@ func (x *Entity) GetParentId() *ResourceID { return nil } +func (x *Entity) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + type Match struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -352,7 +360,7 @@ var file_ocis_messages_search_v0_search_proto_rawDesc = []byte{ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xce, 0x03, 0x0a, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xe2, 0x03, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, @@ -381,18 +389,19 @@ var file_ocis_messages_search_v0_search_proto_rawDesc = []byte{ 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x49, 0x44, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x56, 0x0a, - 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, - 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, - 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, - 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, - 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, - 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x49, 0x44, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, + 0x73, 0x22, 0x56, 0x0a, 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x63, 0x69, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, + 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protogen/gen/ocis/messages/settings/v0/settings.pb.go b/protogen/gen/ocis/messages/settings/v0/settings.pb.go index ede907dba3..e576c87bbd 100644 --- a/protogen/gen/ocis/messages/settings/v0/settings.pb.go +++ b/protogen/gen/ocis/messages/settings/v0/settings.pb.go @@ -1193,6 +1193,7 @@ type Value struct { AccountUuid string `protobuf:"bytes,4,opt,name=account_uuid,json=accountUuid,proto3" json:"account_uuid,omitempty"` Resource *Resource `protobuf:"bytes,5,opt,name=resource,proto3" json:"resource,omitempty"` // Types that are assignable to Value: + // // *Value_BoolValue // *Value_IntValue // *Value_StringValue @@ -1383,6 +1384,7 @@ type ListOptionValue struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Option: + // // *ListOptionValue_StringValue // *ListOptionValue_IntValue Option isListOptionValue_Option `protobuf_oneof:"option"` diff --git a/protogen/gen/ocis/services/search/v0/search.swagger.json b/protogen/gen/ocis/services/search/v0/search.swagger.json index 2ba276bda0..38e9809e82 100644 --- a/protogen/gen/ocis/services/search/v0/search.swagger.json +++ b/protogen/gen/ocis/services/search/v0/search.swagger.json @@ -198,6 +198,12 @@ }, "parentId": { "$ref": "#/definitions/v0ResourceID" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/protogen/proto/ocis/messages/search/v0/search.proto b/protogen/proto/ocis/messages/search/v0/search.proto index 8fc5d9e7aa..dbba26769c 100644 --- a/protogen/proto/ocis/messages/search/v0/search.proto +++ b/protogen/proto/ocis/messages/search/v0/search.proto @@ -30,6 +30,7 @@ message Entity { bool deleted = 10; string shareRootName = 11; ResourceID parent_id = 12; + repeated string tags = 13; } message Match { diff --git a/services/graph/pkg/server/http/server.go b/services/graph/pkg/server/http/server.go index 5e7bb62f5e..8951c58531 100644 --- a/services/graph/pkg/server/http/server.go +++ b/services/graph/pkg/server/http/server.go @@ -17,6 +17,7 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/service/http" "github.com/owncloud/ocis/v2/ocis-pkg/version" + searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" graphMiddleware "github.com/owncloud/ocis/v2/services/graph/pkg/middleware" svc "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" @@ -129,6 +130,7 @@ func Server(opts ...Option) (http.Service, error) { svc.WithRoleService(roleService), svc.WithRequireAdminMiddleware(requireAdminMiddleware), svc.WithGatewayClient(gatewayClient), + svc.WithSearchService(searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient())), ) if handle == nil { diff --git a/services/graph/pkg/service/v0/graph.go b/services/graph/pkg/service/v0/graph.go index ca218a128a..3ee2af8986 100644 --- a/services/graph/pkg/service/v0/graph.go +++ b/services/graph/pkg/service/v0/graph.go @@ -12,6 +12,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/jellydator/ttlcache/v2" "github.com/owncloud/ocis/v2/ocis-pkg/log" + searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/config" "github.com/owncloud/ocis/v2/services/graph/pkg/identity" @@ -57,6 +58,7 @@ type GatewayClient interface { // MUST return CODE_NOT_FOUND if the reference does not exist // MUST return CODE_RESOURCE_EXHAUSTED on exceeded quota limits. GetQuota(ctx context.Context, in *gateway.GetQuotaRequest, opts ...grpc.CallOption) (*provider.GetQuotaResponse, error) + SetArbitraryMetadata(ctx context.Context, request *provider.SetArbitraryMetadataRequest, opts ...grpc.CallOption) (*provider.SetArbitraryMetadataResponse, error) } // Publisher is the interface for events publisher @@ -97,6 +99,7 @@ type Graph struct { permissionsService Permissions spacePropertiesCache *ttlcache.Cache eventsPublisher events.Publisher + searchService searchsvc.SearchProviderService } // ServeHTTP implements the Service interface. diff --git a/services/graph/pkg/service/v0/instrument.go b/services/graph/pkg/service/v0/instrument.go index 351d90aa65..42d5730750 100644 --- a/services/graph/pkg/service/v0/instrument.go +++ b/services/graph/pkg/service/v0/instrument.go @@ -133,3 +133,18 @@ func (i instrument) CreateDrive(w http.ResponseWriter, r *http.Request) { func (i instrument) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { i.next.GetRootDriveChildren(w, r) } + +// GetTags implements the Service interface. +func (i instrument) GetTags(w http.ResponseWriter, r *http.Request) { + i.next.GetTags(w, r) +} + +// AssignTags implements the Service interface. +func (i instrument) AssignTags(w http.ResponseWriter, r *http.Request) { + i.next.AssignTags(w, r) +} + +// UnassignTags implements the Service interface. +func (i instrument) UnassignTags(w http.ResponseWriter, r *http.Request) { + i.next.UnassignTags(w, r) +} diff --git a/services/graph/pkg/service/v0/logging.go b/services/graph/pkg/service/v0/logging.go index 0beaf93b1b..316bbd55b8 100644 --- a/services/graph/pkg/service/v0/logging.go +++ b/services/graph/pkg/service/v0/logging.go @@ -133,3 +133,18 @@ func (l logging) CreateDrive(w http.ResponseWriter, r *http.Request) { func (l logging) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { l.next.GetRootDriveChildren(w, r) } + +// GetTags implements the Service interface. +func (l logging) GetTags(w http.ResponseWriter, r *http.Request) { + l.next.GetTags(w, r) +} + +// AssignTags implements the Service interface. +func (l logging) AssignTags(w http.ResponseWriter, r *http.Request) { + l.next.AssignTags(w, r) +} + +// UnassignTags implements the Service interface. +func (l logging) UnassignTags(w http.ResponseWriter, r *http.Request) { + l.next.UnassignTags(w, r) +} diff --git a/services/graph/pkg/service/v0/option.go b/services/graph/pkg/service/v0/option.go index 8588fe4dd8..4ac405b789 100644 --- a/services/graph/pkg/service/v0/option.go +++ b/services/graph/pkg/service/v0/option.go @@ -6,6 +6,7 @@ import ( "github.com/cs3org/reva/v2/pkg/events" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/roles" + searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/config" "github.com/owncloud/ocis/v2/services/graph/pkg/identity" @@ -26,6 +27,7 @@ type Options struct { PermissionService Permissions RoleManager *roles.Manager EventsPublisher events.Publisher + SearchService searchsvc.SearchProviderService } // newOptions initializes the available default options. @@ -88,6 +90,13 @@ func WithRoleService(val RoleService) Option { } } +// WithRoleService provides a function to set the RoleService option. +func WithSearchService(val searchsvc.SearchProviderService) Option { + return func(o *Options) { + o.SearchService = val + } +} + // PermissionService provides a function to set the PermissionService option. func PermissionService(val settingssvc.PermissionService) Option { return func(o *Options) { diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index c08c5ebdd6..3bd3a4ce5b 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -53,6 +53,10 @@ type Service interface { CreateDrive(w http.ResponseWriter, r *http.Request) UpdateDrive(w http.ResponseWriter, r *http.Request) DeleteDrive(w http.ResponseWriter, r *http.Request) + + GetTags(w http.ResponseWriter, r *http.Request) + AssignTags(w http.ResponseWriter, r *http.Request) + UnassignTags(w http.ResponseWriter, r *http.Request) } // NewService returns a service implementation for Service. @@ -69,6 +73,7 @@ func NewService(opts ...Option) Service { spacePropertiesCache: ttlcache.NewCache(), eventsPublisher: options.EventsPublisher, gatewayClient: options.GatewayClient, + searchService: options.SearchService, } if options.IdentityBackend == nil { @@ -167,6 +172,11 @@ func NewService(opts ...Option) Service { m.Route(options.Config.HTTP.Root, func(r chi.Router) { r.Use(middleware.StripSlashes) r.Route("/v1.0", func(r chi.Router) { + r.Route("/extensions/org.libregraph", func(r chi.Router) { + r.Get("/tags", svc.GetTags) + r.Put("/tags", svc.AssignTags) + r.Delete("/tags", svc.UnassignTags) + }) r.Route("/me", func(r chi.Router) { r.Get("/", svc.GetMe) r.Get("/drives", svc.GetDrives) diff --git a/services/graph/pkg/service/v0/tags.go b/services/graph/pkg/service/v0/tags.go new file mode 100644 index 0000000000..40de0be168 --- /dev/null +++ b/services/graph/pkg/service/v0/tags.go @@ -0,0 +1,228 @@ +package svc + +import ( + "encoding/json" + "net/http" + "strings" + + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + revaCtx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/tags" + "github.com/go-chi/render" + libregraph "github.com/owncloud/libre-graph-api-go" + searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" + "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" + "go-micro.dev/v4/metadata" +) + +// GetTags returns all available tags +func (g Graph) GetTags(w http.ResponseWriter, r *http.Request) { + th := r.Header.Get(revaCtx.TokenHeader) + ctx := revaCtx.ContextSetToken(r.Context(), th) + ctx = metadata.Set(ctx, revaCtx.TokenHeader, th) + sr, err := g.searchService.Search(ctx, &searchsvc.SearchRequest{ + Query: "Tags:*", + PageSize: -1, + }) + if err != nil { + g.logger.Error().Err(err).Msg("Could not search for tags") + w.WriteHeader(http.StatusInternalServerError) + return + } + + tagList := tags.FromList("") + for _, match := range sr.Matches { + for _, tag := range match.Entity.Tags { + tagList.AddList(tag) + } + } + + tagCollection := libregraph.NewCollectionOfTags() + tagCollection.Value = tagList.AsSlice() + + render.Status(r, http.StatusOK) + render.JSON(w, r, tagCollection) +} + +// AssignTags assigns a tag to a resource +func (g Graph) AssignTags(w http.ResponseWriter, r *http.Request) { + var ( + assignment libregraph.TagAssignment + ctx = r.Context() + ) + + if err := json.NewDecoder(r.Body).Decode(&assignment); err != nil { + g.logger.Debug().Err(err).Interface("body", r.Body).Msg("could not decode tag assignment request") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition") + return + } + + rid, err := storagespace.ParseID(assignment.ResourceId) + if err != nil { + g.logger.Debug().Err(err).Msg("could not parse resourceId") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid resourceId") + return + } + + sres, err := g.gatewayClient.Stat(ctx, &provider.StatRequest{ + Ref: &provider.Reference{ResourceId: &rid}, + }) + if err != nil { + g.logger.Error().Err(err).Msg("error stating file") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if sres.GetStatus().GetCode() != rpc.Code_CODE_OK { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "can't stat resource") + return + } + + pm := sres.GetInfo().GetPermissionSet() + if pm == nil { + g.logger.Error().Err(err).Msg("no permissionset on file") + w.WriteHeader(http.StatusInternalServerError) + return + } + + // it says we need "write access" to set tags. One of those should do + if !pm.InitiateFileUpload && !pm.CreateContainer { + g.logger.Info().Msg("no permission to create a tag") + w.WriteHeader(http.StatusForbidden) + return + } + + var currentTags string + if m := sres.GetInfo().GetArbitraryMetadata().GetMetadata(); m != nil { + currentTags = m["tags"] + } + + allTags := tags.FromList(currentTags) + newTags := strings.Join(assignment.Tags, ",") + if !allTags.AddList(newTags) { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "no new tags in createtagsrequest or maximum reached") + return + } + + resp, err := g.gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ + Ref: &provider.Reference{ResourceId: &rid}, + ArbitraryMetadata: &provider.ArbitraryMetadata{ + Metadata: map[string]string{ + "tags": allTags.AsList(), + }, + }, + }) + if err != nil || resp.GetStatus().GetCode() != rpc.Code_CODE_OK { + g.logger.Error().Err(err).Msg("error setting tags") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if g.eventsPublisher != nil { + ev := events.TagsAdded{ + Tags: newTags, + Ref: &provider.Reference{ + ResourceId: &rid, + Path: ".", + }, + SpaceOwner: sres.Info.Owner, + Executant: revaCtx.ContextMustGetUser(r.Context()).Id, + } + if err := events.Publish(g.eventsPublisher, ev); err != nil { + g.logger.Error().Err(err).Msg("Failed to publish TagsAdded event") + } + } +} + +// UnassignTags removes a tag from a resource +func (g Graph) UnassignTags(w http.ResponseWriter, r *http.Request) { + var ( + unassignment libregraph.TagUnassignment + ctx = r.Context() + ) + + if err := json.NewDecoder(r.Body).Decode(&unassignment); err != nil { + g.logger.Debug().Err(err).Interface("body", r.Body).Msg("could not decode tag assignment request") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition") + return + } + + rid, err := storagespace.ParseID(unassignment.ResourceId) + if err != nil { + g.logger.Debug().Err(err).Msg("could not parse resourceId") + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid resourceId") + return + } + + sres, err := g.gatewayClient.Stat(ctx, &provider.StatRequest{ + Ref: &provider.Reference{ResourceId: &rid}, + }) + if err != nil { + g.logger.Error().Err(err).Msg("error stating file") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if sres.GetStatus().GetCode() != rpc.Code_CODE_OK { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "can't stat resource") + return + } + + pm := sres.GetInfo().GetPermissionSet() + if pm == nil { + g.logger.Error().Err(err).Msg("no permissionset on file") + w.WriteHeader(http.StatusInternalServerError) + return + } + + // it says we need "write access" to set tags. One of those should do + if !pm.InitiateFileUpload && !pm.CreateContainer { + g.logger.Info().Msg("no permission to create a tag") + w.WriteHeader(http.StatusForbidden) + return + } + + var currentTags string + if m := sres.GetInfo().GetArbitraryMetadata().GetMetadata(); m != nil { + currentTags = m["tags"] + } + + allTags := tags.FromList(currentTags) + toDelete := strings.Join(unassignment.Tags, ",") + if !allTags.RemoveList(toDelete) { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "no new tags in createtagsrequest or maximum reached") + return + } + + resp, err := g.gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{ + Ref: &provider.Reference{ResourceId: &rid}, + ArbitraryMetadata: &provider.ArbitraryMetadata{ + Metadata: map[string]string{ + "tags": allTags.AsList(), + }, + }, + }) + if err != nil || resp.GetStatus().GetCode() != rpc.Code_CODE_OK { + g.logger.Error().Err(err).Msg("error setting tags") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if g.eventsPublisher != nil { + ev := events.TagsRemoved{ + Tags: toDelete, + Ref: &provider.Reference{ + ResourceId: &rid, + Path: ".", + }, + SpaceOwner: sres.Info.Owner, + Executant: revaCtx.ContextMustGetUser(r.Context()).Id, + } + if err := events.Publish(g.eventsPublisher, ev); err != nil { + g.logger.Error().Err(err).Msg("Failed to publish TagsAdded event") + } + } +} diff --git a/services/graph/pkg/service/v0/tracing.go b/services/graph/pkg/service/v0/tracing.go index 8e38329794..357e23dfe8 100644 --- a/services/graph/pkg/service/v0/tracing.go +++ b/services/graph/pkg/service/v0/tracing.go @@ -129,3 +129,18 @@ func (t tracing) CreateDrive(w http.ResponseWriter, r *http.Request) { func (t tracing) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) { t.next.GetRootDriveChildren(w, r) } + +// GetTags implements the Service interface. +func (t tracing) GetTags(w http.ResponseWriter, r *http.Request) { + t.next.GetTags(w, r) +} + +// AssignTags implements the Service interface. +func (t tracing) AssignTags(w http.ResponseWriter, r *http.Request) { + t.next.AssignTags(w, r) +} + +// UnassignTags implements the Service interface. +func (t tracing) UnassignTags(w http.ResponseWriter, r *http.Request) { + t.next.UnassignTags(w, r) +} diff --git a/services/search/pkg/content/basic.go b/services/search/pkg/content/basic.go index d40bb455d1..6c8382dffe 100644 --- a/services/search/pkg/content/basic.go +++ b/services/search/pkg/content/basic.go @@ -5,7 +5,7 @@ import ( "time" storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - //"github.com/cs3org/reva/v2/pkg/tags" + "github.com/cs3org/reva/v2/pkg/tags" "github.com/owncloud/ocis/v2/ocis-pkg/log" ) @@ -27,11 +27,11 @@ func (b Basic) Extract(_ context.Context, ri *storageProvider.ResourceInfo) (Doc MimeType: ri.MimeType, } - //if m := ri.ArbitraryMetadata.GetMetadata(); m != nil { - //if t, ok := m["tags"]; ok { - //doc.Tags = tags.FromList(t).AsSlice() - //} - //} + if m := ri.ArbitraryMetadata.GetMetadata(); m != nil { + if t, ok := m["tags"]; ok { + doc.Tags = tags.FromList(t).AsSlice() + } + } if ri.Mtime != nil { doc.Mtime = time.Unix(int64(ri.Mtime.Seconds), int64(ri.Mtime.Nanos)).UTC().Format(time.RFC3339) diff --git a/services/search/pkg/content/basic_test.go b/services/search/pkg/content/basic_test.go index b7924bf18f..a88b695418 100644 --- a/services/search/pkg/content/basic_test.go +++ b/services/search/pkg/content/basic_test.go @@ -40,7 +40,7 @@ var _ = Describe("Basic", func() { Expect(doc.MimeType).To(Equal(ri.MimeType)) }) - /*It("adds tags", func() { + It("adds tags", func() { for _, data := range []struct { tags string expect []string @@ -63,7 +63,7 @@ var _ = Describe("Basic", func() { Expect(doc).ToNot(BeNil()) Expect(doc.Tags).To(Equal(data.expect)) } - })*/ + }) It("RFC3339 mtime", func() { for _, data := range []struct { diff --git a/services/search/pkg/content/content.go b/services/search/pkg/content/content.go index 11564df5d5..e58f35dc43 100644 --- a/services/search/pkg/content/content.go +++ b/services/search/pkg/content/content.go @@ -9,5 +9,5 @@ type Document struct { Size uint64 Mtime string MimeType string - //Tags []string + Tags []string } diff --git a/services/search/pkg/content/mocks/extractor.go b/services/search/pkg/content/mocks/extractor.go index d3f705014b..d1ab8d0818 100644 --- a/services/search/pkg/content/mocks/extractor.go +++ b/services/search/pkg/content/mocks/extractor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks diff --git a/services/search/pkg/content/mocks/retriever.go b/services/search/pkg/content/mocks/retriever.go index bc380c7e69..9b50186c64 100644 --- a/services/search/pkg/content/mocks/retriever.go +++ b/services/search/pkg/content/mocks/retriever.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks diff --git a/services/search/pkg/engine/bleve.go b/services/search/pkg/engine/bleve.go index 565eba1490..c8ba38e27b 100644 --- a/services/search/pkg/engine/bleve.go +++ b/services/search/pkg/engine/bleve.go @@ -71,7 +71,7 @@ func BuildBleveMapping() (mapping.IndexMapping, error) { docMapping := bleve.NewDocumentMapping() docMapping.AddFieldMappingsAt("Name", lowercaseMapping) - //docMapping.AddFieldMappingsAt("Tags", lowercaseMapping) + docMapping.AddFieldMappingsAt("Tags", lowercaseMapping) docMapping.AddFieldMappingsAt("Content", fulltextFieldMapping) indexMapping := bleve.NewIndexMapping() @@ -185,7 +185,7 @@ func (b *Bleve) Search(_ context.Context, sir *searchService.SearchIndexRequest) Type: uint64(getValue[float64](hit.Fields, "Type")), MimeType: getValue[string](hit.Fields, "MimeType"), Deleted: getValue[bool](hit.Fields, "Deleted"), - //Tags: getSliceValue[string](hit.Fields, "Tags"), + Tags: getSliceValue[string](hit.Fields, "Tags"), }, } @@ -302,7 +302,7 @@ func (b *Bleve) getResource(id string) (*Resource, error) { Mtime: getValue[string](fields, "Mtime"), MimeType: getValue[string](fields, "MimeType"), Content: getValue[string](fields, "Content"), - //Tags: getSliceValue[string](fields, "Tags"), + Tags: getSliceValue[string](fields, "Tags"), }, }, nil } diff --git a/services/search/pkg/engine/bleve_test.go b/services/search/pkg/engine/bleve_test.go index 4e54659fc1..29048ba0b6 100644 --- a/services/search/pkg/engine/bleve_test.go +++ b/services/search/pkg/engine/bleve_test.go @@ -98,7 +98,7 @@ var _ = Describe("Bleve", func() { Describe("Search", func() { Context("by other fields than filename", func() { - /*It("finds files by tags", func() { + It("finds files by tags", func() { parentResource.Document.Tags = []string{"foo", "bar"} err := eng.Upsert(parentResource.ID, parentResource) Expect(err).ToNot(HaveOccurred()) @@ -109,7 +109,7 @@ var _ = Describe("Bleve", func() { assertDocCount(rootResource.ID, "Tags:foo Tags:bar Tags:baz", 1) assertDocCount(rootResource.ID, "Tags:foo Tags:bar Tags:baz", 1) assertDocCount(rootResource.ID, "Tags:baz", 0) - })*/ + }) It("finds files by size", func() { parentResource.Document.Size = 12345 diff --git a/services/search/pkg/engine/engine.go b/services/search/pkg/engine/engine.go index 74c465c9dd..d050583ffd 100644 --- a/services/search/pkg/engine/engine.go +++ b/services/search/pkg/engine/engine.go @@ -60,29 +60,28 @@ func getValue[T any](m map[string]interface{}, key string) (out T) { return } -// TODO comment back in when re-adding the tags features -// func getSliceValue[T any](m map[string]interface{}, key string) (out []T) { -// iv := getValue[interface{}](m, key) -// add := func(v interface{}) { -// cv, ok := v.(T) -// if !ok { -// return -// } -// -// out = append(out, cv) -// } -// -// // bleve tend to convert []string{"foo"} to type string if slice contains only one value -// // bleve: []string{"foo"} -> "foo" -// // bleve: []string{"foo", "bar"} -> []string{"foo", "bar"} -// switch v := iv.(type) { -// case T: -// add(v) -// case []interface{}: -// for _, rv := range v { -// add(rv) -// } -// } -// -// return -// } +func getSliceValue[T any](m map[string]interface{}, key string) (out []T) { + iv := getValue[interface{}](m, key) + add := func(v interface{}) { + cv, ok := v.(T) + if !ok { + return + } + + out = append(out, cv) + } + + // bleve tend to convert []string{"foo"} to type string if slice contains only one value + // bleve: []string{"foo"} -> "foo" + // bleve: []string{"foo", "bar"} -> []string{"foo", "bar"} + switch v := iv.(type) { + case T: + add(v) + case []interface{}: + for _, rv := range v { + add(rv) + } + } + + return +} diff --git a/services/search/pkg/engine/mocks/engine.go b/services/search/pkg/engine/mocks/engine.go index 273fd2e93b..61037adc51 100644 --- a/services/search/pkg/engine/mocks/engine.go +++ b/services/search/pkg/engine/mocks/engine.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks diff --git a/services/search/pkg/search/events.go b/services/search/pkg/search/events.go index 1fc5e12c21..2d3eb42857 100644 --- a/services/search/pkg/search/events.go +++ b/services/search/pkg/search/events.go @@ -21,12 +21,12 @@ func HandleEvents(s Searcher, bus events.Consumer, logger log.Logger, cfg *confi events.ContainerCreated{}, events.FileTouched{}, events.FileVersionRestored{}, - //events.TagsAdded{}, - //events.TagsRemoved{}, + events.TagsAdded{}, + events.TagsRemoved{}, } if cfg.Events.AsyncUploads { - // evts = append(evts, events.UploadReady{}) + evts = append(evts, events.UploadReady{}) } else { evts = append(evts, events.FileUploaded{}) } @@ -40,7 +40,7 @@ func HandleEvents(s Searcher, bus events.Consumer, logger log.Logger, cfg *confi cfg.Events.NumConsumers = 1 } - spaceID := func(ref *provider.Reference) *provider.StorageSpaceId { + getSpaceID := func(ref *provider.Reference) *provider.StorageSpaceId { return &provider.StorageSpaceId{ OpaqueId: storagespace.FormatResourceID( provider.ResourceId{ @@ -51,6 +51,18 @@ func HandleEvents(s Searcher, bus events.Consumer, logger log.Logger, cfg *confi } } + getUser := func(users ...*user.UserId) *user.UserId { + for _, u := range users { + if u == nil { + continue + } + + return u + } + + return nil + } + indexSpaceDebouncer := NewSpaceDebouncer(time.Duration(cfg.Events.DebounceDuration)*time.Millisecond, func(id *provider.StorageSpaceId, userID *user.UserId) { if err := s.IndexSpace(id, userID); err != nil { logger.Error().Err(err).Interface("spaceID", id).Interface("userID", userID).Msg("error while indexing a space") @@ -66,28 +78,31 @@ func HandleEvents(s Searcher, bus events.Consumer, logger log.Logger, cfg *confi switch ev := e.(type) { case events.ItemTrashed: + u := getUser(ev.SpaceOwner, ev.Executant) s.TrashItem(ev.ID) - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), u) case events.ItemMoved: - s.MoveItem(ev.Ref, ev.Executant) - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + u := getUser(ev.SpaceOwner, ev.Executant) + s.MoveItem(ev.Ref, u) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), getUser(ev.SpaceOwner, ev.Executant)) case events.ItemRestored: - s.RestoreItem(ev.Ref, ev.Executant) - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + u := getUser(ev.SpaceOwner, ev.Executant) + s.RestoreItem(ev.Ref, u) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), u) case events.ContainerCreated: - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), getUser(ev.SpaceOwner, ev.Executant)) case events.FileTouched: - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), getUser(ev.SpaceOwner, ev.Executant)) case events.FileVersionRestored: - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) - //case events.TagsAdded: - // indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) - //case events.TagsRemoved: - //indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), getUser(ev.SpaceOwner, ev.Executant)) + case events.TagsAdded: + s.UpsertItem(ev.Ref, getUser(ev.SpaceOwner, ev.Executant)) + case events.TagsRemoved: + s.UpsertItem(ev.Ref, getUser(ev.SpaceOwner, ev.Executant)) case events.FileUploaded: - indexSpaceDebouncer.Debounce(spaceID(ev.Ref), ev.Executant) - //case events.UploadReady: - //indexSpaceDebouncer.Debounce(spaceID(ev.FileRef), ev.ExecutingUser.Id) + indexSpaceDebouncer.Debounce(getSpaceID(ev.Ref), getUser(ev.SpaceOwner, ev.Executant)) + case events.UploadReady: + indexSpaceDebouncer.Debounce(getSpaceID(ev.FileRef), getUser(ev.SpaceOwner, ev.ExecutingUser.Id)) } if err != nil { diff --git a/services/search/pkg/search/events_test.go b/services/search/pkg/search/events_test.go index b7966dbbfd..465355bb12 100644 --- a/services/search/pkg/search/events_test.go +++ b/services/search/pkg/search/events_test.go @@ -1,6 +1,7 @@ package search_test import ( + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/cs3org/reva/v2/pkg/events" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -46,8 +47,8 @@ var _ = DescribeTable("events", Entry("ContainerCreated", []string{"IndexSpace"}, events.ContainerCreated{}, false), Entry("FileTouched", []string{"IndexSpace"}, events.FileTouched{}, false), Entry("FileVersionRestored", []string{"IndexSpace"}, events.FileVersionRestored{}, false), - //Entry("TagsAdded", []string{"IndexSpace"}, events.TagsAdded{}, false), - //Entry("TagsRemoved", []string{"IndexSpace"}, events.TagsRemoved{}, false), + Entry("TagsAdded", []string{"UpsertItem"}, events.TagsAdded{}, false), + Entry("TagsRemoved", []string{"UpsertItem"}, events.TagsRemoved{}, false), Entry("FileUploaded", []string{"IndexSpace"}, events.FileUploaded{}, false), - //Entry("UploadReady", []string{"IndexSpace"}, events.UploadReady{ExecutingUser: &userv1beta1.User{}}, true), + Entry("UploadReady", []string{"IndexSpace"}, events.UploadReady{ExecutingUser: &userv1beta1.User{}}, true), ) diff --git a/services/search/pkg/search/mocks/searcher.go b/services/search/pkg/search/mocks/searcher.go index 557e9a0e5a..2f94ad0ad1 100644 --- a/services/search/pkg/search/mocks/searcher.go +++ b/services/search/pkg/search/mocks/searcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.15.0. DO NOT EDIT. package mocks diff --git a/services/webdav/pkg/service/v0/search.go b/services/webdav/pkg/service/v0/search.go index d53969fe54..7ad4258d63 100644 --- a/services/webdav/pkg/service/v0/search.go +++ b/services/webdav/pkg/service/v0/search.go @@ -14,6 +14,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/tags" "github.com/cs3org/reva/v2/pkg/utils" searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" @@ -189,6 +190,12 @@ func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType)) propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", match.Entity.Permissions)) + t := tags.FromList("") + for _, tag := range match.Entity.Tags { + t.AddList(tag) + } + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:tags", t.AsList())) + // those seem empty - bug? propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", match.Entity.Etag))