From d88e099e1f2000e54c320157b7f2f47aa9b8ce67 Mon Sep 17 00:00:00 2001 From: Taras Sologub Date: Tue, 25 May 2021 07:04:27 +0200 Subject: [PATCH 1/5] List deleted applications API --- msgraph/applications.go | 35 +++++++++++++++++++++++++++++++++++ msgraph/applications_test.go | 21 +++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/msgraph/applications.go b/msgraph/applications.go index 5abcc66c..0ffd6bad 100644 --- a/msgraph/applications.go +++ b/msgraph/applications.go @@ -148,6 +148,41 @@ func (c *ApplicationsClient) Delete(ctx context.Context, id string) (int, error) return status, nil } +// ListDeleted retrieves a list of recently deleted applications, optionally filtered using OData. +func (c *ApplicationsClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { + params := url.Values{} + if filter != "" { + params.Add("$filter", filter) + } + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: "/directory/deleteditems/microsoft.graph.application", + Params: params, + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, err + } + defer resp.Body.Close() + respBody, _ := ioutil.ReadAll(resp.Body) + var data struct { + DeletedApps []struct { + Type string `json:"@odata.type"` + Id string `json:"id"` + } `json:"value"` + } + if err = json.Unmarshal(respBody, &data); err != nil { + return nil, status, err + } + ret := make([]string, len(data.DeletedApps)) + for i, v := range data.DeletedApps { + ret[i] = v.Id + } + return &ret, status, nil +} + // AddPassword appends a new password credential to an Application. func (c *ApplicationsClient) AddPassword(ctx context.Context, applicationId string, passwordCredential PasswordCredential) (*PasswordCredential, int, error) { var status int diff --git a/msgraph/applications_test.go b/msgraph/applications_test.go index 0f70983b..0edb590c 100644 --- a/msgraph/applications_test.go +++ b/msgraph/applications_test.go @@ -39,6 +39,7 @@ func TestApplicationsClient(t *testing.T) { testApplicationsClient_RemovePassword(t, c, app, pwd) testApplicationsClient_List(t, c) testApplicationsClient_Delete(t, c, *app.ID) + testApplicationsClient_ListDeleted(t, c) } func TestApplicationsClient_groupMembershipClaims(t *testing.T) { @@ -195,3 +196,23 @@ func testApplicationsClient_RemovePassword(t *testing.T, c ApplicationsClientTes t.Fatalf("ApplicationsClient.RemovePassword(): invalid status: %d", status) } } + +func testApplicationsClient_ListDeleted(t *testing.T, c ApplicationsClientTest) (deletedApps *[]string) { + deletedApps, status, err := c.client.ListDeleted(c.connection.Context, "") + if err != nil { + t.Fatalf("ApplicationsClient.ListDeleted(): %v", err) + } + + if status < 200 || status >= 300 { + t.Fatalf("ApplicationsClient.ListDeleted(): invalid status: %d", status) + } + + if deletedApps == nil { + t.Fatal("ApplicationsClient.ListDeleted(): deletedApps was nil") + } + + if len(*deletedApps) == 0 { + t.Fatal("ApplicationsClient.ListDeleted(): expected at least 1 deleted application. was: 0") + } + return +} From a4ea0196bd6b006d1f0dd8b79899407a904e5d78 Mon Sep 17 00:00:00 2001 From: Taras Sologub Date: Tue, 25 May 2021 07:49:41 +0200 Subject: [PATCH 2/5] List deleted groups API --- msgraph/groups.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/msgraph/groups.go b/msgraph/groups.go index df47c006..11b1b94b 100644 --- a/msgraph/groups.go +++ b/msgraph/groups.go @@ -144,6 +144,41 @@ func (c *GroupsClient) Delete(ctx context.Context, id string) (int, error) { return status, nil } +// ListDeleted retrieves a list of recently deleted O365 groups, optionally filtered using OData. +func (c *GroupsClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { + params := url.Values{} + if filter != "" { + params.Add("$filter", filter) + } + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: "/directory/deleteditems/microsoft.graph.group", + Params: params, + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, err + } + defer resp.Body.Close() + respBody, _ := ioutil.ReadAll(resp.Body) + var data struct { + DeletedGroups []struct { + Type string `json:"@odata.type"` + Id string `json:"id"` + } `json:"value"` + } + if err = json.Unmarshal(respBody, &data); err != nil { + return nil, status, err + } + ret := make([]string, len(data.DeletedGroups)) + for i, v := range data.DeletedGroups { + ret[i] = v.Id + } + return &ret, status, nil +} + // ListMembers retrieves the members of the specified Group. // id is the object ID of the group. func (c *GroupsClient) ListMembers(ctx context.Context, id string) (*[]string, int, error) { From 2383aea5cc9b1de770aa436ead2fb5ee8f12c5f1 Mon Sep 17 00:00:00 2001 From: Taras Sologub Date: Tue, 25 May 2021 07:55:41 +0200 Subject: [PATCH 3/5] List deleted users API --- msgraph/users.go | 35 +++++++++++++++++++++++++++++++++++ msgraph/users_test.go | 21 +++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/msgraph/users.go b/msgraph/users.go index b125af3b..b71ad0a7 100644 --- a/msgraph/users.go +++ b/msgraph/users.go @@ -142,6 +142,41 @@ func (c *UsersClient) Delete(ctx context.Context, id string) (int, error) { return status, nil } +// ListDeleted retrieves a list of recently deleted users, optionally filtered using OData. +func (c *UsersClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { + params := url.Values{} + if filter != "" { + params.Add("$filter", filter) + } + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: "/directory/deleteditems/microsoft.graph.user", + Params: params, + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, err + } + defer resp.Body.Close() + respBody, _ := ioutil.ReadAll(resp.Body) + var data struct { + DeletedUsers []struct { + Type string `json:"@odata.type"` + Id string `json:"id"` + } `json:"value"` + } + if err = json.Unmarshal(respBody, &data); err != nil { + return nil, status, err + } + ret := make([]string, len(data.DeletedUsers)) + for i, v := range data.DeletedUsers { + ret[i] = v.Id + } + return &ret, status, nil +} + // ListGroupMemberships returns a list of Groups the user is member of, optionally filtered using OData. func (c *UsersClient) ListGroupMemberships(ctx context.Context, id string, filter string) (*[]Group, int, error) { params := url.Values{} diff --git a/msgraph/users_test.go b/msgraph/users_test.go index d86c96bf..852ff803 100644 --- a/msgraph/users_test.go +++ b/msgraph/users_test.go @@ -71,6 +71,7 @@ func TestUsersClient(t *testing.T) { testGroupsClient_Delete(t, g, *groupChild.ID) testUsersClient_Delete(t, c, *user.ID) + testUsersClient_ListDeleted(t, c) } func testUsersClient_Create(t *testing.T, c UsersClientTest, u msgraph.User) (user *msgraph.User) { @@ -151,3 +152,23 @@ func testUsersClient_ListGroupMemberships(t *testing.T, c UsersClientTest, id st return } + +func testUsersClient_ListDeleted(t *testing.T, c UsersClientTest) (deletedUsers *[]string) { + deletedUsers, status, err := c.client.ListDeleted(c.connection.Context, "") + if err != nil { + t.Fatalf("UsersClient.ListDeleted(): %v", err) + } + + if status < 200 || status >= 300 { + t.Fatalf("UsersClient.ListDeleted(): invalid status: %d", status) + } + + if deletedUsers == nil { + t.Fatal("UsersClient.ListDeleted(): deletedUsers was nil") + } + + if len(*deletedUsers) == 0 { + t.Fatal("UsersClient.ListDeleted(): expected at least 1 deleted user. was: 0") + } + return +} From 839bfe38d29ad029fe3df5a94d33d2759bd9436f Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 27 May 2021 14:58:25 +0100 Subject: [PATCH 4/5] Return deleted items as native type, additional test check for deleted items, add some missing fields --- msgraph/applications.go | 13 +-- msgraph/applications_test.go | 17 ++-- msgraph/groups.go | 13 +-- msgraph/models.go | 159 +++++++++++++++++++---------------- msgraph/users.go | 13 +-- msgraph/users_test.go | 17 ++-- 6 files changed, 120 insertions(+), 112 deletions(-) diff --git a/msgraph/applications.go b/msgraph/applications.go index 0ffd6bad..db8e75d0 100644 --- a/msgraph/applications.go +++ b/msgraph/applications.go @@ -149,7 +149,7 @@ func (c *ApplicationsClient) Delete(ctx context.Context, id string) (int, error) } // ListDeleted retrieves a list of recently deleted applications, optionally filtered using OData. -func (c *ApplicationsClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { +func (c *ApplicationsClient) ListDeleted(ctx context.Context, filter string) (*[]Application, int, error) { params := url.Values{} if filter != "" { params.Add("$filter", filter) @@ -168,19 +168,12 @@ func (c *ApplicationsClient) ListDeleted(ctx context.Context, filter string) (*[ defer resp.Body.Close() respBody, _ := ioutil.ReadAll(resp.Body) var data struct { - DeletedApps []struct { - Type string `json:"@odata.type"` - Id string `json:"id"` - } `json:"value"` + DeletedApps []Application `json:"value"` } if err = json.Unmarshal(respBody, &data); err != nil { return nil, status, err } - ret := make([]string, len(data.DeletedApps)) - for i, v := range data.DeletedApps { - ret[i] = v.Id - } - return &ret, status, nil + return &data.DeletedApps, status, nil } // AddPassword appends a new password credential to an Application. diff --git a/msgraph/applications_test.go b/msgraph/applications_test.go index 0edb590c..28312c0b 100644 --- a/msgraph/applications_test.go +++ b/msgraph/applications_test.go @@ -39,7 +39,7 @@ func TestApplicationsClient(t *testing.T) { testApplicationsClient_RemovePassword(t, c, app, pwd) testApplicationsClient_List(t, c) testApplicationsClient_Delete(t, c, *app.ID) - testApplicationsClient_ListDeleted(t, c) + testApplicationsClient_ListDeleted(t, c, *app.ID) } func TestApplicationsClient_groupMembershipClaims(t *testing.T) { @@ -197,22 +197,29 @@ func testApplicationsClient_RemovePassword(t *testing.T, c ApplicationsClientTes } } -func testApplicationsClient_ListDeleted(t *testing.T, c ApplicationsClientTest) (deletedApps *[]string) { +func testApplicationsClient_ListDeleted(t *testing.T, c ApplicationsClientTest, expectedId string) (deletedApps *[]msgraph.Application) { deletedApps, status, err := c.client.ListDeleted(c.connection.Context, "") if err != nil { t.Fatalf("ApplicationsClient.ListDeleted(): %v", err) } - if status < 200 || status >= 300 { t.Fatalf("ApplicationsClient.ListDeleted(): invalid status: %d", status) } - if deletedApps == nil { t.Fatal("ApplicationsClient.ListDeleted(): deletedApps was nil") } - if len(*deletedApps) == 0 { t.Fatal("ApplicationsClient.ListDeleted(): expected at least 1 deleted application. was: 0") } + found := false + for _, app := range *deletedApps { + if app.ID != nil && *app.ID == expectedId { + found = true + break + } + } + if !found { + t.Fatalf("ApplicationsClient.ListDeleted(): expected appId %q in result", expectedId) + } return } diff --git a/msgraph/groups.go b/msgraph/groups.go index 11b1b94b..a51dd6d8 100644 --- a/msgraph/groups.go +++ b/msgraph/groups.go @@ -145,7 +145,7 @@ func (c *GroupsClient) Delete(ctx context.Context, id string) (int, error) { } // ListDeleted retrieves a list of recently deleted O365 groups, optionally filtered using OData. -func (c *GroupsClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { +func (c *GroupsClient) ListDeleted(ctx context.Context, filter string) (*[]Group, int, error) { params := url.Values{} if filter != "" { params.Add("$filter", filter) @@ -164,19 +164,12 @@ func (c *GroupsClient) ListDeleted(ctx context.Context, filter string) (*[]strin defer resp.Body.Close() respBody, _ := ioutil.ReadAll(resp.Body) var data struct { - DeletedGroups []struct { - Type string `json:"@odata.type"` - Id string `json:"id"` - } `json:"value"` + DeletedGroups []Group `json:"value"` } if err = json.Unmarshal(respBody, &data); err != nil { return nil, status, err } - ret := make([]string, len(data.DeletedGroups)) - for i, v := range data.DeletedGroups { - ret[i] = v.Id - } - return &ret, status, nil + return &data.DeletedGroups, status, nil } // ListMembers retrieves the members of the specified Group. diff --git a/msgraph/models.go b/msgraph/models.go index 4fd0d06e..9787d6e9 100644 --- a/msgraph/models.go +++ b/msgraph/models.go @@ -29,31 +29,38 @@ type ApiPreAuthorizedApplication struct { // Application describes an Application object. type Application struct { - ID *string `json:"id,omitempty"` - AddIns *[]AddIn `json:"addIns,omitempty"` - Api *ApplicationApi `json:"api,omitempty"` - AppId *string `json:"appId,omitempty"` - AppRoles *[]AppRole `json:"appRoles,omitempty"` - CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` - DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - GroupMembershipClaims *[]GroupMembershipClaim `json:"groupMembershipClaims,omitempty"` - IdentifierUris *[]string `json:"identifierUris,omitempty"` - Info *InformationalUrl `json:"info,omitempty"` - IsFallbackPublicClient *bool `json:"isFallbackPublicCLient,omitempty"` - KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` - Oauth2RequiredPostResponse *bool `json:"oauth2RequiredPostResponse,omitempty"` - OnPremisesPublishing *OnPremisesPublishing `json:"onPremisePublishing,omitempty"` - OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` - ParentalControlSettings *ParentalControlSettings `json:"parentalControlSettings,omitempty"` - PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` - PublicClient *PublicClient `json:"publicClient,omitempty"` - PublisherDomain *string `json:"publisherDomain,omitempty"` - RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` - SignInAudience SignInAudience `json:"signInAudience,omitempty"` - Tags *[]string `json:"tags,omitempty"` - TokenEncryptionKeyId *string `json:"tokenEncryptionKeyId,omitempty"` - Web *ApplicationWeb `json:"web,omitempty"` + ID *string `json:"id,omitempty"` + AddIns *[]AddIn `json:"addIns,omitempty"` + Api *ApplicationApi `json:"api,omitempty"` + AppId *string `json:"appId,omitempty"` + AppRoles *[]AppRole `json:"appRoles,omitempty"` + CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` + DefaultRedirectUri *string `json:"defaultRedirectUri,omitempty"` + DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` + DisabledByMicrosoftStatus interface{} `json:"disabledByMicrosoftStatus,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + GroupMembershipClaims *[]GroupMembershipClaim `json:"groupMembershipClaims,omitempty"` + IdentifierUris *[]string `json:"identifierUris,omitempty"` + Info *InformationalUrl `json:"info,omitempty"` + IsAuthorizationServiceEnabled *bool `json:"isAuthorizationServiceEnabled,omitempty"` + IsDeviceOnlyAuthSupported *bool `json:"isDeviceOnlyAuthSupported,omitempty"` + IsFallbackPublicClient *bool `json:"isFallbackPublicClient,omitempty"` + IsManagementRestricted *bool `json:"isManagementRestricted,omitempty"` + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + Oauth2RequirePostResponse *bool `json:"oauth2RequirePostResponse,omitempty"` + OnPremisesPublishing *OnPremisesPublishing `json:"onPremisePublishing,omitempty"` + OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` + ParentalControlSettings *ParentalControlSettings `json:"parentalControlSettings,omitempty"` + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + PublicClient *PublicClient `json:"publicClient,omitempty"` + PublisherDomain *string `json:"publisherDomain,omitempty"` + RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` + SignInAudience SignInAudience `json:"signInAudience,omitempty"` + Tags *[]string `json:"tags,omitempty"` + TokenEncryptionKeyId *string `json:"tokenEncryptionKeyId,omitempty"` + UniqueName *string `json:"uniqueName,omitempty"` + VerifiedPublisher *VerifiedPublisher `json:"verifiedPublisher,omitempty"` + Web *ApplicationWeb `json:"web,omitempty"` Owners *[]string `json:"owners@odata.bind,omitempty"` } @@ -819,53 +826,61 @@ type SingleSignOnField struct { // User describes a User object. type User struct { - ID *string `json:"id,omitempty"` - AboutMe *string `json:"aboutMe,omitempty"` - AccountEnabled *bool `json:"accountEnabled,omitempty"` - BusinessPhones *[]string `json:"businessPhones,omitempty"` - City *string `json:"city,omitempty"` - CompanyName *string `json:"companyName,omitempty"` - Country *string `json:"country,omitempty"` - CreationType *string `json:"creationType,omitempty"` - Department *string `json:"department,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - EmployeeId *string `json:"employeeId,omitempty"` - ExternalUserState *string `json:"externalUserState,omitempty"` - FaxNumber *string `json:"faxNumber,omitempty"` - GivenName *string `json:"givenName,omitempty"` - ImAddresses *[]string `json:"imAddresses,omitempty"` - Interests *[]string `json:"interests,omitempty"` - JobTitle *string `json:"jobTitle,omitempty"` - Mail *string `json:"mail,omitempty"` - MailNickname *string `json:"mailNickname,omitempty"` - MobilePhone *string `json:"mobilePhone,omitempty"` - MySite *string `json:"mySite,omitempty"` - OfficeLocation *string `json:"officeLocation,omitempty"` - OnPremisesDistinguishedName *string `json:"onPremisesDistinguishedName,omitempty"` - OnPremisesDomainName *string `json:"onPremisesDomainName,omitempty"` - OnPremisesImmutableId *string `json:"onPremisesImmutableId,omitempty"` - OnPremisesSamAccountName *string `json:"onPremisesSamAccountName,omitempty"` - OnPremisesSecurityIdentifier *string `json:"onPremisesSecurityIdentifier,omitempty"` - OnPremisesSyncEnabled *bool `json:"onPremisesSyncEnabled,omitempty"` - OnPremisesUserPrincipalName *string `json:"onPremisesUserPrincipalName,omitempty"` - OtherMails *[]string `json:"otherMails,omitempty"` - PasswordPolicies *string `json:"passwordPolicies,omitempty"` - PastProjects *[]string `json:"pastProjects,omitempty"` - PostalCode *string `json:"postalCode,omitempty"` - PreferredDataLocation *string `json:"preferredDataLocation,omitempty"` - PreferredLanguage *string `json:"preferredLanguage,omitempty"` - PreferredName *string `json:"preferredName,omitempty"` - ProxyAddresses *[]string `json:"proxyAddresses,omitempty"` - Responsibilities *[]string `json:"responsibilities,omitempty"` - Schools *[]string `json:"schools,omitempty"` - ShowInAddressList *bool `json:"showInAddressList,omitempty"` - Skills *[]string `json:"skills,omitempty"` - State *string `json:"state,omitempty"` - StreetAddress *string `json:"streetAddress,omitempty"` - Surname *string `json:"surname,omitempty"` - UsageLocation *string `json:"usageLocation,omitempty"` - UserPrincipalName *string `json:"userPrincipalName,omitempty"` - UserType *string `json:"userType,omitempty"` + ID *string `json:"id,omitempty"` + AboutMe *string `json:"aboutMe,omitempty"` + AccountEnabled *bool `json:"accountEnabled,omitempty"` + BusinessPhones *[]string `json:"businessPhones,omitempty"` + City *string `json:"city,omitempty"` + CompanyName *string `json:"companyName,omitempty"` + Country *string `json:"country,omitempty"` + CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` + CreationType *string `json:"creationType,omitempty"` + DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` + Department *string `json:"department,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + EmployeeHireDate *time.Time `json:"employeeHireDate,omitempty"` + EmployeeId *string `json:"employeeId,omitempty"` + EmployeeType *string `json:"employeeType,omitempty"` + ExternalUserState *string `json:"externalUserState,omitempty"` + FaxNumber *string `json:"faxNumber,omitempty"` + GivenName *string `json:"givenName,omitempty"` + ImAddresses *[]string `json:"imAddresses,omitempty"` + Interests *[]string `json:"interests,omitempty"` + IsManagementRestricted *bool `json:"isManagementRestricted,omitempty"` + IsResourceAccount *bool `json:"isResourceAccount,omitempty"` + JobTitle *string `json:"jobTitle,omitempty"` + Mail *string `json:"mail,omitempty"` + MailNickname *string `json:"mailNickname,omitempty"` + MobilePhone *string `json:"mobilePhone,omitempty"` + MySite *string `json:"mySite,omitempty"` + OfficeLocation *string `json:"officeLocation,omitempty"` + OnPremisesDistinguishedName *string `json:"onPremisesDistinguishedName,omitempty"` + OnPremisesDomainName *string `json:"onPremisesDomainName,omitempty"` + OnPremisesLastSyncDateTime *string `json:"onPremisesLastSyncDateTime,omitempty"` + OnPremisesSamAccountName *string `json:"onPremisesSamAccountName,omitempty"` + OnPremisesSecurityIdentifier *string `json:"onPremisesSecurityIdentifier,omitempty"` + OnPremisesSyncEnabled *bool `json:"onPremisesSyncEnabled,omitempty"` + OnPremisesUserPrincipalName *string `json:"onPremisesUserPrincipalName,omitempty"` + OtherMails *[]string `json:"otherMails,omitempty"` + PasswordPolicies *string `json:"passwordPolicies,omitempty"` + PastProjects *[]string `json:"pastProjects,omitempty"` + PostalCode *string `json:"postalCode,omitempty"` + PreferredDataLocation *string `json:"preferredDataLocation,omitempty"` + PreferredLanguage *string `json:"preferredLanguage,omitempty"` + PreferredName *string `json:"preferredName,omitempty"` + ProxyAddresses *[]string `json:"proxyAddresses,omitempty"` + RefreshTokensValidFromDateTime *time.Time `json:"refreshTokensValidFromDateTime,omitempty"` + Responsibilities *[]string `json:"responsibilities,omitempty"` + Schools *[]string `json:"schools,omitempty"` + ShowInAddressList *bool `json:"showInAddressList,omitempty"` + SignInSessionsValidFromDateTime *time.Time `json:"signInSessionsValidFromDateTime,omitempty"` + Skills *[]string `json:"skills,omitempty"` + State *string `json:"state,omitempty"` + StreetAddress *string `json:"streetAddress,omitempty"` + Surname *string `json:"surname,omitempty"` + UsageLocation *string `json:"usageLocation,omitempty"` + UserPrincipalName *string `json:"userPrincipalName,omitempty"` + UserType *string `json:"userType,omitempty"` PasswordProfile *UserPasswordProfile `json:"passwordProfile,omitempty"` } diff --git a/msgraph/users.go b/msgraph/users.go index b71ad0a7..149751c3 100644 --- a/msgraph/users.go +++ b/msgraph/users.go @@ -143,7 +143,7 @@ func (c *UsersClient) Delete(ctx context.Context, id string) (int, error) { } // ListDeleted retrieves a list of recently deleted users, optionally filtered using OData. -func (c *UsersClient) ListDeleted(ctx context.Context, filter string) (*[]string, int, error) { +func (c *UsersClient) ListDeleted(ctx context.Context, filter string) (*[]User, int, error) { params := url.Values{} if filter != "" { params.Add("$filter", filter) @@ -162,19 +162,12 @@ func (c *UsersClient) ListDeleted(ctx context.Context, filter string) (*[]string defer resp.Body.Close() respBody, _ := ioutil.ReadAll(resp.Body) var data struct { - DeletedUsers []struct { - Type string `json:"@odata.type"` - Id string `json:"id"` - } `json:"value"` + DeletedUsers []User `json:"value"` } if err = json.Unmarshal(respBody, &data); err != nil { return nil, status, err } - ret := make([]string, len(data.DeletedUsers)) - for i, v := range data.DeletedUsers { - ret[i] = v.Id - } - return &ret, status, nil + return &data.DeletedUsers, status, nil } // ListGroupMemberships returns a list of Groups the user is member of, optionally filtered using OData. diff --git a/msgraph/users_test.go b/msgraph/users_test.go index 852ff803..2d199bd9 100644 --- a/msgraph/users_test.go +++ b/msgraph/users_test.go @@ -71,7 +71,7 @@ func TestUsersClient(t *testing.T) { testGroupsClient_Delete(t, g, *groupChild.ID) testUsersClient_Delete(t, c, *user.ID) - testUsersClient_ListDeleted(t, c) + testUsersClient_ListDeleted(t, c, *user.ID) } func testUsersClient_Create(t *testing.T, c UsersClientTest, u msgraph.User) (user *msgraph.User) { @@ -153,22 +153,29 @@ func testUsersClient_ListGroupMemberships(t *testing.T, c UsersClientTest, id st return } -func testUsersClient_ListDeleted(t *testing.T, c UsersClientTest) (deletedUsers *[]string) { +func testUsersClient_ListDeleted(t *testing.T, c UsersClientTest, expectedId string) (deletedUsers *[]msgraph.User) { deletedUsers, status, err := c.client.ListDeleted(c.connection.Context, "") if err != nil { t.Fatalf("UsersClient.ListDeleted(): %v", err) } - if status < 200 || status >= 300 { t.Fatalf("UsersClient.ListDeleted(): invalid status: %d", status) } - if deletedUsers == nil { t.Fatal("UsersClient.ListDeleted(): deletedUsers was nil") } - if len(*deletedUsers) == 0 { t.Fatal("UsersClient.ListDeleted(): expected at least 1 deleted user. was: 0") } + found := false + for _, user := range *deletedUsers { + if user.ID != nil && *user.ID == expectedId { + found = true + break + } + } + if !found { + t.Fatalf("UsersClient.ListDeleted(): expected userId %q in result", expectedId) + } return } From bb876d91e01914730f7ffdcb64f99f09592613a2 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 27 May 2021 15:23:56 +0100 Subject: [PATCH 5/5] todo for missing test --- msgraph/groups.go | 1 + 1 file changed, 1 insertion(+) diff --git a/msgraph/groups.go b/msgraph/groups.go index a51dd6d8..470d0c5b 100644 --- a/msgraph/groups.go +++ b/msgraph/groups.go @@ -145,6 +145,7 @@ func (c *GroupsClient) Delete(ctx context.Context, id string) (int, error) { } // ListDeleted retrieves a list of recently deleted O365 groups, optionally filtered using OData. +// TODO: add test coverage once API supports creating O365 groups func (c *GroupsClient) ListDeleted(ctx context.Context, filter string) (*[]Group, int, error) { params := url.Values{} if filter != "" {