Skip to content

Commit c25a24d

Browse files
feat: Add data source github_users
1 parent 7732839 commit c25a24d

File tree

6 files changed

+247
-0
lines changed

6 files changed

+247
-0
lines changed

github/data_source_github_users.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
7+
"github.com/shurcooL/githubv4"
8+
"log"
9+
"reflect"
10+
"strings"
11+
)
12+
13+
func dataSourceGithubUsers() *schema.Resource {
14+
return &schema.Resource{
15+
Read: dataSourceGithubUsersRead,
16+
17+
Schema: map[string]*schema.Schema{
18+
"usernames": {
19+
Type: schema.TypeList,
20+
Elem: &schema.Schema{
21+
Type: schema.TypeString,
22+
},
23+
Required: true,
24+
},
25+
"logins": {
26+
Type: schema.TypeList,
27+
Elem: &schema.Schema{
28+
Type: schema.TypeString,
29+
},
30+
Computed: true,
31+
},
32+
"node_ids": {
33+
Type: schema.TypeList,
34+
Elem: &schema.Schema{
35+
Type: schema.TypeString,
36+
},
37+
Computed: true,
38+
},
39+
"unknown_logins": {
40+
Type: schema.TypeList,
41+
Elem: &schema.Schema{
42+
Type: schema.TypeString,
43+
},
44+
Computed: true,
45+
},
46+
},
47+
}
48+
}
49+
50+
func dataSourceGithubUsersRead(d *schema.ResourceData, meta interface{}) error {
51+
usernames := expandStringList(d.Get("usernames").([]interface{}))
52+
53+
// Create GraphQL variables and query struct
54+
type (
55+
UserFragment struct {
56+
Id string
57+
Login string
58+
}
59+
)
60+
var fields []reflect.StructField
61+
variables := make(map[string]interface{})
62+
for idx, username := range usernames {
63+
label := fmt.Sprintf("User%d", idx)
64+
variables[label] = githubv4.String(username)
65+
fields = append(fields, reflect.StructField{
66+
Name: label, Type: reflect.TypeOf(UserFragment{}), Tag: reflect.StructTag(fmt.Sprintf("graphql:\"%[1]s: user(login: $%[1]s)\"", label)),
67+
})
68+
}
69+
query := reflect.New(reflect.StructOf(fields)).Elem()
70+
71+
if len(usernames) > 0 {
72+
log.Printf("[INFO] Refreshing GitHub Users: %s", strings.Join(usernames, ", "))
73+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
74+
client := meta.(*Owner).v4client
75+
err := client.Query(ctx, query.Addr().Interface(), variables)
76+
if !strings.Contains(err.Error(), "Could not resolve to a User with the login of") {
77+
return err
78+
}
79+
}
80+
81+
var logins, nodeIDs, unknownLogins []string
82+
for idx, username := range usernames {
83+
label := fmt.Sprintf("User%d", idx)
84+
user := query.FieldByName(label).Interface().(UserFragment)
85+
if user.Login != "" {
86+
logins = append(logins, user.Login)
87+
nodeIDs = append(nodeIDs, user.Id)
88+
} else {
89+
unknownLogins = append(unknownLogins, username)
90+
}
91+
}
92+
93+
d.SetId(buildChecksumID(usernames))
94+
d.Set("logins", logins)
95+
d.Set("node_ids", nodeIDs)
96+
d.Set("unknown_logins", unknownLogins)
97+
98+
return nil
99+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
8+
)
9+
10+
func TestAccGithubUsersDataSource(t *testing.T) {
11+
12+
t.Run("queries multiple accounts", func(t *testing.T) {
13+
14+
config := fmt.Sprintf(`
15+
data "github_users" "test" {
16+
usernames = ["%[1]s", "!%[1]s"]
17+
}
18+
`, testOwnerFunc())
19+
20+
check := resource.ComposeAggregateTestCheckFunc(
21+
resource.TestCheckResourceAttr("data.github_users.test", "logins.#", "1"),
22+
resource.TestCheckResourceAttr("data.github_users.test", "logins.0", testOwnerFunc()),
23+
resource.TestCheckResourceAttr("data.github_users.test", "node_ids.#", "1"),
24+
resource.TestCheckResourceAttr("data.github_users.test", "unknown_logins.#", "1"),
25+
resource.TestCheckResourceAttr("data.github_users.test", "unknown_logins.0", fmt.Sprintf("!%s", testOwnerFunc())),
26+
)
27+
28+
testCase := func(t *testing.T, mode string) {
29+
resource.Test(t, resource.TestCase{
30+
PreCheck: func() { skipUnlessMode(t, mode) },
31+
Providers: testAccProviders,
32+
Steps: []resource.TestStep{
33+
{
34+
Config: config,
35+
Check: check,
36+
},
37+
},
38+
})
39+
}
40+
41+
t.Run("with an anonymous account", func(t *testing.T) {
42+
t.Skip("anonymous account not supported for this operation")
43+
})
44+
45+
t.Run("with an individual account", func(t *testing.T) {
46+
testCase(t, individual)
47+
})
48+
49+
t.Run("with an organization account", func(t *testing.T) {
50+
testCase(t, organization)
51+
})
52+
53+
})
54+
55+
t.Run("does not fail if called with empty list of usernames", func(t *testing.T) {
56+
57+
config := `
58+
data "github_users" "test" {
59+
usernames = []
60+
}
61+
`
62+
63+
check := resource.ComposeAggregateTestCheckFunc(
64+
resource.TestCheckResourceAttr("data.github_users.test", "logins.#", "0"),
65+
resource.TestCheckResourceAttr("data.github_users.test", "node_ids.#", "0"),
66+
resource.TestCheckResourceAttr("data.github_users.test", "unknown_logins.#", "0"),
67+
)
68+
69+
testCase := func(t *testing.T, mode string) {
70+
resource.Test(t, resource.TestCase{
71+
PreCheck: func() { skipUnlessMode(t, mode) },
72+
Providers: testAccProviders,
73+
Steps: []resource.TestStep{
74+
{
75+
Config: config,
76+
Check: check,
77+
},
78+
},
79+
})
80+
}
81+
82+
t.Run("with an anonymous account", func(t *testing.T) {
83+
t.Skip("anonymous account not supported for this operation")
84+
})
85+
86+
t.Run("with an individual account", func(t *testing.T) {
87+
testCase(t, individual)
88+
})
89+
90+
t.Run("with an organization account", func(t *testing.T) {
91+
testCase(t, organization)
92+
})
93+
94+
})
95+
}

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func Provider() terraform.ResourceProvider {
125125
"github_repository_pull_requests": dataSourceGithubRepositoryPullRequests(),
126126
"github_team": dataSourceGithubTeam(),
127127
"github_user": dataSourceGithubUser(),
128+
"github_users": dataSourceGithubUsers(),
128129
},
129130
}
130131

