Skip to content

Commit 11ab5d5

Browse files
authored
Merge pull request #86 from ConductorOne/BB763_list_invitations_Jun_15
[BB-763] baton-github: list invitations
2 parents cc8e655 + f6598a1 commit 11ab5d5

File tree

4 files changed

+194
-98
lines changed

4 files changed

+194
-98
lines changed

pkg/connector/connector.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour
9696
userBuilder(gh.client, gh.hasSAMLEnabled, gh.graphqlClient, gh.orgCache),
9797
repositoryBuilder(gh.client, gh.orgCache),
9898
orgRoleBuilder(gh.client, gh.orgCache),
99+
invitationBuilder(invitationBuilderParams{
100+
client: gh.client,
101+
orgCache: gh.orgCache,
102+
}),
99103
}
100104

101105
if gh.syncSecrets {

pkg/connector/invitation.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package connector
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
8+
"github.com/conductorone/baton-sdk/pkg/annotations"
9+
"github.com/conductorone/baton-sdk/pkg/connectorbuilder"
10+
"github.com/conductorone/baton-sdk/pkg/pagination"
11+
"github.com/conductorone/baton-sdk/pkg/types/resource"
12+
"github.com/google/go-github/v69/github"
13+
)
14+
15+
func invitationToUserResource(invitation *github.Invitation) (*v2.Resource, error) {
16+
login := invitation.GetLogin()
17+
if login == "" {
18+
login = invitation.GetEmail()
19+
}
20+
21+
ret, err := resource.NewUserResource(
22+
login,
23+
resourceTypeInvitation,
24+
invitation.GetID(),
25+
[]resource.UserTraitOption{
26+
resource.WithEmail(invitation.GetEmail(), true),
27+
resource.WithUserProfile(map[string]interface{}{
28+
"login": login,
29+
"inviter": invitation.GetInviter().GetLogin(),
30+
}),
31+
resource.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED),
32+
resource.WithUserLogin(login),
33+
},
34+
)
35+
if err != nil {
36+
return nil, err
37+
}
38+
return ret, nil
39+
}
40+
41+
type invitationResourceType struct {
42+
client *github.Client
43+
orgCache *orgNameCache
44+
}
45+
46+
func (i *invitationResourceType) ResourceType(_ context.Context) *v2.ResourceType {
47+
return resourceTypeInvitation
48+
}
49+
50+
func (i *invitationResourceType) List(ctx context.Context, parentID *v2.ResourceId, pt *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
51+
var annotations annotations.Annotations
52+
if parentID == nil {
53+
return nil, "", nil, nil
54+
}
55+
56+
bag, page, err := parsePageToken(pt.Token, &v2.ResourceId{ResourceType: resourceTypeUser.Id})
57+
if err != nil {
58+
return nil, "", nil, err
59+
}
60+
61+
orgName, err := i.orgCache.GetOrgName(ctx, parentID)
62+
if err != nil {
63+
return nil, "", nil, err
64+
}
65+
invitations, resp, err := i.client.Organizations.ListPendingOrgInvitations(ctx, orgName, &github.ListOptions{
66+
Page: page,
67+
PerPage: pt.Size,
68+
})
69+
if err != nil {
70+
return nil, "", nil, fmt.Errorf("github-connector: ListPendingOrgInvitatioins failed: %w", err)
71+
}
72+
73+
restApiRateLimit, err := extractRateLimitData(resp)
74+
if err != nil {
75+
return nil, "", nil, err
76+
}
77+
78+
nextPage, _, err := parseResp(resp)
79+
if err != nil {
80+
return nil, "", nil, err
81+
}
82+
83+
pageToken, err := bag.NextToken(nextPage)
84+
if err != nil {
85+
return nil, "", nil, err
86+
}
87+
88+
invitationResources := make([]*v2.Resource, 0, len(invitations))
89+
for _, invitation := range invitations {
90+
ir, err := invitationToUserResource(invitation)
91+
if err != nil {
92+
return nil, "", nil, err
93+
}
94+
invitationResources = append(invitationResources, ir)
95+
}
96+
annotations.WithRateLimiting(restApiRateLimit)
97+
return invitationResources, pageToken, nil, nil
98+
}
99+
100+
func (i *invitationResourceType) Entitlements(_ context.Context, resource *v2.Resource, _ *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
101+
return nil, "", nil, nil
102+
}
103+
104+
func (i *invitationResourceType) Grants(ctx context.Context, resource *v2.Resource, pToken *pagination.Token) ([]*v2.Grant, string, annotations.Annotations, error) {
105+
return nil, "", nil, nil
106+
}
107+
108+
func (i *invitationResourceType) CreateAccountCapabilityDetails(ctx context.Context) (*v2.CredentialDetailsAccountProvisioning, annotations.Annotations, error) {
109+
return &v2.CredentialDetailsAccountProvisioning{
110+
SupportedCredentialOptions: []v2.CapabilityDetailCredentialOption{
111+
v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
112+
},
113+
PreferredCredentialOption: v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
114+
}, nil, nil
115+
}
116+
117+
func (i *invitationResourceType) CreateAccount(
118+
ctx context.Context,
119+
accountInfo *v2.AccountInfo,
120+
credentialOptions *v2.CredentialOptions,
121+
) (
122+
connectorbuilder.CreateAccountResponse,
123+
[]*v2.PlaintextData,
124+
annotations.Annotations,
125+
error,
126+
) {
127+
params, err := getCreateUserParams(accountInfo)
128+
if err != nil {
129+
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to get CreateUserParams: %w", err)
130+
}
131+
132+
invitation, resp, err := i.client.Organizations.CreateOrgInvitation(ctx, params.org, &github.CreateOrgInvitationOptions{
133+
Email: params.email,
134+
})
135+
if err != nil {
136+
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to invite user to org: %w", err)
137+
}
138+
139+
restApiRateLimit, err := extractRateLimitData(resp)
140+
if err != nil {
141+
return nil, nil, nil, err
142+
}
143+
144+
var annotations annotations.Annotations
145+
annotations.WithRateLimiting(restApiRateLimit)
146+
147+
r, err := invitationToUserResource(invitation)
148+
if err != nil {
149+
return nil, nil, nil, fmt.Errorf("github-connectorv2: cannot create user resource: %w", err)
150+
}
151+
return &v2.CreateAccountResponse_SuccessResult{
152+
Resource: r,
153+
}, nil, nil, nil
154+
}
155+
156+
type createUserParams struct {
157+
org string
158+
email *string
159+
}
160+
161+
func getCreateUserParams(accountInfo *v2.AccountInfo) (*createUserParams, error) {
162+
pMap := accountInfo.Profile.AsMap()
163+
org, ok := pMap["org"].(string)
164+
if !ok || org == "" {
165+
return nil, fmt.Errorf("org is required")
166+
}
167+
168+
e, emailExisted := pMap["email"].(string)
169+
if !emailExisted || e == "" {
170+
return nil, fmt.Errorf("email is required")
171+
}
172+
173+
return &createUserParams{
174+
org: org,
175+
email: &e,
176+
}, nil
177+
}
178+
179+
type invitationBuilderParams struct {
180+
client *github.Client
181+
orgCache *orgNameCache
182+
}
183+
184+
func invitationBuilder(p invitationBuilderParams) *invitationResourceType {
185+
return &invitationResourceType{
186+
client: p.client,
187+
orgCache: p.orgCache,
188+
}
189+
}

