Skip to content

Commit

Permalink
Add table github_search_user. Closes #106 (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
c0d3r-arnab authored Dec 7, 2021
1 parent f3cd972 commit 326554d
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
77 changes: 77 additions & 0 deletions docs/tables/github_search_user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Table: github_search_user

The `github_search_user` table helps to find users and organizations via various criteria. You can filter your search to the personal user or organization account name with `user` or `org` qualifiers.

**You must always include at least one search term when searching source code** in the where or join clause using the `query` column.

## Examples

### List users

```sql
select
id,
login,
type,
url
from
github_search_user
where
query = 'turbot in:name type:user';
```

### List organizations

```sql
select
id,
login,
type,
url
from
github_search_user
where
query = 'turbotio in:login type:org';
```

### Get user with specific username

```sql
select
id,
login,
type,
url
from
github_search_user
where
query = 'user:c0d3r-arnab';
```

### List organizations with over 10000 repositories

```sql
select
id,
login,
type,
url
from
github_search_user
where
query = 'repos:>10000 type:org';
```

### List users and organizations created between specific timestamp

```sql
select
id,
login,
type,
url
from
github_search_user
where
query = 'created:2021-01-01..2021-01-31 turbot';
```
1 change: 1 addition & 0 deletions github/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"github_search_label": tableGitHubSearchLable(ctx),
"github_search_pull_request": tableGitHubSearchPullRequest(ctx),
"github_search_topic": tableGitHubSearchTopic(ctx),
"github_search_user": tableGitHubSearchUser(ctx),
"github_stargazer": tableGitHubStargazer(ctx),
"github_tag": tableGitHubTag(ctx),
"github_traffic_view_daily": tableGitHubTrafficViewDaily(ctx),
Expand Down
147 changes: 147 additions & 0 deletions github/table_github_search_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package github

import (
"context"

"github.com/google/go-github/v33/github"
"github.com/turbot/steampipe-plugin-sdk/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/plugin"
"github.com/turbot/steampipe-plugin-sdk/plugin/transform"
)

//// TABLE DEFINITION

