diff --git a/docs/tables/github_my_repository.md b/docs/tables/github_my_repository.md index 80575a3..5af7a26 100644 --- a/docs/tables/github_my_repository.md +++ b/docs/tables/github_my_repository.md @@ -220,4 +220,27 @@ where json_extract(hook.value, '$.config.insecure_ssl') = '1' or json_extract(hook.value, '$.config.secret') is null or json_extract(hook.value, '$.config.url') not like '%https:%'; -``` \ No newline at end of file +``` + +### List repositories with custom property values +Explore custom property key/value pairs configured on repositories you can access. + +```sql+postgres +select + name_with_owner, + custom_properties +from + github_my_repository +where + custom_properties is not null; +``` + +```sql+sqlite +select + name_with_owner, + custom_properties +from + github_my_repository +where + custom_properties is not null; +``` diff --git a/docs/tables/github_repository.md b/docs/tables/github_repository.md index 480b24a..d481e04 100644 --- a/docs/tables/github_repository.md +++ b/docs/tables/github_repository.md @@ -101,4 +101,27 @@ from github_repository where full_name = 'turbot/steampipe'; -``` \ No newline at end of file +``` + +### Get custom property values for a repository +Explore the custom property key/value pairs assigned to a repository. + +```sql+postgres +select + full_name, + custom_properties +from + github_repository +where + full_name = 'my-org/my-repo'; +``` + +```sql+sqlite +select + full_name, + custom_properties +from + github_repository +where + full_name = 'my-org/my-repo'; +``` diff --git a/docs/tables/github_search_repository.md b/docs/tables/github_search_repository.md index 258c111..a9e1c38 100644 --- a/docs/tables/github_search_repository.md +++ b/docs/tables/github_search_repository.md @@ -149,4 +149,29 @@ from github_search_repository where query = 'tinyspotifyr in:name created:2021-01-01..2021-01-05 fork:only'; +``` + +### Get custom property values from search results +Explore custom property key/value pairs for repositories returned by a search query. + +```sql+postgres +select + name_with_owner, + custom_properties +from + github_search_repository +where + query = 'org:my-org archived:false' + and custom_properties is not null; +``` + +```sql+sqlite +select + name_with_owner, + custom_properties +from + github_search_repository +where + query = 'org:my-org archived:false' + and custom_properties is not null; ``` \ No newline at end of file diff --git a/docs/tables/github_team_repository.md b/docs/tables/github_team_repository.md index 0282d49..214fddd 100644 --- a/docs/tables/github_team_repository.md +++ b/docs/tables/github_team_repository.md @@ -105,4 +105,33 @@ where organization = 'my_org' and slug = 'my-team' and permission = 'ADMIN'; +``` + +### List custom property values for a team's repositories +Explore custom property key/value pairs for repositories visible to a team. + +```sql+postgres +select + organization, + slug, + name, + custom_properties +from + github_team_repository +where + organization = 'my_org' + and slug = 'my-team'; +``` + +```sql+sqlite +select + organization, + slug, + name, + custom_properties +from + github_team_repository +where + organization = 'my_org' + and slug = 'my-team'; ``` \ No newline at end of file diff --git a/github/models/repository.go b/github/models/repository.go index 0d21205..6675429 100644 --- a/github/models/repository.go +++ b/github/models/repository.go @@ -48,33 +48,34 @@ type Repository struct { Owner struct { Login string `json:"login"` } `json:"owner"` - PrimaryLanguage Language `graphql:"primaryLanguage @include(if:$includePrimaryLanguage)" json:"primary_language"` - ProjectsUrl string `graphql:"projectsUrl @include(if:$includeProjectsUrl)" json:"projects_url"` - PullRequestTemplates []PullRequestTemplate `graphql:"pullRequestTemplates @include(if:$includePullRequestTemplates)" json:"pull_request_templates"` - PushedAt NullableTime `graphql:"pushedAt @include(if:$includePushedAt)" json:"pushed_at"` - RebaseMergeAllowed bool `graphql:"rebaseMergeAllowed @include(if:$includeRebaseMergeAllowed)" json:"rebase_merge_allowed"` - SecurityPolicyUrl string `graphql:"securityPolicyUrl @include(if:$includeSecurityPolicyUrl)" json:"security_policy_url"` - SquashMergeAllowed bool `graphql:"squashMergeAllowed @include(if:$includeSquashMergeAllowed)" json:"squash_merge_allowed"` - SquashMergeCommitMessage githubv4.SquashMergeCommitMessage `graphql:"squashMergeCommitMessage @include(if:$includeSquashMergeCommitMessage)" json:"squash_merge_commit_message"` - SquashMergeCommitTitle githubv4.SquashMergeCommitTitle `graphql:"squashMergeCommitTitle @include(if:$includeSquashMergeCommitTitle)" json:"squash_merge_commit_title"` - SshUrl string `graphql:"sshUrl @include(if:$includeSshUrl)" json:"ssh_url"` - StargazerCount int `graphql:"stargazerCount @include(if:$includeStargazerCount)" json:"stargazer_count"` - UpdatedAt NullableTime `graphql:"updatedAt @include(if:$includeUpdatedAt)" json:"updated_at"` - Url string `graphql:"url @include(if:$includeUrl)" json:"url"` - UsesCustomOpenGraphImage bool `graphql:"usesCustomOpenGraphImage @include(if:$includeUsesCustomOpenGraphImage)" json:"uses_custom_open_graph_image"` - CanAdminister bool `graphql:"canAdminister: viewerCanAdminister @include(if:$includeCanAdminister)" json:"can_administer"` - CanCreateProjects bool `graphql:"canCreateProjects: viewerCanCreateProjects @include(if:$includeCanCreateProjects)" json:"can_create_projects"` - CanSubscribe bool `graphql:"canSubscribe: viewerCanSubscribe @include(if:$includeCanSubscribe)" json:"can_subscribe"` - CanUpdateTopics bool `graphql:"canUpdateTopics: viewerCanUpdateTopics @include(if:$includeCanUpdateTopics)" json:"can_update_topics"` - HasStarred bool `graphql:"hasStarred: viewerHasStarred @include(if:$includeHasStarred)" json:"has_starred"` - YourPermission githubv4.RepositoryPermission `graphql:"yourPermission: viewerPermission @include(if:$includeYourPermission)" json:"your_permission"` - PossibleCommitEmails []string `graphql:"possibleCommitEmails: viewerPossibleCommitEmails @include(if:$includePossibleCommitEmails)" json:"possible_commit_emails"` - Subscription githubv4.SubscriptionState `graphql:"subscription: viewerSubscription @include(if:$includeSubscription)" json:"subscription"` - Visibility githubv4.RepositoryVisibility `graphql:"visibility @include(if:$includeVisibility)" json:"visibility"` - WebCommitSignoffRequired bool `graphql:"webCommitSignoffRequired @include(if:$includeWebCommitSignoffRequired)" json:"web_commit_signoff_required"` - RepositoryTopics Count `graphql:"repositoryTopics @include(if:$includeRepositoryTopics)" json:"repository_topics"` - OpenIssues Count `graphql:"issues(states: OPEN) @include(if:$includeOpenIssues)" json:"open_issues"` - Watchers Count `graphql:"watchers @include(if:$includeWatchers)" json:"watchers"` + PrimaryLanguage Language `graphql:"primaryLanguage @include(if:$includePrimaryLanguage)" json:"primary_language"` + ProjectsUrl string `graphql:"projectsUrl @include(if:$includeProjectsUrl)" json:"projects_url"` + PullRequestTemplates []PullRequestTemplate `graphql:"pullRequestTemplates @include(if:$includePullRequestTemplates)" json:"pull_request_templates"` + PushedAt NullableTime `graphql:"pushedAt @include(if:$includePushedAt)" json:"pushed_at"` + RebaseMergeAllowed bool `graphql:"rebaseMergeAllowed @include(if:$includeRebaseMergeAllowed)" json:"rebase_merge_allowed"` + SecurityPolicyUrl string `graphql:"securityPolicyUrl @include(if:$includeSecurityPolicyUrl)" json:"security_policy_url"` + SquashMergeAllowed bool `graphql:"squashMergeAllowed @include(if:$includeSquashMergeAllowed)" json:"squash_merge_allowed"` + SquashMergeCommitMessage githubv4.SquashMergeCommitMessage `graphql:"squashMergeCommitMessage @include(if:$includeSquashMergeCommitMessage)" json:"squash_merge_commit_message"` + SquashMergeCommitTitle githubv4.SquashMergeCommitTitle `graphql:"squashMergeCommitTitle @include(if:$includeSquashMergeCommitTitle)" json:"squash_merge_commit_title"` + SshUrl string `graphql:"sshUrl @include(if:$includeSshUrl)" json:"ssh_url"` + StargazerCount int `graphql:"stargazerCount @include(if:$includeStargazerCount)" json:"stargazer_count"` + UpdatedAt NullableTime `graphql:"updatedAt @include(if:$includeUpdatedAt)" json:"updated_at"` + Url string `graphql:"url @include(if:$includeUrl)" json:"url"` + UsesCustomOpenGraphImage bool `graphql:"usesCustomOpenGraphImage @include(if:$includeUsesCustomOpenGraphImage)" json:"uses_custom_open_graph_image"` + CanAdminister bool `graphql:"canAdminister: viewerCanAdminister @include(if:$includeCanAdminister)" json:"can_administer"` + CanCreateProjects bool `graphql:"canCreateProjects: viewerCanCreateProjects @include(if:$includeCanCreateProjects)" json:"can_create_projects"` + CanSubscribe bool `graphql:"canSubscribe: viewerCanSubscribe @include(if:$includeCanSubscribe)" json:"can_subscribe"` + CanUpdateTopics bool `graphql:"canUpdateTopics: viewerCanUpdateTopics @include(if:$includeCanUpdateTopics)" json:"can_update_topics"` + HasStarred bool `graphql:"hasStarred: viewerHasStarred @include(if:$includeHasStarred)" json:"has_starred"` + YourPermission githubv4.RepositoryPermission `graphql:"yourPermission: viewerPermission @include(if:$includeYourPermission)" json:"your_permission"` + PossibleCommitEmails []string `graphql:"possibleCommitEmails: viewerPossibleCommitEmails @include(if:$includePossibleCommitEmails)" json:"possible_commit_emails"` + Subscription githubv4.SubscriptionState `graphql:"subscription: viewerSubscription @include(if:$includeSubscription)" json:"subscription"` + Visibility githubv4.RepositoryVisibility `graphql:"visibility @include(if:$includeVisibility)" json:"visibility"` + WebCommitSignoffRequired bool `graphql:"webCommitSignoffRequired @include(if:$includeWebCommitSignoffRequired)" json:"web_commit_signoff_required"` + RepositoryTopics Count `graphql:"repositoryTopics @include(if:$includeRepositoryTopics)" json:"repository_topics"` + RepositoryCustomPropertyValues RepositoryCustomPropertyValueConnection `graphql:"repositoryCustomPropertyValues(first: 100) @include(if:$includeRepositoryCustomPropertyValues)" json:"repository_custom_property_values"` + OpenIssues Count `graphql:"issues(states: OPEN) @include(if:$includeOpenIssues)" json:"open_issues"` + Watchers Count `graphql:"watchers @include(if:$includeWatchers)" json:"watchers"` // AssignableUsers [pageable] // BranchProtectionRules [pageable] // CodeOwners [search by refName] @@ -146,3 +147,12 @@ type RepositoryFundingLinks struct { Url string `json:"url"` Platform githubv4.FundingPlatform `json:"platform"` } + +type RepositoryCustomPropertyValue struct { + PropertyName string `json:"property_name"` + Value interface{} `json:"value"` +} + +type RepositoryCustomPropertyValueConnection struct { + Nodes []RepositoryCustomPropertyValue `json:"nodes"` +} diff --git a/github/repo_utils.go b/github/repo_utils.go index 1e0e5b6..f8e1535 100644 --- a/github/repo_utils.go +++ b/github/repo_utils.go @@ -417,6 +417,7 @@ func appendRepoColumnIncludes(m *map[string]interface{}, cols []string) { "can_update_topics": "includeCanUpdateTopics", "code_of_conduct": "includeCodeOfConduct", "contact_links": "includeContactLinks", + "custom_properties": "includeRepositoryCustomPropertyValues", "created_at": "includeCreatedAt", "default_branch_ref": "includeDefaultBranchRef", "delete_branch_on_merge": "includeDeleteBranchOnMerge", @@ -1197,6 +1198,17 @@ func repoHydrateRepositoryTopicsCount(_ context.Context, _ *plugin.QueryData, h return nil, nil } +func repoHydrateRepositoryCustomPropertyValues(_ context.Context, _ *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + if r, ok := h.Item.(models.Repository); ok { + return r.RepositoryCustomPropertyValues.Nodes, nil + } else if r, ok := h.Item.(models.SearchRepositoryResult); ok { + return r.Node.RepositoryCustomPropertyValues.Nodes, nil + } else if r, ok := h.Item.(models.TeamRepositoryWithPermission); ok { + return r.Node.RepositoryCustomPropertyValues.Nodes, nil + } + return nil, nil +} + func repoHydrateOpenIssuesCount(_ context.Context, _ *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { if r, ok := h.Item.(models.Repository); ok { return r.OpenIssues.TotalCount, nil diff --git a/github/table_github_repository.go b/github/table_github_repository.go index b660dab..8b89958 100644 --- a/github/table_github_repository.go +++ b/github/table_github_repository.go @@ -93,6 +93,7 @@ func sharedRepositoryColumns() []*plugin.Column { {Name: "repository_topics_total_count", Type: proto.ColumnType_INT, Hydrate: repoHydrateRepositoryTopicsCount, Transform: transform.FromValue(), Description: "Count of topics associated with the repository."}, {Name: "open_issues_total_count", Type: proto.ColumnType_INT, Hydrate: repoHydrateOpenIssuesCount, Transform: transform.FromValue(), Description: "Count of issues open on the repository."}, {Name: "watchers_total_count", Type: proto.ColumnType_INT, Hydrate: repoHydrateWatchersCount, Transform: transform.FromValue(), Description: "Count of watchers on the repository."}, + {Name: "custom_properties", Type: proto.ColumnType_JSON, Description: "A map of custom property names and values assigned to the repository.", Hydrate: repoHydrateRepositoryCustomPropertyValues, Transform: transform.FromValue().Transform(repositoryCustomPropertiesTransform)}, // Columns from v3 api - hydrates {Name: "hooks", Type: proto.ColumnType_JSON, Description: "The API Hooks URL.", Hydrate: hydrateRepositoryHooksFromV3, Transform: transform.FromValue()}, {Name: "topics", Type: proto.ColumnType_JSON, Description: "The topics (similar to tags or labels) associated with the repository.", Hydrate: hydrateRepositoryDataFromV3}, @@ -146,11 +147,30 @@ func tableGitHubRepositoryList(ctx context.Context, d *plugin.QueryData, h *plug return nil, nil } +func repositoryCustomPropertiesTransform(_ context.Context, input *transform.TransformData) (interface{}, error) { + values, ok := input.Value.([]models.RepositoryCustomPropertyValue) + if !ok || len(values) == 0 { + return nil, nil + } + + customProperties := make(map[string]interface{}, len(values)) + for _, value := range values { + customProperties[value.PropertyName] = value.Value + } + + if len(customProperties) == 0 { + return nil, nil + } + + return customProperties, nil +} + func hydrateRepositoryDataFromV3(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { repo, err := extractRepoFromHydrateItem(h) if err != nil { return nil, err } + owner := repo.Owner.Login repoName := repo.Name