github/util.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package github
22

33
import (
44
"context"
5+
"crypto/md5"
56
"errors"
67
"fmt"
78
"regexp"
9+
"sort"
810
"strconv"
911
"strings"
1012

@@ -78,6 +80,16 @@ func buildThreePartID(a, b, c string) string {
7880
return fmt.Sprintf("%s:%s:%s", a, b, c)
7981
}
8082

83+
func buildChecksumID(v []string) string {
84+
sort.Strings(v)
85+
86+
h := md5.New()
87+
h.Write([]byte(strings.Join(v, "")))
88+
bs := h.Sum(nil)
89+
90+
return fmt.Sprintf("%x", bs)
91+
}
92+
8193
func expandStringList(configured []interface{}) []string {
8294
vs := make([]string, 0, len(configured))
8395
for _, v := range configured {

website/docs/d/users.html.markdown

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: github_users"
4+
description: |-
5+
Get information about multiple GitHub users.
6+
---
7+
8+
# github\_users
9+
10+
Use this data source to retrieve information about multiple GitHub users at once.
11+
12+
## Example Usage
13+
14+
```hcl
15+
# Retrieve information about multiple GitHub users.
16+
data "github_users" "example" {
17+
usernames = ["example1", "example2", "example3"]
18+
}
19+
20+
output "valid_users" {
21+
value = "${data.github_user.example.logins}"
22+
}
23+
24+
output "invalid_users" {
25+
value = "${data.github_user.example.unknown_logins}"
26+
}
27+
```
28+
29+
## Argument Reference
30+
31+
* `usernames` - (Required) List of usernames.
32+
33+
## Attributes Reference
34+
35+
* `node_ids` - list of Node IDs of users that could be found.
36+
* `logins` - list of logins of users that could be found.
37+
* `unknown_logins` - list of logins without matching user.

website/github.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
<li>
5656
<a href="/docs/providers/github/d/user.html">github_user</a>
5757
</li>
58+
<li>
59+
<a href="/docs/providers/github/d/users.html">github_users</a>
60+
</li>
5861
</ul>
5962
</li>
6063

0 commit comments

Comments
 (0)