func tableGitHubSearchUser(ctx context.Context) *plugin.Table {
return &plugin.Table{
Name: "github_search_user",
Description: "Find users via various criteria.",
List: &plugin.ListConfig{
KeyColumns: plugin.SingleColumn("query"),
Hydrate: tableGitHubSearchUserList,
},
Columns: []*plugin.Column{
{Name: "id", Type: proto.ColumnType_INT, Description: "The unique ID of the user or organization."},
{Name: "login", Type: proto.ColumnType_STRING, Description: "The login name of the user or organization."},
{Name: "query", Type: proto.ColumnType_STRING, Transform: transform.FromQual("query"), Description: "The query used to match the the user or organization."},
{Name: "type", Type: proto.ColumnType_STRING, Description: "The type of the user or organization."},
{Name: "avatar_url", Type: proto.ColumnType_STRING, Description: "The URL of the user's avatar."},
{Name: "bio", Type: proto.ColumnType_STRING, Description: "The biography of the user or organization."},
{Name: "blog", Type: proto.ColumnType_STRING, Description: "The blog address of the user or organization."},
{Name: "collaborators", Type: proto.ColumnType_INT, Description: "The number of collaborators."},
{Name: "company", Type: proto.ColumnType_STRING, Description: "The company the user works for."},
{Name: "created_at", Type: proto.ColumnType_TIMESTAMP, Description: "The timestamp when the user or the organization was created."},
{Name: "disk_usage", Type: proto.ColumnType_INT, Description: "The total disk usage for the user or organization."},
{Name: "email", Type: proto.ColumnType_STRING, Description: "The public email address of the user or organization."},
{Name: "events_url", Type: proto.ColumnType_STRING, Description: "The event URL of the user or organization."},
{Name: "followers", Type: proto.ColumnType_INT, Description: "The number of users following the user or organization."},
{Name: "followers_url", Type: proto.ColumnType_STRING, Description: "The URL to get list of followers."},
{Name: "following", Type: proto.ColumnType_INT, Description: "The number of users followed by the user or organization."},
{Name: "following_url", Type: proto.ColumnType_STRING, Description: "The URL to get list of users followed by the user or organization."},
{Name: "gists_url", Type: proto.ColumnType_STRING, Description: "The URL get the gists of the user or organization."},
{Name: "gravatar_id", Type: proto.ColumnType_STRING, Description: "The gravatar id of the user or organization."},
{Name: "hireable", Type: proto.ColumnType_BOOL, Default: false, Description: "Whether the user or organization is hireable."},
{Name: "html_url", Type: proto.ColumnType_STRING, Description: "The GitHub page for the user or organization."},
{Name: "ldap_dn", Type: proto.ColumnType_STRING, Description: "The LDAP distinguished name of the user or organization."},
{Name: "location", Type: proto.ColumnType_STRING, Description: "The URL of the user or organization."},
{Name: "name", Type: proto.ColumnType_STRING, Description: "The name of the user or organization."},
{Name: "node_id", Type: proto.ColumnType_STRING, Description: "The node ID of the user or organization."},
{Name: "organizations_url", Type: proto.ColumnType_STRING, Description: "The URL to get the organization details of the user or organization."},
{Name: "owned_private_repos", Type: proto.ColumnType_INT, Description: "The number of owned private repositories by the user or organization."},
{Name: "private_gists", Type: proto.ColumnType_INT, Description: "The number of private gists owned by the user or organization."},
{Name: "public_gists", Type: proto.ColumnType_INT, Description: "The number of public gists owned by the user or organization."},
{Name: "public_repos", Type: proto.ColumnType_INT, Description: "The number of public repositories owned by the user or organization."},
{Name: "received_events_url", Type: proto.ColumnType_STRING, Description: "The URL to get the received events of the user or organization."},
{Name: "repos_url", Type: proto.ColumnType_STRING, Description: "The URL to get the repositories that the user or organization is part of."},
{Name: "site_admin", Type: proto.ColumnType_BOOL, Default: false, Description: "Whether the user or organization is an administrator."},
{Name: "starred_url", Type: proto.ColumnType_STRING, Description: "The URL to get the starred details of the user or organization."},
{Name: "subscriptions_url", Type: proto.ColumnType_STRING, Description: "The URL to get subscription details of the user or organization."},
{Name: "suspended_at", Type: proto.ColumnType_TIMESTAMP, Description: "The timestamp when the user or the organization was suspended."},
{Name: "total_private_repos", Type: proto.ColumnType_INT, Description: "The number of private repositories of the user or organization."},
{Name: "twitter_username", Type: proto.ColumnType_STRING, Description: "The twitter username of the user or organization."},
{Name: "two_factor_authentication", Type: proto.ColumnType_BOOL, Default: false, Description: "Whether two-factor authentication is enabled for the user or organization."},
{Name: "updated_at", Type: proto.ColumnType_TIMESTAMP, Description: "The timestamp when the user or the organization was updated."},
{Name: "url", Type: proto.ColumnType_STRING, Description: "The URL to get information regarding the user or organization."},
{Name: "permissions", Type: proto.ColumnType_JSON, Description: "The permission details."},
{Name: "plan", Type: proto.ColumnType_JSON, Description: "The plan details."},
{Name: "text_matches", Type: proto.ColumnType_JSON, Description: "The text match details."},
},
}
}

//// LIST FUNCTION

func tableGitHubSearchUserList(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
logger.Trace("tableGitHubSearchUserList")

quals := d.KeyColumnQuals
query := quals["query"].GetStringValue()

if query == "" {
return nil, nil
}

opt := &github.SearchOptions{
ListOptions: github.ListOptions{PerPage: 100},
TextMatch: true,
}

type ListPageResponse struct {
result *github.UsersSearchResult
resp *github.Response
}

client := connect(ctx, d)

// Reduce the basic request limit down if the user has only requested a small number of rows
limit := d.QueryContext.Limit
if limit != nil {
if *limit < int64(opt.ListOptions.PerPage) {
opt.ListOptions.PerPage = int(*limit)
}
}

listPage := func(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
result, resp, err := client.Search.Users(ctx, query, opt)

if err != nil {
logger.Error("tableGitHubSearchUserList", "error_Search.Users", err)
return nil, err
}

return ListPageResponse{
result: result,
resp: resp,
}, nil
}

for {
listPageResponse, err := plugin.RetryHydrate(ctx, d, h, listPage, &plugin.RetryConfig{ShouldRetryError: shouldRetryError})

if err != nil {
logger.Error("tableGitHubSearchUserList", "error_RetryHydrate", err)
return nil, err
}

listResponse := listPageResponse.(ListPageResponse)
users := listResponse.result.Users
resp := listResponse.resp

for _, i := range users {
d.StreamListItem(ctx, i)

// Context can be cancelled due to manual cancellation or the limit has been hit
if d.QueryStatus.RowsRemaining(ctx) == 0 {
return nil, nil
}
}

if resp.NextPage == 0 {
break
}

opt.Page = resp.NextPage
}

return nil, nil
}

0 comments on commit 326554d

Please sign in to comment.