Skip to content

Commit 58d1694

Browse files
committed
feat: add initial support for custom entry unmarshaler
1 parent e80f029 commit 58d1694

File tree

2 files changed

+92
-23
lines changed

2 files changed

+92
-23
lines changed

v3/search.go

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -246,38 +246,19 @@ func readTag(f reflect.StructField) (string, bool) {
246246
// // ...
247247
// }
248248
func (e *Entry) Unmarshal(i interface{}) (err error) {
249-
// Make sure it's a ptr
250-
if vo := reflect.ValueOf(i).Kind(); vo != reflect.Ptr {
251-
return fmt.Errorf("ldap: cannot use %s, expected pointer to a struct", vo)
252-
}
253-
254-
sv, st := reflect.ValueOf(i).Elem(), reflect.TypeOf(i).Elem()
255-
// Make sure it's pointing to a struct
256-
if sv.Kind() != reflect.Struct {
257-
return fmt.Errorf("ldap: expected pointer to a struct, got %s", sv.Kind())
258-
}
259-
260-
for n := 0; n < st.NumField(); n++ {
261-
// Holds struct field value and type
262-
fv, ft := sv.Field(n), st.Field(n)
263-
264-
// skip unexported fields
265-
if ft.PkgPath != "" {
266-
continue
267-
}
268-
249+
return e.UnmarshalFunc(i, func(entry *Entry, ft reflect.StructField, fv reflect.Value) error {
269250
// omitempty can be safely discarded, as it's not needed when unmarshalling
270251
fieldTag, _ := readTag(ft)
271252

272253
// Fill the field with the distinguishedName if the tag key is `dn`
273254
if fieldTag == "dn" {
274255
fv.SetString(e.DN)
275-
continue
256+
return nil
276257
}
277258

278259
values := e.GetAttributeValues(fieldTag)
279260
if len(values) == 0 {
280-
continue
261+
return nil
281262
}
282263

283264
switch fv.Interface().(type) {
@@ -320,8 +301,37 @@ func (e *Entry) Unmarshal(i interface{}) (err error) {
320301
default:
321302
return fmt.Errorf("ldap: expected field to be of type string, *string, []string, int, int64, []byte, *DN, []*DN or time.Time, got %v", ft.Type)
322303
}
304+
return nil
305+
})
306+
}
307+
308+
func (e *Entry) UnmarshalFunc(i interface{},
309+
fn func(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error) error {
310+
// Make sure it's a ptr
311+
if vo := reflect.ValueOf(i).Kind(); vo != reflect.Ptr {
312+
return fmt.Errorf("ldap: cannot use %s, expected pointer to a struct", vo)
313+
}
314+
315+
sv, st := reflect.ValueOf(i).Elem(), reflect.TypeOf(i).Elem()
316+
// Make sure it's pointing to a struct
317+
if sv.Kind() != reflect.Struct {
318+
return fmt.Errorf("ldap: expected pointer to a struct, got %s", sv.Kind())
319+
}
320+
321+
for n := 0; n < st.NumField(); n++ {
322+
fv, ft := sv.Field(n), st.Field(n)
323+
324+
// skip unexported fields
325+
if ft.PkgPath != "" {
326+
continue
327+
}
328+
329+
if err := fn(e, ft, fv); err != nil {
330+
return err
331+
}
323332
}
324-
return
333+
334+
return nil
325335
}
326336

327337
// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair

v3/search_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ldap
22

33
import (
4+
"fmt"
45
"reflect"
56
"testing"
67
"time"
@@ -219,3 +220,61 @@ func TestEntry_Unmarshal(t *testing.T) {
219220
assert.Equal(t, expect, result)
220221
})
221222
}
223+
224+
func TestEntry_UnmarshalFunc(t *testing.T) {
225+
conn, err := DialURL(ldapServer)
226+
if err != nil {
227+
t.Fatalf("Failed to connect: %s\n", err)
228+
}
229+
defer conn.Close()
230+
231+
searchResult, err := conn.Search(&SearchRequest{
232+
BaseDN: baseDN,
233+
Scope: ScopeWholeSubtree,
234+
Filter: "(cn=cis-fac)",
235+
Attributes: []string{"cn", "objectClass"},
236+
})
237+
if err != nil {
238+
t.Fatalf("Failed to search: %s\n", err)
239+
}
240+
241+
type user struct {
242+
ObjectClass string `custom_tag:"objectClass"`
243+
CN string `custom_tag:"cn"`
244+
}
245+
246+
t.Run("expect custom unmarshal function to be successfull", func(t *testing.T) {
247+
for _, entry := range searchResult.Entries {
248+
var u user
249+
if err := entry.UnmarshalFunc(&u, func(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error {
250+
tagData, ok := fieldType.Tag.Lookup("custom_tag")
251+
if !ok {
252+
return nil
253+
}
254+
255+
value := entry.GetAttributeValue(tagData)
256+
// log.Printf("Marshaling field %s with tag %s and value '%s'", fieldType.Name, tagData, value)
257+
fieldValue.SetString(value)
258+
return nil
259+
}); err != nil {
260+
t.Errorf("Failed to unmarshal entry: %s\n", err)
261+
}
262+
263+
if u.CN != entry.GetAttributeValue("cn") {
264+
t.Errorf("UnmarshalFunc did not set the field correctly. Expected: %s, got: %s", entry.GetAttributeValue("cn"), u.CN)
265+
}
266+
}
267+
})
268+
269+
t.Run("expect an error within the custom unmarshal function", func(t *testing.T) {
270+
for _, entry := range searchResult.Entries {
271+
var u user
272+
err := entry.UnmarshalFunc(&u, func(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error {
273+
return fmt.Errorf("error from custom unmarshal func on field: %s", fieldType.Name)
274+
})
275+
if err == nil {
276+
t.Errorf("UnmarshalFunc should have returned an error")
277+
}
278+
}
279+
})
280+
}

0 commit comments

Comments
 (0)