diff --git a/actions_test.go b/actions_test.go index 7d24dae4..1b433a72 100644 --- a/actions_test.go +++ b/actions_test.go @@ -352,12 +352,12 @@ func TestDeleteTriggerDefinition(t *testing.T) { func TestListExtendedTeamAccess(t *testing.T) { // Arrange testRequestOne := autopilot.NewTestRequest( - `query ExtendedTeamAccessList($after:String!$first:Int!$input:IdentifierInput!){account{customActionsTriggerDefinition(input: $input){extendedTeamAccess(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}}`, + `query ExtendedTeamAccessList($after:String!$first:Int!$input:IdentifierInput!){account{customActionsTriggerDefinition(input: $input){extendedTeamAccess(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}}`, `{{ template "extended_team_access_get_vars_1" }}`, `{{ template "extended_team_access_response_1" }}`, ) testRequestTwo := autopilot.NewTestRequest( - `query ExtendedTeamAccessList($after:String!$first:Int!$input:IdentifierInput!){account{customActionsTriggerDefinition(input: $input){extendedTeamAccess(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}}`, + `query ExtendedTeamAccessList($after:String!$first:Int!$input:IdentifierInput!){account{customActionsTriggerDefinition(input: $input){extendedTeamAccess(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}}`, `{{ template "extended_team_access_get_vars_2" }}`, `{{ template "extended_team_access_response_2" }}`, ) diff --git a/cache_test.go b/cache_test.go index af551edd..ef3158b1 100644 --- a/cache_test.go +++ b/cache_test.go @@ -25,7 +25,7 @@ func TestCache(t *testing.T) { `{"data":{"account":{ "systems":{ "nodes":[{{ template "system1_response" }}] } }}}`, ) testRequestFour := autopilot.NewTestRequest( - `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, + `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, `{ "after": "", "first": 500 }`, `{"data":{"account":{ "teams":{ "nodes":[{{ template "team_1" }}] } }}}`, ) diff --git a/clientGQL_test.go b/clientGQL_test.go index 170d7bf6..5437acd0 100644 --- a/clientGQL_test.go +++ b/clientGQL_test.go @@ -210,7 +210,7 @@ func httpResponseByCode(statusCode int) autopilot.ResponseWriter { func TestMissingTeamIsAnOpsLevelApiError(t *testing.T) { testRequest := autopilot.NewTestRequest( - `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, `{ {{ template "id1" }} }`, `{"errors": [ { diff --git a/object.go b/object.go index 1f829124..9b392ec3 100644 --- a/object.go +++ b/object.go @@ -647,10 +647,12 @@ type UserId struct { // User A user is someone who belongs to an organization type User struct { UserId - Contacts []Contact // The contacts for the user (Optional) - HtmlUrl string // A link to the HTML page for the resource. Ex. https://app.opslevel.com/services/shopping_cart (Required) - ProvisionedBy ProvisionedByEnum // What provisioned this user (Optional) - Role UserRole // The user's assigned role (Optional) + Contacts []Contact // The contacts for the user (Optional) + HtmlUrl string // A link to the HTML page for the resource. Ex. https://app.opslevel.com/services/shopping_cart (Required) + ProvisionedBy ProvisionedByEnum // What provisioned this user (Optional) + Role UserRole // The user's assigned role (Optional) + Tags *TagConnection `json:"tags,omitempty"` + TeamsConnection *TeamIdConnection `graphql:"teams" json:"teams,omitempty"` } // Warning The warnings of the mutation diff --git a/team_test.go b/team_test.go index eb1bbc09..cd7299f4 100644 --- a/team_test.go +++ b/team_test.go @@ -165,7 +165,7 @@ func TestCreateTeam(t *testing.T) { ParentTeam: ol.NewIdentifier("parent_team"), }) testRequest := autopilot.NewTestRequest( - `mutation TeamCreate($input:TeamCreateInput!){teamCreate(input: $input){team{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},errors{message,path}}}`, + `mutation TeamCreate($input:TeamCreateInput!){teamCreate(input: $input){team{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},errors{message,path}}}`, `{"input": {{ template "team_create_input" }} }`, `{ "data": { "teamCreate": { @@ -213,7 +213,7 @@ func TestCreateTeam(t *testing.T) { func TestGetTeam(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, `{ {{ template "id1" }} }`, `{ "data": { "account": { @@ -363,7 +363,7 @@ func TestGetTeamWithAlias(t *testing.T) { func TestListTeams(t *testing.T) { // Arrange testRequestOne := autopilot.NewTestRequest( - `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, + `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, `{{ template "pagination_initial_query_variables" }}`, `{ "data": { "account": { @@ -434,7 +434,7 @@ func TestListTeams(t *testing.T) { }}}}`, ) testRequestTwo := autopilot.NewTestRequest( - `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, + `query TeamList($after:String!$first:Int!){account{teams(after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, `{{ template "pagination_second_query_variables" }}`, `{ "data": { "account": { @@ -503,7 +503,7 @@ func TestListTeams(t *testing.T) { func TestListTeamsWithManager(t *testing.T) { // Arrange testRequestOne := autopilot.NewTestRequest( - `query TeamList($after:String!$email:String!$first:Int!){account{teams(managerEmail: $email, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, + `query TeamList($after:String!$email:String!$first:Int!){account{teams(managerEmail: $email, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, `{ "after": "", "first": 500, "email": "kyle@opslevel.com" }`, `{ "data": { "account": { @@ -574,7 +574,7 @@ func TestListTeamsWithManager(t *testing.T) { }}}}`, ) testRequestTwo := autopilot.NewTestRequest( - `query TeamList($after:String!$email:String!$first:Int!){account{teams(managerEmail: $email, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, + `query TeamList($after:String!$email:String!$first:Int!){account{teams(managerEmail: $email, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, `{ "after": "OA", "first": 500, "email": "kyle@opslevel.com" }`, `{ "data": { "account": { @@ -650,7 +650,7 @@ func TestUpdateTeam(t *testing.T) { }, ) testRequest := autopilot.NewTestRequest( - `mutation TeamUpdate($input:TeamUpdateInput!){teamUpdate(input: $input){team{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},errors{message,path}}}`, + `mutation TeamUpdate($input:TeamUpdateInput!){teamUpdate(input: $input){team{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},errors{message,path}}}`, `{"input": {{ template "team_update_input" }} }`, `{ "data": { "teamUpdate": { @@ -766,7 +766,7 @@ func TestTeamAddMembership(t *testing.T) { func TestTeamRemoveMembership(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `mutation TeamMembershipDelete($input:TeamMembershipDeleteInput!){teamMembershipDelete(input: $input){deletedMembers{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},errors{message,path}}}`, + `mutation TeamMembershipDelete($input:TeamMembershipDeleteInput!){teamMembershipDelete(input: $input){deletedMembers{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},errors{message,path}}}`, `{"input": { "teamId": "{{ template "id1_string" }}", "members": [ { {{ template "team_membership_user_input_1" }} } ] }}`, `{ "data": { "teamMembershipDelete": { @@ -901,7 +901,7 @@ func TestTeamReconcileAliasesDeleteAll(t *testing.T) { ) // get team testRequestThree := autopilot.NewTestRequest( - `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, `{ {{ template "id1" }} }`, `{ "data": { "account": { "team": { {{ template "id1" }}, "aliases": [], "managedAliases": [] }}}}`, ) @@ -936,7 +936,7 @@ func TestTeamReconcileAliasesDeleteSome(t *testing.T) { ) // get team testRequestTwo := autopilot.NewTestRequest( - `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, `{ {{ template "id1" }} }`, `{ "data": { "account": { "team": { {{ template "id1" }}, "aliases": ["two"], "managedAliases": ["two"] }}}}`, ) @@ -989,7 +989,7 @@ func TestTeamReconcileAliases(t *testing.T) { ) // get team testRequestFive := autopilot.NewTestRequest( - `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `query TeamGet($id:ID!){account{team(id: $id){alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},{{ template "pagination_request" }}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},{{ template "pagination_request" }}}}}}`, `{ {{ template "id1" }} }`, `{ "data": { "account": { "team": { {{ template "id1" }}, "aliases": ["one", "two", "three"], "managedAliases": ["one", "two", "three"] }}}}`, ) @@ -1008,7 +1008,7 @@ func TestTeamReconcileAliases(t *testing.T) { func TestSearchTeams(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `query TeamSearch($after:String!$first:Int!$searchTerm:String!){account{teams(searchTerm: $searchTerm, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, + `query TeamSearch($after:String!$first:Int!$searchTerm:String!){account{teams(searchTerm: $searchTerm, after: $after, first: $first){nodes{alias,id,aliases,managedAliases,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,manager{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},memberships{nodes{role,team{alias,id},user{id,email,name}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}},name,parentTeam{alias,id},responsibilities,tags{nodes{id,key,value},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor}}}}`, `{"searchTerm": "DevOps", "after": "", "first": 500}`, `{ "data": { "account": { "teams": { "nodes": [ { "alias": "devops", "id": "id1", "aliases": ["devops"], "managedAliases": ["devops"], "contacts": [], "htmlUrl": "https://app.opslevel.com/teams/devops", "manager": { "id": "user1", "email": "manager@opslevel.com", "name": "Manager" }, "memberships": { "nodes": [], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "startCursor": null, "endCursor": null } }, "name": "DevOps", "parentTeam": null, "responsibilities": "Own Infra & Tools.", "tags": { "nodes": [], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "startCursor": null, "endCursor": null } } } ], "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "startCursor": null, "endCursor": null } } } } }`, ) diff --git a/testdata/templates/query/team/get.tpl b/testdata/templates/query/team/get.tpl index b00a2ef6..5ee7dd2f 100644 --- a/testdata/templates/query/team/get.tpl +++ b/testdata/templates/query/team/get.tpl @@ -29,7 +29,32 @@ }, htmlUrl, provisionedBy, - role + role, + tags{ + nodes{ + id, + key, + value + }, + pageInfo{ + hasNextPage, + hasPreviousPage, + startCursor, + endCursor + } + }, + teams{ + nodes{ + alias, + id + }, + pageInfo{ + hasNextPage, + hasPreviousPage, + startCursor, + endCursor + } + } }, memberships{ nodes{ diff --git a/user.go b/user.go index 99a28b58..4f06abb5 100644 --- a/user.go +++ b/user.go @@ -76,7 +76,7 @@ func (user *User) Teams(client *Client, variables *PayloadVariables) (*TeamIdCon variables = client.InitialPageVariablesPointer() } (*variables)["user"] = user.Id - if err := client.Query(&q, *variables, WithName("UserTeamsList")); err != nil { // what goes in "" here and how is it derived? + if err := client.Query(&q, *variables, WithName("UserTeamsList")); err != nil { return nil, err } if q.Account.User.Teams.PageInfo.HasNextPage { @@ -92,6 +92,40 @@ func (user *User) Teams(client *Client, variables *PayloadVariables) (*TeamIdCon return &q.Account.User.Teams, nil } +func (user *User) Hydrate(client *Client) error { + if user.Tags == nil { + user.Tags = &TagConnection{} + } + if user.Tags.PageInfo.HasNextPage { + variables := client.InitialPageVariablesPointer() + (*variables)["after"] = user.Tags.PageInfo.End + resp, err := user.GetTags(client, variables) + if err != nil { + return err + } + user.Tags.Nodes = append(user.Tags.Nodes, resp.Nodes...) + user.Tags.PageInfo = resp.PageInfo + } + user.Tags.TotalCount = len(user.Tags.Nodes) + + if user.TeamsConnection == nil { + user.TeamsConnection = &TeamIdConnection{} + } + if user.TeamsConnection.PageInfo.HasNextPage { + variables := client.InitialPageVariablesPointer() + (*variables)["after"] = user.TeamsConnection.PageInfo.End + resp, err := user.Teams(client, variables) + if err != nil { + return err + } + user.TeamsConnection.Nodes = append(user.TeamsConnection.Nodes, resp.Nodes...) + user.TeamsConnection.PageInfo = resp.PageInfo + } + user.TeamsConnection.TotalCount = len(user.TeamsConnection.Nodes) + + return nil +} + func (client *Client) InviteUser(email string, input UserInput, sendInvite bool) (*User, error) { var m struct { Payload struct { // TODO: need to fix this @@ -136,6 +170,15 @@ func (client *Client) ListUsers(variables *PayloadVariables) (*UserConnection, e return nil, err } + // Hydrate inner tags/teams connections for every user on this page. + // Without this, users on the first outer page would never paginate their + // nested tag/team connections beyond the server's default page size. + for i := range q.Account.Users.Nodes { + if err := q.Account.Users.Nodes[i].Hydrate(client); err != nil { + return nil, err + } + } + if q.Account.Users.PageInfo.HasNextPage { (*variables)["after"] = q.Account.Users.PageInfo.End resp, err := client.ListUsers(variables) diff --git a/user_test.go b/user_test.go index 7d26eb3a..b0a58932 100644 --- a/user_test.go +++ b/user_test.go @@ -10,7 +10,7 @@ import ( func TestInviteUser(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `mutation UserInvite($email:String!$forceSendInvite:Boolean$input:UserInput!){userInvite(email: $email input: $input, forceSendInvite: $forceSendInvite){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},errors{message,path}}}`, + `mutation UserInvite($email:String!$forceSendInvite:Boolean$input:UserInput!){userInvite(email: $email input: $input, forceSendInvite: $forceSendInvite){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},errors{message,path}}}`, `{"email": "kyle@opslevel.com", "input": { "name": "Kyle Rockman", "skipWelcomeEmail": false }, "forceSendInvite": true}`, `{"data": { "userInvite": { "user": {{ template "user_1" }}, "errors": [] }}}`, ) @@ -32,7 +32,7 @@ func TestInviteUser(t *testing.T) { func TestInviteUserSkipSendInvite(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `mutation UserInvite($email:String!$forceSendInvite:Boolean$input:UserInput!){userInvite(email: $email input: $input, forceSendInvite: $forceSendInvite){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},errors{message,path}}}`, + `mutation UserInvite($email:String!$forceSendInvite:Boolean$input:UserInput!){userInvite(email: $email input: $input, forceSendInvite: $forceSendInvite){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},errors{message,path}}}`, `{"email": "kyle@opslevel.com", "input": { "name": "Kyle Rockman", "role": "team_member", "skipWelcomeEmail": false }, "forceSendInvite": false}`, `{"data": { "userInvite": { "user": { {{ template "user_id_email_1" }}, "name": "Kyle Rockman", "role": "team_member" }, "errors": [] }}}`, ) @@ -55,7 +55,7 @@ func TestInviteUserSkipSendInvite(t *testing.T) { func TestGetUser(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `query UserGet($input:UserIdentifierInput!){account{user(input: $input){id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role}}}`, + `query UserGet($input:UserIdentifierInput!){account{user(input: $input){id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}}}}`, `{"input": { "email": "kyle@opslevel.com" }}`, `{"data": {"account": {"user": {{ template "user_1" }} }}}`, ) @@ -104,12 +104,12 @@ func TestGetUserTeams(t *testing.T) { func TestListUser(t *testing.T) { // Arrange testRequestOne := autopilot.NewTestRequest( - `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},{{ template "pagination_request" }}}}}`, + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, `{ "after": "", "filter": null, "first": 500 }`, `{ "data": { "account": { "users": { "nodes": [ {{ template "user_1" }}, {{ template "user_2" }} ], {{ template "pagination_initial_pageInfo_response" }} }}}}`, ) testRequestTwo := autopilot.NewTestRequest( - `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},{{ template "pagination_request" }}}}}`, + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, `{ "after": "OA", "filter": null, "first": 500 }`, `{ "data": { "account": { "users": { "nodes": [ {{ template "user_3" }} ], {{ template "pagination_second_pageInfo_response" }} }}}}`, ) @@ -132,12 +132,12 @@ func TestListUser(t *testing.T) { func TestListUserOmitDeactivated(t *testing.T) { // Arrange testRequestOne := autopilot.NewTestRequest( - `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},{{ template "pagination_request" }}}}}`, + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, `{ "after": "", "filter": [ {"key": "deactivated_at", "type": "equals"} ], "first": 500 }`, `{ "data": { "account": { "users": { "nodes": [ {{ template "user_1" }}, {{ template "user_2" }} ], {{ template "pagination_initial_pageInfo_response" }} }}}}`, ) testRequestTwo := autopilot.NewTestRequest( - `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},{{ template "pagination_request" }}}}}`, + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, `{ "after": "OA", "filter": [ {"key": "deactivated_at", "type": "equals"} ], "first": 500 }`, `{ "data": { "account": { "users": { "nodes": [], {{ template "pagination_second_pageInfo_response" }} }}}}`, ) @@ -158,10 +158,90 @@ func TestListUserOmitDeactivated(t *testing.T) { autopilot.Equals(t, ol.UserRoleAdmin, result[1].Role) } +func TestListUserPopulatesTagsAndTeams(t *testing.T) { + // Arrange + testRequest := autopilot.NewTestRequest( + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, + `{ "after": "", "filter": null, "first": 500 }`, + `{ "data": { "account": { "users": { "nodes": [ { + {{ template "user_id_email_1" }}, + "name": "Kyle Rockman", + "contacts": [ {{ template "contact_1" }} ], + "htmlUrl": "https://app.opslevel.com/users/kyle", + "provisionedBy": "manual", + "role": "user", + "tags": { + "nodes": [ { "id": "Z2lkOi8vb3BzbGV2ZWwvVGFnLzEwODIw", "key": "on-call", "value": "true" } ], + {{ template "pagination_second_pageInfo_response" }} + }, + "teams": { + "nodes": [ { {{ template "teamId_1" }} } ], + {{ template "pagination_second_pageInfo_response" }} + } + } ], {{ template "pagination_second_pageInfo_response" }} }}}}`, + ) + + client := BestTestClient(t, "user/list_with_tags_and_teams", testRequest) + // Act + response, err := client.ListUsers(nil) + autopilot.Ok(t, err) + // Assert + autopilot.Equals(t, 1, response.TotalCount) + user := response.Nodes[0] + autopilot.Equals(t, "Kyle Rockman", user.Name) + autopilot.Equals(t, 1, len(user.Tags.Nodes)) + autopilot.Equals(t, "on-call", user.Tags.Nodes[0].Key) + autopilot.Equals(t, "true", user.Tags.Nodes[0].Value) + autopilot.Equals(t, 1, len(user.TeamsConnection.Nodes)) + autopilot.Equals(t, "example", user.TeamsConnection.Nodes[0].Alias) +} + +func TestListUserHydratesFirstPageInnerConnections(t *testing.T) { + // Regression: a user on the first outer users page must have its inner + // tag/team connections paginated when hasNextPage is true. Previously + // Hydrate was only invoked for users merged from recursive list pages, + // so power users on page 1 had their tags/teams silently truncated. + listRequest := autopilot.NewTestRequest( + `query UserList($after:String!$filter:[UsersFilterInput!]$first:Int!){account{users(after: $after, first: $first, filter: $filter){nodes{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},{{ template "pagination_request" }}}}}`, + `{ "after": "", "filter": null, "first": 500 }`, + `{ "data": { "account": { "users": { "nodes": [ { + {{ template "user_id_email_1" }}, + "name": "Kyle Rockman", + "contacts": [ {{ template "contact_1" }} ], + "htmlUrl": "https://app.opslevel.com/users/kyle", + "provisionedBy": "manual", + "role": "user", + "tags": { + "nodes": [ { "id": "Z2lkOi8vb3BzbGV2ZWwvVGFnLzEwODIw", "key": "on-call", "value": "true" } ], + {{ template "pagination_initial_pageInfo_response" }} + }, + "teams": { + "nodes": [ { {{ template "teamId_1" }} } ], + {{ template "pagination_second_pageInfo_response" }} + } + } ], {{ template "pagination_second_pageInfo_response" }} }}}}`, + ) + tagsFollowup := autopilot.NewTestRequest( + `query UserTagsList($after:String!$first:Int!$user:ID!){account{user(id: $user){tags(after: $after, first: $first){nodes{id,key,value},{{ template "pagination_request" }}}}}}`, + `{ {{ template "second_page_variables" }}, "user": "{{ template "id1_string" }}" }`, + `{ "data": { "account": { "user": { "tags": { "nodes": [ { "id": "Z2lkOi8vb3BzbGV2ZWwvVGFnLzEwODIx", "key": "team", "value": "platform" } ], {{ template "pagination_second_pageInfo_response" }} }}}}}`, + ) + + client := BestTestClient(t, "user/list_first_page_hydrates_inner", listRequest, tagsFollowup) + response, err := client.ListUsers(nil) + autopilot.Ok(t, err) + autopilot.Equals(t, 1, response.TotalCount) + user := response.Nodes[0] + autopilot.Equals(t, 2, len(user.Tags.Nodes)) + autopilot.Equals(t, "on-call", user.Tags.Nodes[0].Key) + autopilot.Equals(t, "team", user.Tags.Nodes[1].Key) + autopilot.Equals(t, "platform", user.Tags.Nodes[1].Value) +} + func TestUpdateUser(t *testing.T) { // Arrange testRequest := autopilot.NewTestRequest( - `mutation UserUpdate($input:UserInput!$user:UserIdentifierInput!){userUpdate(user: $user input: $input){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role},errors{message,path}}}`, + `mutation UserUpdate($input:UserInput!$user:UserIdentifierInput!){userUpdate(user: $user input: $input){user{id,email,name,contacts{address,displayName,displayType,externalId,id,isDefault,type},htmlUrl,provisionedBy,role,tags{nodes{id,key,value},{{ template "pagination_request" }}},teams{nodes{alias,id},{{ template "pagination_request" }}}},errors{message,path}}}`, `{"input": {"role": "admin", "skipWelcomeEmail": false }, "user": {"email": "kyle@opslevel.com" }}`, `{"data": {"userUpdate": {"user": {{ template "user_1_update" }}, "errors": [] }}}`, )