mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-07 21:32:54 +02:00
github: use GraphQL API to reduce number of API calls for directory sync (#2715)
* github: use GraphQL API to reduce number of API calls for directory sync * fix id encoding * github: use slug instead of id, update upgrading.md * Update docs/docs/upgrading.md Co-authored-by: Alex Fornuto <afornuto@pomerium.com> Co-authored-by: Alex Fornuto <afornuto@pomerium.com>
This commit is contained in:
parent
d390e80b30
commit
99b905a336
6 changed files with 499 additions and 204 deletions
|
@ -11,6 +11,8 @@ import (
|
|||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
"github.com/vektah/gqlparser/parser"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/testutil"
|
||||
)
|
||||
|
@ -35,26 +37,198 @@ func newMockAPI(t *testing.T, srv *httptest.Server) http.Handler {
|
|||
}
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
|
||||
_ = json.NewEncoder(w).Encode(M{
|
||||
"data": M{
|
||||
"organization": M{
|
||||
"teams": M{
|
||||
"totalCount": 3,
|
||||
"edges": []M{
|
||||
{"node": M{
|
||||
"id": "MDQ6VGVhbTE=",
|
||||
}},
|
||||
{"node": M{
|
||||
"id": "MDQ6VGVhbTI=",
|
||||
}},
|
||||
{"node": M{
|
||||
"id": "MDQ6VGVhbTM=",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
q, err := parser.ParseQuery(&ast.Source{
|
||||
Input: body.Query,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result := qlResult{
|
||||
Data: &qlData{
|
||||
Organization: &qlOrganization{},
|
||||
},
|
||||
}
|
||||
handleMembersWithRole := func(orgSlug string, field *ast.Field) {
|
||||
membersWithRole := &qlMembersWithRoleConnection{}
|
||||
|
||||
var cursor string
|
||||
for _, arg := range field.Arguments {
|
||||
if arg.Name == "after" {
|
||||
cursor = arg.Value.Raw
|
||||
}
|
||||
}
|
||||
|
||||
switch cursor {
|
||||
case `null`:
|
||||
switch orgSlug {
|
||||
case "org1":
|
||||
membersWithRole.PageInfo = qlPageInfo{EndCursor: "TOKEN1", HasNextPage: true}
|
||||
membersWithRole.Nodes = []qlUser{
|
||||
{ID: "user1", Login: "user1", Name: "User 1", Email: "user1@example.com"},
|
||||
{ID: "user2", Login: "user2", Name: "User 2", Email: "user2@example.com"},
|
||||
}
|
||||
case "org2":
|
||||
membersWithRole.PageInfo = qlPageInfo{HasNextPage: false}
|
||||
membersWithRole.Nodes = []qlUser{
|
||||
{ID: "user4", Login: "user4", Name: "User 4", Email: "user4@example.com"},
|
||||
}
|
||||
default:
|
||||
t.Errorf("unexpected org slug: %s", orgSlug)
|
||||
}
|
||||
case `TOKEN1`:
|
||||
membersWithRole.PageInfo = qlPageInfo{HasNextPage: false}
|
||||
membersWithRole.Nodes = []qlUser{
|
||||
{ID: "user3", Login: "user3", Name: "User 3", Email: "user3@example.com"},
|
||||
}
|
||||
default:
|
||||
t.Errorf("unexpected cursor: %s", cursor)
|
||||
}
|
||||
|
||||
result.Data.Organization.MembersWithRole = membersWithRole
|
||||
}
|
||||
handleTeamMembers := func(orgSlug, teamSlug string, field *ast.Field) {
|
||||
result.Data.Organization.Team.Members = &qlTeamMemberConnection{
|
||||
PageInfo: qlPageInfo{HasNextPage: false},
|
||||
}
|
||||
|
||||
switch teamSlug {
|
||||
case "team3":
|
||||
result.Data.Organization.Team.Members.Edges = []qlTeamMemberEdge{
|
||||
{Node: qlUser{ID: "user3"}},
|
||||
}
|
||||
}
|
||||
}
|
||||
handleTeam := func(orgSlug string, field *ast.Field) {
|
||||
result.Data.Organization.Team = &qlTeam{}
|
||||
|
||||
var teamSlug string
|
||||
for _, arg := range field.Arguments {
|
||||
if arg.Name == "slug" {
|
||||
teamSlug = arg.Value.Raw
|
||||
}
|
||||
}
|
||||
|
||||
for _, selection := range field.SelectionSet {
|
||||
subField, ok := selection.(*ast.Field)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch subField.Name {
|
||||
case "members":
|
||||
handleTeamMembers(orgSlug, teamSlug, subField)
|
||||
}
|
||||
}
|
||||
}
|
||||
handleTeams := func(orgSlug string, field *ast.Field) {
|
||||
teams := &qlTeamConnection{}
|
||||
|
||||
var cursor string
|
||||
var userLogin string
|
||||
for _, arg := range field.Arguments {
|
||||
if arg.Name == "after" {
|
||||
cursor = arg.Value.Raw
|
||||
}
|
||||
if arg.Name == "userLogins" {
|
||||
userLogin = arg.Value.Children[0].Value.Raw
|
||||
}
|
||||
}
|
||||
|
||||
switch cursor {
|
||||
case `null`:
|
||||
switch orgSlug {
|
||||
case "org1":
|
||||
teams.PageInfo = qlPageInfo{HasNextPage: true, EndCursor: "TOKEN1"}
|
||||
teams.Edges = []qlTeamEdge{
|
||||
{Node: qlTeam{ID: "MDQ6VGVhbTE=", Slug: "team1", Name: "Team 1", Members: &qlTeamMemberConnection{
|
||||
PageInfo: qlPageInfo{HasNextPage: false},
|
||||
Edges: []qlTeamMemberEdge{
|
||||
{Node: qlUser{ID: "user1"}},
|
||||
{Node: qlUser{ID: "user2"}},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
case "org2":
|
||||
teams.PageInfo = qlPageInfo{HasNextPage: false}
|
||||
teams.Edges = []qlTeamEdge{
|
||||
{Node: qlTeam{ID: "MDQ6VGVhbTM=", Slug: "team3", Name: "Team 3", Members: &qlTeamMemberConnection{
|
||||
PageInfo: qlPageInfo{HasNextPage: true, EndCursor: "TOKEN1"},
|
||||
Edges: []qlTeamMemberEdge{
|
||||
{Node: qlUser{ID: "user1"}},
|
||||
{Node: qlUser{ID: "user2"}},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
if userLogin == "" || userLogin == "user4" {
|
||||
teams.Edges = append(teams.Edges, qlTeamEdge{
|
||||
Node: qlTeam{ID: "MDQ6VGVhbTQ=", Slug: "team4", Name: "Team 4", Members: &qlTeamMemberConnection{
|
||||
PageInfo: qlPageInfo{HasNextPage: false},
|
||||
Edges: []qlTeamMemberEdge{
|
||||
{Node: qlUser{ID: "user4"}},
|
||||
},
|
||||
}},
|
||||
})
|
||||
}
|
||||
default:
|
||||
t.Errorf("unexpected org slug: %s", orgSlug)
|
||||
}
|
||||
case "TOKEN1":
|
||||
teams.PageInfo = qlPageInfo{HasNextPage: false}
|
||||
teams.Edges = []qlTeamEdge{
|
||||
{Node: qlTeam{ID: "MDQ6VGVhbTI=", Slug: "team2", Name: "Team 2", Members: &qlTeamMemberConnection{
|
||||
PageInfo: qlPageInfo{HasNextPage: false},
|
||||
Edges: []qlTeamMemberEdge{
|
||||
{Node: qlUser{ID: "user1"}},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
default:
|
||||
t.Errorf("unexpected cursor: %s", cursor)
|
||||
}
|
||||
|
||||
result.Data.Organization.Teams = teams
|
||||
}
|
||||
handleOrganization := func(field *ast.Field) {
|
||||
var orgSlug string
|
||||
for _, arg := range field.Arguments {
|
||||
if arg.Name == "login" {
|
||||
orgSlug = arg.Value.Raw
|
||||
}
|
||||
}
|
||||
for _, orgSelection := range field.SelectionSet {
|
||||
orgField, ok := orgSelection.(*ast.Field)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch orgField.Name {
|
||||
case "teams":
|
||||
handleTeams(orgSlug, orgField)
|
||||
case "team":
|
||||
handleTeam(orgSlug, orgField)
|
||||
case "membersWithRole":
|
||||
handleMembersWithRole(orgSlug, orgField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, operation := range q.Operations {
|
||||
for _, selection := range operation.SelectionSet {
|
||||
field, ok := selection.(*ast.Field)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Name != "organization" {
|
||||
continue
|
||||
}
|
||||
|
||||
handleOrganization(field)
|
||||
}
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(w).Encode(result)
|
||||
})
|
||||
r.Get("/user/orgs", func(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode([]M{
|
||||
|
@ -136,7 +310,7 @@ func TestProvider_User(t *testing.T) {
|
|||
}
|
||||
testutil.AssertProtoJSONEqual(t, `{
|
||||
"id": "user1",
|
||||
"groupIds": ["1", "2", "3"],
|
||||
"groupIds": ["team1", "team2", "team3"],
|
||||
"displayName": "User 1",
|
||||
"email": "user1@example.com"
|
||||
}`, du)
|
||||
|
@ -160,16 +334,16 @@ func TestProvider_UserGroups(t *testing.T) {
|
|||
groups, users, err := p.UserGroups(context.Background())
|
||||
assert.NoError(t, err)
|
||||
testutil.AssertProtoJSONEqual(t, `[
|
||||
{ "id": "user1", "groupIds": ["1", "2", "3"], "displayName": "User 1", "email": "user1@example.com" },
|
||||
{ "id": "user2", "groupIds": ["1", "3"], "displayName": "User 2", "email": "user2@example.com" },
|
||||
{ "id": "user3", "groupIds": ["3"], "displayName": "User 3", "email": "user3@example.com" },
|
||||
{ "id": "user4", "groupIds": ["4"], "displayName": "User 4", "email": "user4@example.com" }
|
||||
{ "id": "user1", "groupIds": ["team1", "team2", "team3"], "displayName": "User 1", "email": "user1@example.com" },
|
||||
{ "id": "user2", "groupIds": ["team1", "team3"], "displayName": "User 2", "email": "user2@example.com" },
|
||||
{ "id": "user3", "groupIds": ["team3"], "displayName": "User 3", "email": "user3@example.com" },
|
||||
{ "id": "user4", "groupIds": ["team4"], "displayName": "User 4", "email": "user4@example.com" }
|
||||
]`, users)
|
||||
testutil.AssertProtoJSONEqual(t, `[
|
||||
{ "id": "1", "name": "team1" },
|
||||
{ "id": "2", "name": "team2" },
|
||||
{ "id": "3", "name": "team3" },
|
||||
{ "id": "4", "name": "team4" }
|
||||
{ "id": "team1", "name": "team1" },
|
||||
{ "id": "team2", "name": "team2" },
|
||||
{ "id": "team3", "name": "team3" },
|
||||
{ "id": "team4", "name": "team4" }
|
||||
]`, groups)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue