Skip to content

Commit c95aa85

Browse files
authored
Implement CS3 Labels API (#5480)
* GORMify favorites SQL driver * write migration script for favorites db * WIP * adapt to new CS3 labels API * parse labels in propfind response * dont use old attribute anymore * remove leftover stuff from favorites * Implement ListLabels
1 parent 2e76769 commit c95aa85

26 files changed

Lines changed: 843 additions & 438 deletions

File tree

changelog/unreleased/labels-api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Enhancement: implement CS3 labels API
2+
3+
Together with this, the new table is also "GORM-ified"
4+
5+
https://github.com/cs3org/reva/pull/5480

cmd/revad/runtime/loader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
_ "github.com/cs3org/reva/v3/pkg/auth/registry/loader"
3737
_ "github.com/cs3org/reva/v3/pkg/datatx/manager/loader"
3838
_ "github.com/cs3org/reva/v3/pkg/group/manager/loader"
39+
_ "github.com/cs3org/reva/v3/pkg/labels/loader"
3940
_ "github.com/cs3org/reva/v3/pkg/metrics/driver/loader"
4041
_ "github.com/cs3org/reva/v3/pkg/notification/handler/loader"
4142
_ "github.com/cs3org/reva/v3/pkg/notification/manager/loader"
@@ -51,7 +52,6 @@ import (
5152
_ "github.com/cs3org/reva/v3/pkg/share/cache/loader"
5253
_ "github.com/cs3org/reva/v3/pkg/share/cache/warmup/loader"
5354
_ "github.com/cs3org/reva/v3/pkg/share/manager/loader"
54-
_ "github.com/cs3org/reva/v3/pkg/favorite/loader"
5555
_ "github.com/cs3org/reva/v3/pkg/storage/fs/loader"
5656
_ "github.com/cs3org/reva/v3/pkg/storage/registry/loader"
5757
_ "github.com/cs3org/reva/v3/pkg/token/manager/loader"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/coreos/go-oidc/v3 v3.18.0
1515
github.com/creasty/defaults v1.8.0
1616
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e
17-
github.com/cs3org/go-cs3apis v0.0.0-20260422140321-634bbcfdfc79
17+
github.com/cs3org/go-cs3apis v0.0.0-20260505152900-5c56a16e60fb
1818
github.com/dgraph-io/ristretto v0.2.0
1919
github.com/dolthub/go-mysql-server v0.14.0
2020
github.com/glpatcern/go-mime v0.0.0-20221026162842-2a8d71ad17a9

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,8 +896,10 @@ github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYK
896896
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
897897
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
898898
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
899-
github.com/cs3org/go-cs3apis v0.0.0-20260422140321-634bbcfdfc79 h1:h3qWiT5sdKeC8tt0CCfaqt2xmZRrbwtWXDeVimHv9yA=
900-
github.com/cs3org/go-cs3apis v0.0.0-20260422140321-634bbcfdfc79/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
899+
github.com/cs3org/go-cs3apis v0.0.0-20260424072047-8d9ef7076ae9 h1:8WtMb7TKKx1AJM3j+uR+H25y4v+b8o8GIQg/2ooCyRo=
900+
github.com/cs3org/go-cs3apis v0.0.0-20260424072047-8d9ef7076ae9/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
901+
github.com/cs3org/go-cs3apis v0.0.0-20260505152900-5c56a16e60fb h1:LIgYp+xYlT5Y1DhGhLxREtXkWEJKDsMkdvoRfXhlcPg=
902+
github.com/cs3org/go-cs3apis v0.0.0-20260505152900-5c56a16e60fb/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
901903
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
902904
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
903905
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

internal/grpc/services/gateway/gateway.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type config struct {
5959
DataTxEndpoint string `mapstructure:"datatx"`
6060
DataGatewayEndpoint string `mapstructure:"datagateway"`
6161
PermissionsEndpoint string `mapstructure:"permissionssvc"`
62+
LabelsEndpoint string `mapstructure:"labelssvc"`
6263
SpacesEndpoint string `mapstructure:"spacessvc"`
6364
CommitShareToStorageGrant bool `mapstructure:"commit_share_to_storage_grant"`
6465
DisableHomeCreationOnLogin bool `mapstructure:"disable_home_creation_on_login"`
@@ -107,6 +108,7 @@ func (c *config) ApplyDefaults() {
107108
c.UserProviderEndpoint = sharedconf.GetGatewaySVC(c.UserProviderEndpoint)
108109
c.GroupProviderEndpoint = sharedconf.GetGatewaySVC(c.GroupProviderEndpoint)
109110
c.DataTxEndpoint = sharedconf.GetGatewaySVC(c.DataTxEndpoint)
111+
c.LabelsEndpoint = sharedconf.GetGatewaySVC(c.LabelsEndpoint)
110112
c.SpacesEndpoint = sharedconf.GetGatewaySVC(c.SpacesEndpoint)
111113

112114
c.DataGatewayEndpoint = sharedconf.GetDataGateway(c.DataGatewayEndpoint)
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2018-2026 CERN
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// In applying this license, CERN does not waive the privileges and immunities
16+
// granted to it by virtue of its status as an Intergovernmental Organization
17+
// or submit itself to any jurisdiction.
18+
19+
package gateway
20+
21+
import (
22+
"context"
23+
"fmt"
24+
25+
labels "github.com/cs3org/go-cs3apis/cs3/labels/v1beta1"
26+
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
27+
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
28+
"github.com/cs3org/reva/v3/pkg/appctx"
29+
"github.com/cs3org/reva/v3/pkg/rgrpc/status"
30+
"github.com/cs3org/reva/v3/pkg/rgrpc/todo/pool"
31+
"github.com/pkg/errors"
32+
)
33+
34+
// resolveRef ensures the reference has a ResourceId by statting it if necessary.
35+
func (s *svc) resolveRef(ctx context.Context, ref *provider.Reference) (*provider.Reference, *rpc.Status, error) {
36+
if ref == nil {
37+
return nil, status.NewInvalidArg(ctx, "ref is required"), nil
38+
}
39+
if ref.GetResourceId() != nil {
40+
return ref, nil, nil
41+
}
42+
43+
statRes, err := s.Stat(ctx, &provider.StatRequest{Ref: ref})
44+
if err != nil {
45+
return nil, nil, errors.Wrap(err, "gateway: error statting ref")
46+
}
47+
if statRes.Status.Code != rpc.Code_CODE_OK {
48+
return nil, statRes.Status, nil
49+
}
50+
51+
return &provider.Reference{ResourceId: statRes.Info.Id}, nil, nil
52+
}
53+
54+
func labelMetadataKey(uid, label string) string {
55+
return fmt.Sprintf("reva.labels.%s.%s", uid, label)
56+
}
57+
58+
func (s *svc) AddLabel(ctx context.Context, req *labels.AddLabelRequest) (*labels.AddLabelResponse, error) {
59+
ref, st, err := s.resolveRef(ctx, req.GetRef())
60+
if err != nil {
61+
return &labels.AddLabelResponse{
62+
Status: status.NewInternal(ctx, err, "error resolving ref"),
63+
}, nil
64+
}
65+
if st != nil {
66+
return &labels.AddLabelResponse{Status: st}, nil
67+
}
68+
req.Ref = ref
69+
70+
u := appctx.ContextMustGetUser(ctx)
71+
72+
// Set the label as metadata on the storage provider first.
73+
metaRes, err := s.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{
74+
Ref: ref,
75+
ArbitraryMetadata: &provider.ArbitraryMetadata{
76+
Metadata: map[string]string{
77+
labelMetadataKey(u.Id.OpaqueId, req.GetLabel()): "1",
78+
},
79+
},
80+
})
81+
if err != nil {
82+
return &labels.AddLabelResponse{
83+
Status: status.NewInternal(ctx, err, "error setting label metadata on storage provider"),
84+
}, nil
85+
}
86+
if metaRes.Status.Code != rpc.Code_CODE_OK {
87+
return &labels.AddLabelResponse{Status: metaRes.Status}, nil
88+
}
89+
90+
// Store the label in the labels database.
91+
c, err := pool.GetLabelsClient(pool.Endpoint(s.c.LabelsEndpoint))
92+
if err != nil {
93+
err = errors.Wrap(err, "gateway: error calling GetLabelsClient")
94+
return &labels.AddLabelResponse{
95+
Status: status.NewInternal(ctx, err, "error getting labels client"),
96+
}, nil
97+
}
98+
99+
res, err := c.AddLabel(ctx, req)
100+
if err != nil {
101+
return nil, errors.Wrap(err, "gateway: error calling AddLabel")
102+
}
103+
104+
return res, nil
105+
}
106+
107+
func (s *svc) RemoveLabel(ctx context.Context, req *labels.RemoveLabelRequest) (*labels.RemoveLabelResponse, error) {
108+
ref, st, err := s.resolveRef(ctx, req.GetRef())
109+
if err != nil {
110+
return &labels.RemoveLabelResponse{
111+
Status: status.NewInternal(ctx, err, "error resolving ref"),
112+
}, nil
113+
}
114+
if st != nil {
115+
return &labels.RemoveLabelResponse{Status: st}, nil
116+
}
117+
req.Ref = ref
118+
119+
u := appctx.ContextMustGetUser(ctx)
120+
121+
// Remove the label metadata from the storage provider first.
122+
metaRes, err := s.UnsetArbitraryMetadata(ctx, &provider.UnsetArbitraryMetadataRequest{
123+
Ref: ref,
124+
ArbitraryMetadataKeys: []string{labelMetadataKey(u.Id.OpaqueId, req.GetLabel())},
125+
})
126+
if err != nil {
127+
return &labels.RemoveLabelResponse{
128+
Status: status.NewInternal(ctx, err, "error unsetting label metadata on storage provider"),
129+
}, nil
130+
}
131+
if metaRes.Status.Code != rpc.Code_CODE_OK {
132+
return &labels.RemoveLabelResponse{Status: metaRes.Status}, nil
133+
}
134+
135+
// Remove the label from the labels database.
136+
c, err := pool.GetLabelsClient(pool.Endpoint(s.c.LabelsEndpoint))
137+
if err != nil {
138+
err = errors.Wrap(err, "gateway: error calling GetLabelsClient")
139+
return &labels.RemoveLabelResponse{
140+
Status: status.NewInternal(ctx, err, "error getting labels client"),
141+
}, nil
142+
}
143+
144+
res, err := c.RemoveLabel(ctx, req)
145+
if err != nil {
146+
return nil, errors.Wrap(err, "gateway: error calling RemoveLabel")
147+
}
148+
149+
return res, nil
150+
}
151+
152+
func (s *svc) ListLabels(ctx context.Context, req *labels.ListLabelsRequest) (*labels.ListLabelsResponse, error) {
153+
c, err := pool.GetLabelsClient(pool.Endpoint(s.c.LabelsEndpoint))
154+
if err != nil {
155+
err = errors.Wrap(err, "gateway: error calling GetLabelsClient")
156+
return &labels.ListLabelsResponse{
157+
Status: status.NewInternal(ctx, err, "error getting labels client"),
158+
}, nil
159+
}
160+
161+
res, err := c.ListLabels(ctx, req)
162+
if err != nil {
163+
return nil, errors.Wrap(err, "gateway: error calling ListLabels")
164+
}
165+
166+
return res, nil
167+
}
168+
169+
func (s *svc) ListResourcesForLabel(ctx context.Context, req *labels.ListResourcesForLabelRequest) (*labels.ListResourcesForLabelResponse, error) {
170+
c, err := pool.GetLabelsClient(pool.Endpoint(s.c.LabelsEndpoint))
171+
if err != nil {
172+
err = errors.Wrap(err, "gateway: error calling GetLabelsClient")
173+
return &labels.ListResourcesForLabelResponse{
174+
Status: status.NewInternal(ctx, err, "error getting labels client"),
175+
}, nil
176+
}
177+
178+
res, err := c.ListResourcesForLabel(ctx, req)
179+
if err != nil {
180+
return nil, errors.Wrap(err, "gateway: error calling ListResourcesForLabel")
181+
}
182+
183+
return res, nil
184+
}

internal/grpc/services/gateway/storageprovider.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -726,17 +726,6 @@ func (s *svc) splitPath(_ context.Context, p string) []string {
726726
return strings.SplitN(p, "/", 4) // ["home", "MyShares", "photos", "Ibiza/beach.png"]
727727
}
728728

729-
func (s *svc) AddFavorite(ctx context.Context, req *provider.AddFavoriteRequest) (*provider.AddFavoriteResponse, error) {
730-
return &provider.AddFavoriteResponse{
731-
Status: status.NewUnimplemented(ctx, errtypes.NotSupported("AddFavorite not implemented"), "AddFavorite not implemented"),
732-
}, nil
733-
}
734-
735-
func (s *svc) RemoveFavorite(ctx context.Context, req *provider.RemoveFavoriteRequest) (*provider.RemoveFavoriteResponse, error) {
736-
return &provider.RemoveFavoriteResponse{
737-
Status: status.NewUnimplemented(ctx, errtypes.NotSupported("RemoveFavorite not implemented"), "RemoveFavorite not implemented"),
738-
}, nil
739-
}
740729

741730
func (s *svc) CreateSymlink(ctx context.Context, req *provider.CreateSymlinkRequest) (*provider.CreateSymlinkResponse, error) {
742731
return &provider.CreateSymlinkResponse{

0 commit comments

Comments
 (0)