Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 5434b41

Browse files
authored
Merge pull request #38 from philips-software/bugfix/user-management
Support limited user updates using legacy API
2 parents b67576d + 02f3fc3 commit 5434b41

File tree

3 files changed

+205
-31
lines changed

3 files changed

+205
-31
lines changed

iam/user.go

+44-27
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package iam
22

3-
import "time"
3+
import (
4+
"time"
5+
)
46

57
// User represents a user profile in IAM
68
type User struct {
7-
PreferredLanguage string `json:"preferredLanguage"`
8-
EmailAddress string `json:"emailAddress"`
9-
ID string `json:"id"`
10-
LoginID string `json:"loginId"`
11-
Name struct {
12-
Given string `json:"given"`
13-
Family string `json:"family"`
14-
} `json:"name"`
9+
PreferredLanguage string `json:"preferredLanguage"`
10+
EmailAddress string `json:"emailAddress"`
11+
ID string `json:"id"`
12+
LoginID string `json:"loginId"`
13+
Name Name `json:"name"`
1514
ManagingOrganization string `json:"managingOrganization"`
1615
PasswordStatus struct {
1716
PasswordExpiresOn time.Time `json:"passwordExpiresOn"`
@@ -49,16 +48,17 @@ type Person struct {
4948
ManagingOrganization string `json:"managingOrganization,omitempty"`
5049
PreferredLanguage string `json:"preferredLanguage,omitempty"`
5150
IsAgeValidated string `json:"isAgeValidated,omitempty"`
51+
Password string `json:"password,omitempty"`
5252
Disabled bool `json:"disabled"`
5353
Loaded bool `json:"-"`
5454
}
5555

5656
// Contact describes contact details of a Profile
5757
type Contact struct {
58-
EmailAddress string
59-
MobilePhone string
60-
WorkPhone string
61-
HomePhone string
58+
EmailAddress string `json:"emailAddress,omitempty"`
59+
MobilePhone string `json:"mobilePhone,omitempty"`
60+
WorkPhone string `json:"workPhone,omitempty"`
61+
HomePhone string `json:"homePhone,omitempty"`
6262
}
6363

6464
// Address describes an address of a Profile
@@ -83,15 +83,30 @@ type Period struct {
8383
// Profile describes the response from legacy User APIs
8484
// The response does not correspond to the object that is used to create a user
8585
type Profile struct {
86-
GivenName string `json:"givenName"`
87-
MiddleName string `json:"middleName"`
88-
FamilyName string `json:"familyName"`
89-
BirthDay string `json:"birthDay"`
90-
DisplayName string `json:"displayName"`
91-
Gender string `json:"gender" enum:"Male|Female"`
92-
Country string `json:"country"`
93-
Addresses []Address `json:"addresses"`
94-
PreferredLanguage string `json:"preferredLanguage"`
86+
ID string `json:"-"`
87+
GivenName string `json:"givenName"`
88+
MiddleName string `json:"middleName"`
89+
FamilyName string `json:"familyName"`
90+
BirthDay *time.Time `json:"birthDay,omitempty"`
91+
DisplayName string `json:"displayName,omitempty"`
92+
Gender string `json:"gender,omitempty" enum:"Male|Female"`
93+
Country string `json:"country,omitempty"`
94+
Contact Contact `json:"contact,omitempty"`
95+
Addresses []Address `json:"addresses,omitempty,omitempty"`
96+
PreferredLanguage string `json:"preferredLanguage,omitempty"`
97+
}
98+
99+
// MergeUser merges User into legacy Profile
100+
func (p *Profile) MergeUser(user *User) {
101+
p.GivenName = user.Name.Given
102+
p.FamilyName = user.Name.Family
103+
// See INC0058741 for backround for this workaround
104+
if p.MiddleName == "" {
105+
p.MiddleName = " "
106+
}
107+
p.ID = user.ID
108+
p.Contact.EmailAddress = user.EmailAddress
109+
p.PreferredLanguage = user.PreferredLanguage
95110
}
96111

97112
// Name entity
@@ -110,9 +125,11 @@ type TelecomEntry struct {
110125

111126
// AddressEntry entity
112127
type AddressEntry struct {
113-
Use string `json:"use"`
114-
City string `json:"city"`
115-
State string `json:"state"`
116-
Country string `json:"country"`
117-
Postalcode string `json:"postalcode"`
128+
Use string `json:"use"`
129+
Text string `json:"text"`
130+
Line []string `json:"line"`
131+
City string `json:"city"`
132+
State string `json:"state"`
133+
Country string `json:"country"`
134+
Postalcode string `json:"postalcode"`
118135
}

iam/users_service.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ package iam
22

33
import (
44
"fmt"
5+
validator "github.com/go-playground/validator/v10"
56
"net/http"
67
"strconv"
7-
8-
validator "github.com/go-playground/validator/v10"
98
)
109

1110
const (
@@ -75,7 +74,7 @@ func (u *UsersService) CreateUser(person Person) (*User, *Response, error) {
7574
if err != nil {
7675
return nil, nil, err
7776
}
78-
req.Header.Set("api-version", userAPIVersion)
77+
req.Header.Set("api-version", "3")
7978

8079
var bundleResponse interface{}
8180

@@ -313,6 +312,47 @@ func (u *UsersService) GetUserIDByLoginID(loginID string) (string, *Response, er
313312
return user.ID, resp, nil
314313
}
315314

315+
// LegacyUpdateUser updates the user profile
316+
func (u *UsersService) LegacyUpdateUser(profile Profile) (*Profile, *Response, error) {
317+
req, _ := u.client.newRequest(IDM, "PUT", "security/users/"+profile.ID, profile, nil)
318+
req.Header.Set("api-version", "1")
319+
320+
var responseStruct struct {
321+
Exchange struct {
322+
UserUUID string `json:"userUUID"`
323+
LoginID string `json:"loginId"`
324+
Profile Profile `json:"profile"`
325+
} `json:"exchange"`
326+
ResponseCode string `json:"responseCode"`
327+
ResponseMessage string `json:"responseMessage"`
328+
}
329+
resp, err := u.client.do(req, &responseStruct)
330+
if err != nil {
331+
return nil, resp, err
332+
}
333+
return &responseStruct.Exchange.Profile, resp, err
334+
}
335+
336+
// LegacyGetUserByUUID looks the a user by UUID using the legacy API
337+
func (u *UsersService) LegacyGetUserByUUID(uuid string) (*Profile, *Response, error) {
338+
req, _ := u.client.newRequest(IDM, "GET", "security/users/"+uuid, nil, nil)
339+
req.Header.Set("api-version", "1")
340+
341+
var responseStruct struct {
342+
Exchange struct {
343+
LoginID string `json:"loginId"`
344+
Profile Profile `json:"profile"`
345+
} `json:"exchange"`
346+
ResponseCode string `json:"responseCode"`
347+
ResponseMessage string `json:"responseMessage"`
348+
}
349+
resp, err := u.client.do(req, &responseStruct)
350+
if err != nil {
351+
return nil, resp, err
352+
}
353+
return &responseStruct.Exchange.Profile, resp, err
354+
}
355+
316356
// LegacyGetUserIDByLoginID looks up the UUID of a user by LoginID (email address)
317357
func (u *UsersService) LegacyGetUserIDByLoginID(loginID string) (string, *Response, error) {
318358
opt := &GetUserOptions{

iam/users_service_test.go

+118-1
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,13 @@ func TestCreateUserSelfRegistration(t *testing.T) {
135135
IsAgeValidated: "true",
136136
}
137137
user, resp, err := client.Users.CreateUser(person)
138+
if !assert.Nil(t, err) {
139+
return
140+
}
138141
if !assert.NotNil(t, resp) {
139142
return
140143
}
141144
assert.Equal(t, http.StatusOK, resp.StatusCode)
142-
assert.Nil(t, err)
143145
if !assert.NotNil(t, user) {
144146
return
145147
}
@@ -503,3 +505,118 @@ func actionRequestHandler(t *testing.T, paramName, informationalMessage string,
503505
}
504506
}
505507
}
508+
509+
func TestLegacyPaths(t *testing.T) {
510+
teardown := setup(t)
511+
defer teardown()
512+
513+
userUUID := "44d20214-7879-4e35-923d-f9d4e01c9746"
514+
515+
muxIDM.HandleFunc("/authorize/identity/User", func(w http.ResponseWriter, request *http.Request) {
516+
switch request.Method {
517+
case http.MethodGet:
518+
w.WriteHeader(http.StatusOK)
519+
_, _ = w.Write([]byte(`{
520+
"total": 1,
521+
"entry": [
522+
{
523+
"preferredLanguage": "en-us",
524+
"loginId": "ron",
525+
"emailAddress": "[email protected]",
526+
"id": "` + userUUID + `",
527+
"managingOrganization": "daedbeaf-888d-4a26-8c1d-578e97365aaa",
528+
"name": {
529+
"given": "Ron",
530+
"family": "Swanson"
531+
},
532+
"memberships": [],
533+
"passwordStatus": {
534+
"passwordExpiresOn": "2023-05-09T16:41:29Z",
535+
"passwordChangedOn": "2020-05-09T16:41:29Z"
536+
},
537+
"accountStatus": {
538+
"mfaStatus": "REQUIRED",
539+
"lastLoginTime": null,
540+
"emailVerified": true,
541+
"disabled": false
542+
},
543+
"consentedApps": [
544+
"default default default"
545+
]
546+
}
547+
]
548+
}`))
549+
550+
}
551+
})
552+
553+
muxIDM.HandleFunc("/security/users/"+userUUID,
554+
func(w http.ResponseWriter, request *http.Request) {
555+
switch request.Method {
556+
case http.MethodGet:
557+
w.WriteHeader(http.StatusOK)
558+
_, _ = w.Write([]byte(`{
559+
"exchange": {
560+
"loginId": "ron",
561+
"profile": {
562+
"contact": {
563+
"emailAddress": "[email protected]",
564+
"mobilePhone": "1234567890"
565+
},
566+
"givenName": "Ron",
567+
"familyName": "Swanson",
568+
"addresses": [],
569+
"disabled": false,
570+
"preferredLanguage": "en-US"
571+
}
572+
},
573+
"responseCode": "200",
574+
"responseMessage": "Success"
575+
}`))
576+
case http.MethodPut:
577+
w.WriteHeader(http.StatusOK)
578+
_, _ = w.Write([]byte(`{
579+
"exchange": {
580+
"userUUID": "` + userUUID + `",
581+
"loginId": "ron",
582+
"profile": {
583+
"contact": {
584+
"emailAddress": "[email protected]",
585+
"mobilePhone": "31612345678"
586+
},
587+
"givenName": "Ron",
588+
"familyName": "Swanson",
589+
"addresses": [],
590+
"disabled": false,
591+
"preferredLanguage": "en-US"
592+
}
593+
},
594+
"responseCode": "200",
595+
"responseMessage": "Success"
596+
}`))
597+
}
598+
})
599+
600+
user, resp, err := client.Users.LegacyGetUserByUUID(userUUID)
601+
if !assert.NotNil(t, resp) {
602+
return
603+
}
604+
if !assert.NotNil(t, user) {
605+
return
606+
}
607+
assert.Nil(t, err)
608+
assert.Equal(t, "Swanson", user.FamilyName)
609+
user.GivenName = "Ron"
610+
user.ID = userUUID
611+
profile, resp, err := client.Users.LegacyUpdateUser(*user)
612+
if !assert.Nil(t, err) {
613+
return
614+
}
615+
if !assert.NotNil(t, resp) {
616+
return
617+
}
618+
if !assert.NotNil(t, profile) {
619+
return
620+
}
621+
assert.Equal(t, "Swanson", profile.FamilyName)
622+
}

0 commit comments

Comments
 (0)