From fc85837d8281db5cd599c20f5bf5b54ec0269105 Mon Sep 17 00:00:00 2001 From: Roman Perekhod Date: Thu, 1 Aug 2024 19:29:19 +0200 Subject: [PATCH] added the iterator over slice and map --- .../unreleased/added-generyc-translator.md | 8 ++ ocis-pkg/l10n/l10n.go | 43 +++++++--- ocis-pkg/l10n/l10n_test.go | 83 ++++++++++++++++++- services/activitylog/pkg/service/http.go | 8 +- .../pkg/l10n/locale/ru/LC_MESSAGES/graph.po | 35 -------- .../service/v0/api_driveitem_permissions.go | 30 +++---- 6 files changed, 139 insertions(+), 68 deletions(-) create mode 100644 changelog/unreleased/added-generyc-translator.md diff --git a/changelog/unreleased/added-generyc-translator.md b/changelog/unreleased/added-generyc-translator.md new file mode 100644 index 000000000..60cc9ee8f --- /dev/null +++ b/changelog/unreleased/added-generyc-translator.md @@ -0,0 +1,8 @@ +Enhancement: Added generic way to translate composite entities + +Added a 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. + +https://github.com/owncloud/ocis/pull/9722 +https://github.com/owncloud/ocis/issues/9700 diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go index 8f3da38e6..fafb58f56 100644 --- a/ocis-pkg/l10n/l10n.go +++ b/ocis-pkg/l10n/l10n.go @@ -65,7 +65,9 @@ func TranslateLocation(t Translator, locale string) func(string, ...any) string return t.Locale(locale).Get } -// TranslateEntity function tranlate all described fields in the struct in the given locale including nested structs +// 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 @@ -86,12 +88,29 @@ func TranslateLocation(t Translator, locale string) func(string, ...any) string // TranslateField("DisplayName"))) func TranslateEntity(entity any, tr func(string, ...any) string, fields ...any) error { value := reflect.ValueOf(entity) - if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct { - // must be a pointer to a struct - return ErrStructPointer + // 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() } - if value.IsNil() { - // treat a nil struct pointer as valid + + switch value.Kind() { + case reflect.Slice, reflect.Map: + for i := 0; i < value.Len(); i++ { + nextValue := value.Index(i) + // Indirect through pointers and interfaces + if nextValue.Kind() == reflect.Ptr || nextValue.Kind() == reflect.Interface { + if nextValue.IsNil() { + // treat a nil struct pointer as valid + continue + } + nextValue = value.Index(i).Elem() + } + translateInner(nextValue, tr, fields...) + } return nil } translateInner(value, tr, fields...) @@ -111,8 +130,8 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel return } fieldName, fields := fl() - // exported field - if value.Kind() == reflect.Ptr { + // Indirect through pointers and interfaces + if value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface { if value.IsNil() { return } @@ -122,6 +141,12 @@ func translateField(value reflect.Value, tr func(string, ...any) string, fl fiel if !innerValue.IsValid() { return } + switch innerValue.Kind() { + case reflect.Slice, reflect.Map: + for i := 0; i < innerValue.Len(); i++ { + translateInner(innerValue.Index(i), tr, fields...) + } + } if isStruct(innerValue) { translateInner(innerValue, tr, fields...) return @@ -169,8 +194,6 @@ func isStruct(r reflect.Value) bool { } var ( - // ErrStructPointer is the error that a struct being validated is not specified as a pointer. - ErrStructPointer = errors.New("only a pointer to a struct can be validated") ErrUnsupportedType = errors.New("unsupported type") ) diff --git a/ocis-pkg/l10n/l10n_test.go b/ocis-pkg/l10n/l10n_test.go index 9b5ef1a54..d00d269a4 100644 --- a/ocis-pkg/l10n/l10n_test.go +++ b/ocis-pkg/l10n/l10n_test.go @@ -19,6 +19,10 @@ func TestTranslateStruct(t *testing.T) { SubStruct *InnreStruct } + type WrapperStruct struct { + StructList []*InnreStruct + } + toStrPointer := func(str string) *string { return &str } @@ -34,7 +38,68 @@ func TestTranslateStruct(t *testing.T) { wantErr bool }{ { - name: "empty struct", + name: "top level slice of struct", + args: args{ + structPtr: []*InnreStruct{ + { + Description: "inner 1", + DisplayName: toStrPointer("innerDisplayName 1"), + }, + { + Description: "inner 2", + DisplayName: toStrPointer("innerDisplayName 2"), + }, + }, + request: []any{ + TranslateField("Description"), + TranslateField("DisplayName")}, + }, + expected: []*InnreStruct{ + { + Description: "new Inner 1", + DisplayName: toStrPointer("new InnerDisplayName 1"), + }, + { + Description: "new Inner 2", + DisplayName: toStrPointer("new InnerDisplayName 2"), + }, + }, + }, + { + name: "wrapped struct full", + args: args{ + structPtr: &WrapperStruct{ + StructList: []*InnreStruct{ + { + Description: "inner 1", + DisplayName: toStrPointer("innerDisplayName 1"), + }, + { + Description: "inner 2", + DisplayName: toStrPointer("innerDisplayName 2"), + }, + }, + }, + request: []any{ + TranslateField("StructList", + TranslateField("Description"), + TranslateField("DisplayName"))}, + }, + expected: &WrapperStruct{ + StructList: []*InnreStruct{ + { + Description: "new Inner 1", + DisplayName: toStrPointer("new InnerDisplayName 1"), + }, + { + Description: "new Inner 2", + DisplayName: toStrPointer("new InnerDisplayName 2"), + }, + }, + }, + }, + { + name: "empty struct, NotExistingSubStructName", args: args{ structPtr: &TopLevelStruct{}, request: []any{ @@ -140,6 +205,14 @@ func TestTranslateStruct(t *testing.T) { }, }, }, + { + name: "nil", + args: args{ + structPtr: nil, + request: []any{TranslateField("Description")}, + }, + expected: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -163,6 +236,14 @@ func mock() func(string, ...interface{}) string { return "new Inner" case "innerDisplayName": return "new InnerDisplayName" + case "inner 1": + return "new Inner 1" + case "innerDisplayName 1": + return "new InnerDisplayName 1" + case "inner 2": + return "new Inner 2" + case "innerDisplayName 2": + return "new InnerDisplayName 2" } return s } diff --git a/services/activitylog/pkg/service/http.go b/services/activitylog/pkg/service/http.go index 5efe36651..d47e2e718 100644 --- a/services/activitylog/pkg/service/http.go +++ b/services/activitylog/pkg/service/http.go @@ -85,10 +85,6 @@ func (s *ActivitylogService) HandleGetItemActivities(w http.ResponseWriter, r *h return } - // FIXME: configurable default locale? - loc := l10n.MustGetUserLocale(r.Context(), activeUser.GetId().GetOpaqueId(), r.Header.Get(l10n.HeaderAcceptLanguage), s.valService) - t := l10n.NewTranslatorFromCommonConfig("en", _domain, "", _localeFS, _localeSubPath) - resp := GetActivitiesResponse{Activities: make([]libregraph.Activity, 0, len(evRes.GetEvents()))} for _, e := range evRes.GetEvents() { delete(toDelete, e.GetId()) @@ -168,6 +164,10 @@ func (s *ActivitylogService) HandleGetItemActivities(w http.ResponseWriter, r *h continue } + // FIXME: configurable default locale? + loc := l10n.MustGetUserLocale(r.Context(), activeUser.GetId().GetOpaqueId(), r.Header.Get(l10n.HeaderAcceptLanguage), s.valService) + t := l10n.NewTranslatorFromCommonConfig("en", _domain, "", _localeFS, _localeSubPath) + resp.Activities = append(resp.Activities, NewActivity(t.Translate(message, loc), ts, e.GetId(), vars)) } diff --git a/services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po b/services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po index e10f76e46..239b3eb78 100644 --- a/services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po +++ b/services/graph/pkg/l10n/locale/ru/LC_MESSAGES/graph.po @@ -25,38 +25,3 @@ msgstr "" #: pkg/service/v0/spacetemplates.go:37 msgid "Here you can add a description for this Space." msgstr "Здесь вы можете добавить описание этого пространства." - -#: pkg/unifiedrole/unifiedrole.go:87 -msgid "Can edit" -msgstr "" - -#. UnifiedRole Viewer, Role DisplayName (resolves directly) -#: pkg/unifiedrole/unifiedrole.go:79 -msgid "Can view" -msgstr "" - -#. default description for new spaces -#: pkg/service/v0/spacetemplates.go:31 -msgid "Here you can add a description for this Space." -msgstr "" - -#. UnifiedRole Viewer, Role Description (resolves directly) -#. UnifiedRole SpaceViewer, Role Description (resolves directly) -#: pkg/unifiedrole/unifiedrole.go:77 pkg/unifiedrole/unifiedrole.go:82 -msgid "View and download." -msgstr "" - -#: pkg/unifiedrole/unifiedrole.go:91 -msgid "View, download and edit." -msgstr "" - -#: pkg/unifiedrole/unifiedrole.go:92 -msgid "View, download and upload." -msgstr "" - -#. canEdit := "Can edit" -#. UnifiedRole Editor, Role Description (resolves directly) -#. UnifiedRole SpaseEditor, Role Description (resolves directly) -#: pkg/unifiedrole/unifiedrole.go:86 pkg/unifiedrole/unifiedrole.go:90 -msgid "View, download, upload, edit, add and delete." -msgstr "" diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions.go b/services/graph/pkg/service/v0/api_driveitem_permissions.go index 983ee06b6..9e94f34ff 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions.go @@ -638,15 +638,12 @@ func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *htt w.Header().Add("Content-Language", loc) if loc != "" && loc != "en" { trf := l10n2.NewTranslateLocation(loc, "en") - for i, role := range permissions.LibreGraphPermissionsRolesAllowedValues { - err := l10n.TranslateEntity(&role, trf, - l10n.TranslateField("Description"), - l10n.TranslateField("DisplayName")) - if err != nil { - api.logger.Warn().Err(err).Msg("tranlation error") - continue - } - permissions.LibreGraphPermissionsRolesAllowedValues[i] = role + err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf, + l10n.TranslateField("Description"), + l10n.TranslateField("DisplayName"), + ) + if err != nil { + api.logger.Error().Err(err).Msg("tranlation error") } } @@ -675,15 +672,12 @@ func (api DriveItemPermissionsApi) ListSpaceRootPermissions(w http.ResponseWrite w.Header().Add("Content-Language", loc) if loc != "" && loc != "en" { trf := l10n2.NewTranslateLocation(loc, "en") - for i, role := range permissions.LibreGraphPermissionsRolesAllowedValues { - err := l10n.TranslateEntity(&role, trf, - l10n.TranslateField("Description"), - l10n.TranslateField("DisplayName")) - if err != nil { - api.logger.Warn().Err(err).Msg("tranlation error") - continue - } - permissions.LibreGraphPermissionsRolesAllowedValues[i] = role + err := l10n.TranslateEntity(permissions.LibreGraphPermissionsRolesAllowedValues, trf, + l10n.TranslateField("Description"), + l10n.TranslateField("DisplayName"), + ) + if err != nil { + api.logger.Error().Err(err).Msg("tranlation error") } }