diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go index fafb58f56..d6927c71b 100644 --- a/ocis-pkg/l10n/l10n.go +++ b/ocis-pkg/l10n/l10n.go @@ -53,45 +53,79 @@ func (t Translator) Translate(str, locale string) string { return t.Locale(locale).Get(str) } -type field func() (string, []any) +func TranslateLocation(t Translator, locale string) func(string, ...any) string { + return t.Locale(locale).Get +} -func TranslateField(fieldName string, fn ...any) field { +type field func() string +type structs func() (string, []any) +type each func() (string, []any) + +func TranslateField(fieldName string) field { + return func() string { + return fieldName + } +} + +func TranslateStruct(fieldName string, fn ...any) structs { return func() (string, []any) { return fieldName, fn } } -func TranslateLocation(t Translator, locale string) func(string, ...any) string { - return t.Locale(locale).Get +func TranslateEach(fieldName string, fn ...any) each { + return func() (string, []any) { + return fieldName, fn + } } // TranslateEntity function provides the generic way to translate the necessary fields in composite entities. // The function takes the entity, translation function and fields to translate // that are described by the TranslateField function. The function supports nested structs and slices of structs. // -// type InnreStruct struct { -// Description string -// DisplayName *string -// } +// type InnreStruct struct { +// Description string +// DisplayName *string +// } // -// type TopLevelStruct struct { -// Description string -// DisplayName *string -// SubStruct *InnreStruct -// } -// -// TranslateEntity(tt.args.structPtr, translateFunc(), -// TranslateField("Description"), -// TranslateField("DisplayName"), -// TranslateField("SubStruct", -// TranslateField("Description"), -// TranslateField("DisplayName"))) +// type TopLevelStruct struct { +// Description string +// DisplayName *string +// SubStruct *InnreStruct +// StructList []*InnreStruct +// } +// s:= &TopLevelStruct{ +// Description: "description", +// DisplayName: toStrPointer("displayName"), +// SubStruct: &InnreStruct{ +// Description: "inner", +// DisplayName: toStrPointer("innerDisplayName"), +// }, +// StructList: []*InnreStruct{ +// { +// Description: "inner 1", +// DisplayName: toStrPointer("innerDisplayName 1"), +// }, +// { +// Description: "inner 2", +// DisplayName: toStrPointer("innerDisplayName 2"), +// }, +// }, +// } +// TranslateEntity(s, translateFunc(), +// TranslateField("Description"), +// TranslateField("DisplayName"), +// TranslateStruct("SubStruct", +// TranslateField("Description"), +// TranslateField("DisplayName")), +// TranslateEach("StructList", +// TranslateField("Description"), +// TranslateField("DisplayName"))) func TranslateEntity(entity any, tr func(string, ...any) string, fields ...any) error { value := reflect.ValueOf(entity) // Indirect through pointers and interfaces if value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface { if value.IsNil() { - // treat a nil struct pointer as valid return nil } value = value.Elem() @@ -118,18 +152,9 @@ func TranslateEntity(entity any, tr func(string, ...any) string, fields ...any) } func translateInner(value reflect.Value, tr func(string, ...any) string, fields ...any) { - for _, fl := range fields { - if _, ok := fl.(field); ok { - translateField(value, tr, fl.(field)) - } - } -} - -func translateField(value reflect.Value, tr func(string, ...any) string, fl field) { if !value.IsValid() { return } - fieldName, fields := fl() // Indirect through pointers and interfaces if value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface { if value.IsNil() { @@ -137,6 +162,24 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel } value = value.Elem() } + if !value.IsValid() { + return + } + for _, fl := range fields { + switch fl.(type) { + case field: + translateStringField(value, tr, fl.(field)) + case each: + translateEach(value, tr, fl.(each)) + case structs: + translateStruct(value, tr, fl.(structs)) + } + } +} + +func translateEach(value reflect.Value, tr func(string, ...any) string, fl each) { + fieldName, fields := fl() + // exported field innerValue := value.FieldByName(fieldName) if !innerValue.IsValid() { return @@ -147,20 +190,23 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel translateInner(innerValue.Index(i), tr, fields...) } } +} + +func translateStruct(value reflect.Value, tr func(string, ...any) string, fl structs) { + fieldName, fields := fl() + // exported field + innerValue := value.FieldByName(fieldName) + if !innerValue.IsValid() { + return + } if isStruct(innerValue) { translateInner(innerValue, tr, fields...) return } - translateStringField(value, tr, fieldName) } -func translateStringField(value reflect.Value, tr func(string, ...any) string, fieldName string) { - if value.Kind() == reflect.Ptr { - if value.IsNil() { - return - } - value = value.Elem() - } +func translateStringField(value reflect.Value, tr func(string, ...any) string, fl field) { + fieldName := fl() // exported field f := value.FieldByName(fieldName) if f.IsValid() { diff --git a/ocis-pkg/l10n/l10n_test.go b/ocis-pkg/l10n/l10n_test.go index d00d269a4..d4c97b84b 100644 --- a/ocis-pkg/l10n/l10n_test.go +++ b/ocis-pkg/l10n/l10n_test.go @@ -20,7 +20,8 @@ func TestTranslateStruct(t *testing.T) { } type WrapperStruct struct { - StructList []*InnreStruct + Description string + StructList []*InnreStruct } toStrPointer := func(str string) *string { @@ -65,6 +66,45 @@ func TestTranslateStruct(t *testing.T) { }, }, }, + { + name: "top level slice of struct", + args: args{ + structPtr: []*TopLevelStruct{ + { + Description: "inner 1", + DisplayName: toStrPointer("innerDisplayName 1"), + SubStruct: &InnreStruct{ + Description: "inner", + DisplayName: toStrPointer("innerDisplayName"), + }, + }, + { + Description: "inner 2", + DisplayName: toStrPointer("innerDisplayName 2"), + }, + }, + request: []any{ + TranslateField("Description"), + TranslateField("DisplayName"), + TranslateStruct("SubStruct", + TranslateField("Description"), + TranslateField("DisplayName"))}, + }, + expected: []*TopLevelStruct{ + { + Description: "new Inner 1", + DisplayName: toStrPointer("new InnerDisplayName 1"), + SubStruct: &InnreStruct{ + Description: "new Inner", + DisplayName: toStrPointer("new InnerDisplayName"), + }, + }, + { + Description: "new Inner 2", + DisplayName: toStrPointer("new InnerDisplayName 2"), + }, + }, + }, { name: "wrapped struct full", args: args{ @@ -81,7 +121,7 @@ func TestTranslateStruct(t *testing.T) { }, }, request: []any{ - TranslateField("StructList", + TranslateEach("StructList", TranslateField("Description"), TranslateField("DisplayName"))}, }, @@ -105,7 +145,7 @@ func TestTranslateStruct(t *testing.T) { request: []any{ TranslateField("Description"), TranslateField("DisplayName"), - TranslateField("NotExistingSubStructName", + TranslateStruct("NotExistingSubStructName", TranslateField("Description"), TranslateField("DisplayName")), }, @@ -119,7 +159,7 @@ func TestTranslateStruct(t *testing.T) { request: []any{ TranslateField("Description"), TranslateField("DisplayName"), - TranslateField("SubStruct", + TranslateStruct("SubStruct", TranslateField("Description"), TranslateField("DisplayName"))}, }, @@ -134,7 +174,7 @@ func TestTranslateStruct(t *testing.T) { }, request: []any{ TranslateField("NotExistingFieldName"), - TranslateField("SubStruct", + TranslateStruct("SubStruct", TranslateField("NotExistingFieldName"))}, }, expected: &TopLevelStruct{ @@ -151,7 +191,7 @@ func TestTranslateStruct(t *testing.T) { }, request: []any{TranslateField("Description"), TranslateField("DisplayName"), - TranslateField("SubStruct", + TranslateStruct("SubStruct", TranslateField("Description"), TranslateField("DisplayName"))}, }, @@ -169,7 +209,7 @@ func TestTranslateStruct(t *testing.T) { }, request: []any{TranslateField("Description"), TranslateField("DisplayName"), - TranslateField("SubStruct", + TranslateStruct("SubStruct", TranslateField("Description"), TranslateField("DisplayName"))}, }, @@ -192,7 +232,7 @@ func TestTranslateStruct(t *testing.T) { request: []any{ TranslateField("Description"), TranslateField("DisplayName"), - TranslateField("SubStruct", + TranslateStruct("SubStruct", TranslateField("Description"), TranslateField("DisplayName"))}, }, @@ -213,6 +253,14 @@ func TestTranslateStruct(t *testing.T) { }, expected: nil, }, + { + name: "empty slice", + args: args{ + structPtr: []string{}, + request: []any{TranslateField("Description")}, + }, + expected: []string{}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions.go b/services/graph/pkg/service/v0/api_driveitem_permissions.go index 9e94f34ff..b968b4c7a 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions.go @@ -26,7 +26,7 @@ import ( "github.com/go-chi/render" libregraph "github.com/owncloud/libre-graph-api-go" "github.com/owncloud/ocis/v2/ocis-pkg/l10n" - l10n2 "github.com/owncloud/ocis/v2/services/graph/pkg/l10n" + l10n_pkg "github.com/owncloud/ocis/v2/services/graph/pkg/l10n" "github.com/owncloud/ocis/v2/ocis-pkg/conversions" "github.com/owncloud/ocis/v2/ocis-pkg/log" @@ -637,10 +637,11 @@ func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *htt loc := r.Header.Get(l10n.HeaderAcceptLanguage) w.Header().Add("Content-Language", loc) if loc != "" && loc != "en" { - trf := l10n2.NewTranslateLocation(loc, "en") - err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf, - l10n.TranslateField("Description"), - l10n.TranslateField("DisplayName"), + trf := l10n_pkg.NewTranslateLocation(loc, "en") + err := l10n.TranslateEntity(permissions, trf, + l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues", + l10n.TranslateField("Description"), + l10n.TranslateField("DisplayName")), ) if err != nil { api.logger.Error().Err(err).Msg("tranlation error") @@ -671,10 +672,11 @@ func (api DriveItemPermissionsApi) ListSpaceRootPermissions(w http.ResponseWrite loc := r.Header.Get(l10n.HeaderAcceptLanguage) w.Header().Add("Content-Language", loc) if loc != "" && loc != "en" { - trf := l10n2.NewTranslateLocation(loc, "en") - err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf, - l10n.TranslateField("Description"), - l10n.TranslateField("DisplayName"), + trf := l10n_pkg.NewTranslateLocation(loc, "en") + err := l10n.TranslateEntity(permissions, trf, + l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues", + l10n.TranslateField("Description"), + l10n.TranslateField("DisplayName")), ) if err != nil { api.logger.Error().Err(err).Msg("tranlation error")