pkg/connector/org.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func organizationResource(
5252
&v2.ChildResourceType{ResourceTypeId: resourceTypeTeam.Id},
5353
&v2.ChildResourceType{ResourceTypeId: resourceTypeRepository.Id},
5454
&v2.ChildResourceType{ResourceTypeId: resourceTypeOrgRole.Id},
55+
&v2.ChildResourceType{ResourceTypeId: resourceTypeInvitation.Id},
5556
}
5657
if syncSecrets {
5758
annotations = append(annotations, &v2.ChildResourceType{ResourceTypeId: resourceTypeApiToken.Id})

pkg/connector/user.go

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
1212
"github.com/conductorone/baton-sdk/pkg/annotations"
13-
"github.com/conductorone/baton-sdk/pkg/connectorbuilder"
1413
"github.com/conductorone/baton-sdk/pkg/pagination"
1514
"github.com/conductorone/baton-sdk/pkg/types/resource"
1615
"github.com/google/go-github/v69/github"
@@ -20,32 +19,6 @@ import (
2019
"google.golang.org/protobuf/types/known/timestamppb"
2120
)
2221

23-
func invitationToUserResource(invitation *github.Invitation) (*v2.Resource, error) {
24-
login := invitation.GetLogin()
25-
if login == "" {
26-
login = invitation.GetEmail()
27-
}
28-
29-
ret, err := resource.NewUserResource(
30-
login,
31-
resourceTypeInvitation,
32-
invitation.GetID(),
33-
[]resource.UserTraitOption{
34-
resource.WithEmail(invitation.GetEmail(), true),
35-
resource.WithUserProfile(map[string]interface{}{
36-
"login": login,
37-
"inviter": invitation.GetInviter().GetLogin(),
38-
}),
39-
resource.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED),
40-
resource.WithUserLogin(login),
41-
},
42-
)
43-
if err != nil {
44-
return nil, err
45-
}
46-
return ret, nil
47-
}
48-
4922
// Create a new connector resource for a GitHub user.
5023
func userResource(ctx context.Context, user *github.User, userEmail string, extraEmails []string) (*v2.Resource, error) {
5124
displayName := user.GetName()
@@ -238,77 +211,6 @@ func (o *userResourceType) List(ctx context.Context, parentID *v2.ResourceId, pt
238211
return rv, pageToken, annotations, nil
239212
}
240213

241-
func (o *userResourceType) CreateAccountCapabilityDetails(ctx context.Context) (*v2.CredentialDetailsAccountProvisioning, annotations.Annotations, error) {
242-
return &v2.CredentialDetailsAccountProvisioning{
243-
SupportedCredentialOptions: []v2.CapabilityDetailCredentialOption{
244-
v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
245-
},
246-
PreferredCredentialOption: v2.CapabilityDetailCredentialOption_CAPABILITY_DETAIL_CREDENTIAL_OPTION_NO_PASSWORD,
247-
}, nil, nil
248-
}
249-
250-
func (o *userResourceType) CreateAccount(
251-
ctx context.Context,
252-
accountInfo *v2.AccountInfo,
253-
credentialOptions *v2.CredentialOptions,
254-
) (
255-
connectorbuilder.CreateAccountResponse,
256-
[]*v2.PlaintextData,
257-
annotations.Annotations,
258-
error,
259-
) {
260-
params, err := getCreateUserParams(accountInfo)
261-
if err != nil {
262-
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to get CreateUserParams: %w", err)
263-
}
264-
265-
invitation, resp, err := o.client.Organizations.CreateOrgInvitation(ctx, params.org, &github.CreateOrgInvitationOptions{
266-
Email: params.email,
267-
})
268-
if err != nil {
269-
return nil, nil, nil, fmt.Errorf("github-connectorv2: failed to invite user to org: %w", err)
270-
}
271-
272-
restApiRateLimit, err := extractRateLimitData(resp)
273-
if err != nil {
274-
return nil, nil, nil, err
275-
}
276-
277-
var annotations annotations.Annotations
278-
annotations.WithRateLimiting(restApiRateLimit)
279-
280-
r, err := invitationToUserResource(invitation)
281-
if err != nil {
282-
return nil, nil, nil, fmt.Errorf("github-connectorv2: cannot create user resource: %w", err)
283-
}
284-
return &v2.CreateAccountResponse_SuccessResult{
285-
Resource: r,
286-
}, nil, nil, nil
287-
}
288-
289-
type createUserParams struct {
290-
org string
291-
email *string
292-
}
293-
294-
func getCreateUserParams(accountInfo *v2.AccountInfo) (*createUserParams, error) {
295-
pMap := accountInfo.Profile.AsMap()
296-
org, ok := pMap["org"].(string)
297-
if !ok || org == "" {
298-
return nil, fmt.Errorf("org is required")
299-
}
300-
301-
e, emailExisted := pMap["email"].(string)
302-
if !emailExisted || e == "" {
303-
return nil, fmt.Errorf("email is required")
304-
}
305-
306-
return &createUserParams{
307-
org: org,
308-
email: &e,
309-
}, nil
310-
}
311-
312214
func isEmail(email string) bool {
313215
_, err := mail.ParseAddress(email)
314216
return err == nil

0 commit comments

Comments
 (0)