added the iterator over slice and map

This commit is contained in:
Roman Perekhod
2024-08-01 19:29:19 +02:00
committed by jkoberg
parent 90bd1fc403
commit fc85837d82
6 changed files with 139 additions and 68 deletions

View File

@@ -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

View File

@@ -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")
)

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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 ""

View File

@@ -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")
}
}