Skip to content

Commit eccc900

Browse files
committed
feature: add profile photos graph service and api
1 parent 6e4cbf2 commit eccc900

8 files changed

Lines changed: 217 additions & 238 deletions

File tree

pkg/systemstorageclient/systemstorageclient.go

Lines changed: 0 additions & 82 deletions
This file was deleted.

services/graph/pkg/config/config.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Config struct {
3838

3939
Context context.Context `yaml:"-"`
4040

41-
SystemStorageClient SystemStorageClient `yaml:"system_storage_client"`
41+
Metadata Metadata `yaml:"metadata_config"`
4242
}
4343

4444
type Spaces struct {
@@ -156,13 +156,12 @@ type ServiceAccount struct {
156156
ServiceAccountSecret string `yaml:"service_account_secret" env:"OC_SERVICE_ACCOUNT_SECRET;GRAPH_SERVICE_ACCOUNT_SECRET" desc:"The service account secret." introductionVersion:"1.0.0"`
157157
}
158158

159-
// SystemStorageClient configures the metadata store to use
160-
type SystemStorageClient struct {
161-
GatewayAddress string `yaml:"gateway_addr" env:"GRAPH_STORAGE_GATEWAY_GRPC_ADDR;STORAGE_GATEWAY_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"1.0.0"`
162-
StorageAddress string `yaml:"storage_addr" env:"GRAPH_STORAGE_GRPC_ADDR;STORAGE_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"1.0.0"`
159+
// Metadata configures the metadata store to use
160+
type Metadata struct {
161+
GatewayAddress string `yaml:"gateway_addr" env:"GRAPH_STORAGE_GATEWAY_GRPC_ADDR;STORAGE_GATEWAY_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"%%NEXT%%"`
162+
StorageAddress string `yaml:"storage_addr" env:"GRAPH_STORAGE_GRPC_ADDR;STORAGE_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"%%NEXT%%"`
163163

164-
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID;GRAPH_SYSTEM_USER_ID" desc:"ID of the OpenCloud STORAGE-SYSTEM system user. Admins need to set the ID for the STORAGE-SYSTEM system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"1.0.0"`
165-
SystemUserIDP string `yaml:"system_user_idp" env:"OC_SYSTEM_USER_IDP;GRAPH_SYSTEM_USER_IDP" desc:"IDP of the OpenCloud STORAGE-SYSTEM system user." introductionVersion:"1.0.0"`
166-
SystemUserAPIKey string `yaml:"system_user_api_key" env:"OC_SYSTEM_USER_API_KEY" desc:"API key for the STORAGE-SYSTEM system user." introductionVersion:"1.0.0"`
167-
Cache *Cache `yaml:"cache"`
164+
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID;GRAPH_SYSTEM_USER_ID" desc:"ID of the OpenCloud STORAGE-SYSTEM system user. Admins need to set the ID for the STORAGE-SYSTEM system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"%%NEXT%%"`
165+
SystemUserIDP string `yaml:"system_user_idp" env:"OC_SYSTEM_USER_IDP;GRAPH_SYSTEM_USER_IDP" desc:"IDP of the OpenCloud STORAGE-SYSTEM system user." introductionVersion:"%%NEXT%%"`
166+
SystemUserAPIKey string `yaml:"system_user_api_key" env:"OC_SYSTEM_USER_API_KEY" desc:"API key for the STORAGE-SYSTEM system user." introductionVersion:"%%NEXT%%"`
168167
}

services/graph/pkg/config/defaults/defaultconfig.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,10 @@ func DefaultConfig() *config.Config {
125125
UnifiedRoles: config.UnifiedRoles{
126126
AvailableRoles: nil, // will be populated with defaults in EnsureDefaults
127127
},
128-
SystemStorageClient: config.SystemStorageClient{
128+
Metadata: config.Metadata{
129129
GatewayAddress: "eu.opencloud.api.storage-system",
130130
StorageAddress: "eu.opencloud.api.storage-system",
131131
SystemUserIDP: "internal",
132-
Cache: &config.Cache{
133-
Store: "memory",
134-
Nodes: []string{"127.0.0.1:9233"},
135-
Database: "settings-cache",
136-
TTL: time.Minute * 10,
137-
},
138132
},
139133
}
140134
}
@@ -203,12 +197,12 @@ func EnsureDefaults(cfg *config.Config) {
203197
}
204198
}
205199

206-
if cfg.SystemStorageClient.SystemUserAPIKey == "" && cfg.Commons != nil && cfg.Commons.SystemUserAPIKey != "" {
207-
cfg.SystemStorageClient.SystemUserAPIKey = cfg.Commons.SystemUserAPIKey
200+
if cfg.Metadata.SystemUserAPIKey == "" && cfg.Commons != nil && cfg.Commons.SystemUserAPIKey != "" {
201+
cfg.Metadata.SystemUserAPIKey = cfg.Commons.SystemUserAPIKey
208202
}
209203

210-
if cfg.SystemStorageClient.SystemUserID == "" && cfg.Commons != nil && cfg.Commons.SystemUserID != "" {
211-
cfg.SystemStorageClient.SystemUserID = cfg.Commons.SystemUserID
204+
if cfg.Metadata.SystemUserID == "" && cfg.Commons != nil && cfg.Commons.SystemUserID != "" {
205+
cfg.Metadata.SystemUserID = cfg.Commons.SystemUserID
212206
}
213207

214208
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package svc
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
"net/http"
8+
9+
"github.com/go-chi/render"
10+
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
11+
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata"
12+
13+
"github.com/opencloud-eu/opencloud/pkg/log"
14+
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
15+
)
16+
17+
type (
18+
// UsersUserProfilePhotoProvider is the interface that defines the methods for the user profile photo service
19+
UsersUserProfilePhotoProvider interface {
20+
// GetPhoto retrieves the requested photo
21+
GetPhoto(ctx context.Context, id string) ([]byte, error)
22+
23+
// UpdatePhoto retrieves the requested photo
24+
UpdatePhoto(ctx context.Context, id string, rc io.Reader) error
25+
26+
// DeletePhoto deletes the requested photo
27+
DeletePhoto(ctx context.Context, id string) error
28+
}
29+
)
30+
31+
var (
32+
// profilePhotoSpaceID is the space ID for the profile photo
33+
profilePhotoSpaceID = "f2bdd61a-da7c-49fc-8203-0558109d1b4f"
34+
35+
// ErrNoBytes is returned when no bytes are found
36+
ErrNoBytes = errors.New("no bytes")
37+
38+
// ErrNoUser is returned when no user is found
39+
ErrNoUser = errors.New("no user found")
40+
)
41+
42+
// UsersUserProfilePhotoService is the implementation of the UsersUserProfilePhotoProvider interface
43+
type UsersUserProfilePhotoService struct {
44+
storage metadata.Storage
45+
}
46+
47+
// NewUsersUserProfilePhotoService creates a new UsersUserProfilePhotoService
48+
func NewUsersUserProfilePhotoService(storage metadata.Storage) (UsersUserProfilePhotoService, error) {
49+
if err := storage.Init(context.Background(), profilePhotoSpaceID); err != nil {
50+
return UsersUserProfilePhotoService{}, err
51+
}
52+
53+
return UsersUserProfilePhotoService{
54+
storage: storage,
55+
}, nil
56+
}
57+
58+
// GetPhoto retrieves the requested photo
59+
func (s UsersUserProfilePhotoService) GetPhoto(ctx context.Context, id string) ([]byte, error) {
60+
photo, err := s.storage.SimpleDownload(ctx, id)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
return photo, nil
66+
}
67+
68+
// DeletePhoto deletes the requested photo
69+
func (s UsersUserProfilePhotoService) DeletePhoto(ctx context.Context, id string) error {
70+
return s.storage.Delete(ctx, id)
71+
}
72+
73+
// UpdatePhoto updates the requested photo
74+
func (s UsersUserProfilePhotoService) UpdatePhoto(ctx context.Context, id string, rc io.Reader) error {
75+
photo, err := io.ReadAll(rc)
76+
if err != nil {
77+
return err
78+
}
79+
80+
if len(photo) == 0 {
81+
return ErrNoBytes
82+
}
83+
84+
return s.storage.SimpleUpload(ctx, id, photo)
85+
}
86+
87+
// UsersUserProfilePhotoApi contains all photo related api endpoints
88+
type UsersUserProfilePhotoApi struct {
89+
logger log.Logger
90+
usersUserProfilePhotoService UsersUserProfilePhotoProvider
91+
}
92+
93+
// NewUsersUserProfilePhotoApi creates a new UsersUserProfilePhotoApi
94+
func NewUsersUserProfilePhotoApi(usersUserProfilePhotoService UsersUserProfilePhotoProvider, logger log.Logger) (UsersUserProfilePhotoApi, error) {
95+
return UsersUserProfilePhotoApi{
96+
logger: log.Logger{Logger: logger.With().Str("graph api", "UsersUserProfilePhotoApi").Logger()},
97+
usersUserProfilePhotoService: usersUserProfilePhotoService,
98+
}, nil
99+
}
100+
101+
// GetProfilePhoto provides the requested photo
102+
func (api UsersUserProfilePhotoApi) GetProfilePhoto(w http.ResponseWriter, r *http.Request) {
103+
id, ok := api.getUserID(w, r)
104+
if !ok {
105+
return
106+
}
107+
108+
photo, err := api.usersUserProfilePhotoService.GetPhoto(r.Context(), id)
109+
if err != nil {
110+
api.logger.Debug().Err(err)
111+
errorcode.GeneralException.Render(w, r, http.StatusNotFound, "failed to get photo")
112+
return
113+
}
114+
115+
render.Status(r, http.StatusOK)
116+
_, _ = w.Write(photo)
117+
}
118+
119+
// UpsertProfilePhoto updates or inserts (initial create) the requested photo
120+
func (api UsersUserProfilePhotoApi) UpsertProfilePhoto(w http.ResponseWriter, r *http.Request) {
121+
id, ok := api.getUserID(w, r)
122+
if !ok {
123+
return
124+
}
125+
126+
if err := api.usersUserProfilePhotoService.UpdatePhoto(r.Context(), id, r.Body); err != nil {
127+
api.logger.Debug().Err(err)
128+
errorcode.GeneralException.Render(w, r, http.StatusNotFound, "failed to update photo")
129+
return
130+
}
131+
defer func() {
132+
_ = r.Body.Close()
133+
}()
134+
135+
render.Status(r, http.StatusOK)
136+
}
137+
138+
// DeleteProfilePhoto deletes the requested photo
139+
func (api UsersUserProfilePhotoApi) DeleteProfilePhoto(w http.ResponseWriter, r *http.Request) {
140+
id, ok := api.getUserID(w, r)
141+
if !ok {
142+
return
143+
}
144+
145+
if err := api.usersUserProfilePhotoService.DeletePhoto(r.Context(), id); err != nil {
146+
api.logger.Debug().Err(err)
147+
errorcode.GeneralException.Render(w, r, http.StatusNotFound, "failed to delete photo")
148+
return
149+
}
150+
151+
render.Status(r, http.StatusOK)
152+
}
153+
154+
func (api UsersUserProfilePhotoApi) getUserID(w http.ResponseWriter, r *http.Request) (string, bool) {
155+
u, ok := revactx.ContextGetUser(r.Context())
156+
if !ok {
157+
api.logger.Debug().Msg(ErrNoUser.Error())
158+
errorcode.GeneralException.Render(w, r, http.StatusMethodNotAllowed, ErrNoUser.Error())
159+
return "", false
160+
}
161+
162+
return u.GetId().GetOpaqueId(), true
163+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package svc_test
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestNewUsersUserProfilePhotoApi(t *testing.T) {
8+
panic("add UsersUserProfilePhotoApi tests")
9+
}
10+
11+
func TestNewUsersUserProfilePhotoService(t *testing.T) {
12+
panic("add UsersUserProfilePhotoService tests")
13+
}

services/graph/pkg/service/v0/graph.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package svc
33
import (
44
"context"
55
"errors"
6-
"github.com/opencloud-eu/opencloud/pkg/systemstorageclient"
76
"net/http"
87
"net/url"
98
"path"
@@ -68,7 +67,6 @@ type Graph struct {
6867
keycloakClient keycloak.Client
6968
historyClient ehsvc.EventHistoryService
7069
traceProvider trace.TracerProvider
71-
sdsc systemstorageclient.SystemDataStorageClient
7270
}
7371

7472
// ServeHTTP implements the Service interface.

0 commit comments

Comments
 (0)